UILabel vertical alignment - ios

In my application i am using ActiveLabelfram Github.
In that case, my label does not show the text in the middle of the UILabel. If i use a normal UILabel it works fine, but when settings it to a ActiveLabel, it gets like this.
(Image is taken in runtime)
I think this is the code to play with the alignment somehow:
/// add line break mode
private func addLineBreak(attrString: NSAttributedString) -> NSMutableAttributedString {
let mutAttrString = NSMutableAttributedString(attributedString: attrString)
var range = NSRange(location: 0, length: 0)
var attributes = mutAttrString.attributesAtIndex(0, effectiveRange: &range)
let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? NSMutableParagraphStyle ?? NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = NSLineBreakMode.ByWordWrapping
if let lineSpacing = lineSpacing {
paragraphStyle.lineSpacing = CGFloat(lineSpacing)
}
attributes[NSParagraphStyleAttributeName] = paragraphStyle
mutAttrString.setAttributes(attributes, range: range)
return mutAttrString
}
ActiveLabel.swift
ActiveType.swift
Any ideas how i can make it in the middle like this:
(Image is taken from Storyboard)

In ActiveLabel.swift replace the drawTextInRect method with
public override func drawTextInRect(rect: CGRect) {
let range = NSRange(location: 0, length: textStorage.length)
textContainer.size = rect.size
let usedRect = layoutManager.usedRectForTextContainer(textContainer)
let glyphOriginY = (rect.height > usedRect.height) ? rect.origin.y + (rect.height - usedRect.height) / 2 : rect.origin.y
let glyphOrigin = CGPointMake(rect.origin.x, glyphOriginY)
layoutManager.drawBackgroundForGlyphRange(range, atPoint: glyphOrigin)
layoutManager.drawGlyphsForGlyphRange(range, atPoint: glyphOrigin)
}
I have also forked the repo under https://github.com/rishi420/ActiveLabel.swift
If you download the repo, remember to set verticalTextAlignmentCenter to true

Have a look at this post:
Programmatically Add CenterX/CenterY Constraints
Well the problem will be when u have dragged a label already from the IB and then you are trying to change its position. The code will break.
You will need to programmatically make the label and then set it to the centre.
And very seriously, #Alex is correct. AutoLayout solves a lot of problems.

You can set the textAlignment in the code like this:
showLab.textAlignment = NSTextAlignmentCenter;
Or you can also use Storyboard or xib to see what happen in the lab,StoryBoard,as you look i choose the Second of alignment what means middle in the label

Related

How to fix UILabel text spacing?

This code worked fine on iOS 12 and under and the issue occurs when running iOS 13. The goal is to remove the line height spacing to 0 so my labels have a reduced amount of space in between text. I have two labels inside a collection view cell and when I scroll the cells off the screen and then scroll back down the label text is now "cut off". This was not the case as I mentioned in previous versions of iOS. Any help fixing this would be amazing. Thanks ahead of time.
This is my code:
extension: UILabel {
func addLineSpacing(spacing: CGFloat) {
guard let text = text else { return }
let originalText = NSMutableAttributedString(string: text)
let style = NSMutableParagraphStyle()
let lineHeight = font.pointSize - font.ascender + font.capHeight
let offset = font.capHeight - font.ascender
let range = NSRange(location: 0, length: text.count)
style.maximumLineHeight = lineHeight
style.minimumLineHeight = lineHeight
style.alignment = .center
originalText.addAttribute(.paragraphStyle, value: style, range: range)
originalText.addAttribute(.baselineOffset, value: offset, range: range)
attributedText = originalText
}
}
This is how the UILabel text looks like before scrolling:
This is how it looks after scrolling. Notice how the text seems to be shifted up and cut off
I had the similar issue with UILabel and I fixed it with in following way:
style.maximumLineHeight = lineHeight
style.minimumLineHeight = lineHeight - 0.0001
I know that's not the most beautiful solution and this just a workaround but it's working. Hope it help.

Swift changing line spacing leaves bottom white space

I am trying to change the line spacing for a label to reduce the line space in Arabic language it is too much. The extension function I used from here with additions for Arabic styling is working on controlling the line spacing of the label but the only problem it leaves bottom margin white space I assume equals the same label size before reducing the line space.
The extension function here:
extension UILabel {
// Pass value for any one of both parameters and see result
func setLineSpacing(lineSpacing: CGFloat = 0.0, lineHeightMultiple: CGFloat = 0.0) {
guard let labelText = self.text else { return }
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpacing
paragraphStyle.lineHeightMultiple = lineHeightMultiple
paragraphStyle.alignment = .justified
paragraphStyle.baseWritingDirection = .rightToLeft
let attributedString:NSMutableAttributedString
if let labelattributedText = self.attributedText {
attributedString = NSMutableAttributedString(attributedString: labelattributedText)
} else {
attributedString = NSMutableAttributedString(string: labelText)
}
attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
self.attributedText = attributedString
}
}
then I just call the function like this:
bodyLabel.attributedText = entry.attributedText
bodyLabel.setLineSpacing(lineSpacing: -20)
I tried the extension it's working fine like this:
bodyLabel.attributedText = entry.attributedText
bodyLabel.setLineSpacing(lineSpacing: -20)
bodyLabel.sizeToFit()
Also if that didn't work check the height for the label, and try setting the content to Fill.
bodyLabel.contentMode = .scaleAspectFill
Constraints of the bodyLabel must be like,
Adjust the bottom constraint of your label to >=0

Scroll UITextView to specific text

i have a uitextview with large texts and above i have a search field. Anything searched, i would love the uitextview to scroll to that specific text and hightlight. And i need to make sure that the uitextview scrolls in such a way that the searched text stays in the first line
I populated the textview with attributed text with some attributes. What i tried is get the text before the target text and tried to get the size of it adding the exact attributes i added on textview text at the beginning.
made a CGPoint using that height and set the contentOffset of the uitextview. But the textview scrolled no where near the target text. Maybe my approach is wrong as i dont have the width of the textview when setting attributes
My code is :
func goToBla() {
if let string = ayatTextView.text {
if let range = string.range(of: tazweedAyahas[8].text) {
let firstPart = string[string.startIndex..<range.lowerBound]
print("before \(tazweedAyahas[5].text) : ")
print(firstPart)
if let otherStr = firstPart as? NSString {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
let size = otherStr.size(withAttributes: [NSAttributedStringKey.paragraphStyle: paragraphStyle,
NSAttributedStringKey.font: UIFont(name: "me_quran", size: 30)
])
let point = CGPoint(x: 0, y: size.height)
ayatTextView.setContentOffset(point, animated: true)
}
}
}
}
Your approach is much more complicated than it needs to be. Use the scrollRangeToVisible method of UITextView.
func goToBla() {
if let string = ayatTextView.text, let range = string.localizedStandardRange(of: tazweedAyahas[8].text) {
let viewRange = NSRange(range, in: string)
ayatTextView.selectedRange = viewRange // optional
ayatTextView.scrollRangeToVisible(viewRange)
}
}
Note the following:
Use localizedStandardRange when searching if you want to ignore diacritics and case, and you want other locale specific search features.
Call selectedRange if you want the matching text selected.
If you want to get the selected range at the top, try something like this instead of calling scrollRangeToVisible:
let rect = ayatTextView.layoutManager.boundingRect(forGlyphRange: viewRange, in: ayatTextView.textContainer)
ayatTextView.contentOffset = CGPoint(x: 0, y: rect.origin.y)

Dynamically set UILabel text alignment between .left and .justified

In my app I have a UILabel with two lines preset. I can set the text alignment to either .left or .justified.
If I set it to .left, there is no layout issue if there is enough space between the last word in a line and the maximum x position of the label. Yet, when there is not so much space, so that the last word is very near the maximum x position, it looks kinda weird, because it is not exactly right-aligned (as it would be with .justified.
If I set it to .justified, it is always aligned well, yet sometimes the distance between the individual characters looks weird.
What I'm looking for is a way to dynamically adjust the text alignment depending on the distance between the last word in the first line to the maximum x position of the label. Say, if the position of the last character of the last word is smaller than 50, I want to have text alignment .left, otherwise I'd like to have .justified. Is there any way on how to accomplish this?
I took a quite hacky approach which takes some processing power, but it seems to work.
First of all, I fetch the string in the first line of the label using this extension:
import CoreText
extension UILabel {
/// Returns the String displayed in the first line of the UILabel or "" if text or font is missing
var firstLineString: String {
guard let text = self.text else { return "" }
guard let font = self.font else { return "" }
let rect = self.frame
let attStr = NSMutableAttributedString(string: text)
attStr.addAttribute(String(kCTFontAttributeName), value: CTFontCreateWithName(font.fontName as CFString, font.pointSize, nil), range: NSMakeRange(0, attStr.length))
let frameSetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
let path = CGMutablePath()
path.addRect(CGRect(x: 0, y: 0, width: rect.size.width + 7, height: 100))
let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)
guard let line = (CTFrameGetLines(frame) as! [CTLine]).first else { return "" }
let lineString = text[text.startIndex...text.index(text.startIndex, offsetBy: CTLineGetStringRange(line).length-2)]
return lineString
}
}
After that I calculate the width, a label with line number 1 and fixed height would require for that string using this extension:
extension UILabel {
/// Get required width for a UILabel depending on its text content and font configuration
class func calculateWidth(text: String, height: CGFloat, font: UIFont) -> CGFloat {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: CGFloat.greatestFiniteMagnitude, height: height))
label.numberOfLines = 1
label.font = font
label.text = text
label.sizeToFit()
return label.frame.size.width
}
}
Based on that, I can calculate the distance to the right and decide whether to choose text alignment .left or .justified, so the main code looks like this:
// Set text
myLabel.text = someString
// Change text alignment depending on distance to right
let firstLineString = myLabel.firstLineString
let distanceToRight = myLabel.frame.size.width - UILabel.calculateWidth(text: firstLineString, height: myLabel.frame.size.height, font: myLabel.font)
myLabel.textAlignment = distanceToRight < 20 ? .justified : .left

How to implement UILabel line spacing using xib?

I want to align a UILabel's text vertically with line spacing via xib in iOS, for example:
hello,
how are
you?
Please help me.
Yes it is possible to adjust line spacing via xib. Click on UIlabel on xib,then change ‘text’ property type to ’Attributed’,then go through following images. Then enter new line(press ctrl-return shortcut key)and keep cursor in between two lines then adjust ‘line’ property and ‘Max Height’ property you want. My UILael text is "Hello,how are you?"
Create this simple UILabel subclass:
#interface CustomLabel : UILabel
#property (assign, nonatomic) CGFloat myLineSpacing;
#end
#implementation CustomLabel
- (void)setMyLineSpacing:(CGFloat)myLineSpacing {
_myLineSpacing = myLineSpacing;
self.text = self.text;
}
- (void)setText:(NSString *)text {
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = _myLineSpacing;
paragraphStyle.alignment = self.textAlignment;
NSDictionary *attributes = #{NSParagraphStyleAttributeName: paragraphStyle};
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text
attributes:attributes];
self.attributedText = attributedText;
}
Set myLineSpacing property value with IB.
Still you cannot preview in IB, but line spacing value is in xib!
You can set label.text in your code without caring about line spacing.
Note:
don't make property name "lineSpacing".
UILabel do have undocumented lineSpacing property, and overriding that breaks something.
*From Interface Builder:**
Programmatically:
SWift 4
Using label extension
extension UILabel {
// Pass value for any one of both parameters and see result
func setLineSpacing(lineSpacing: CGFloat = 0.0, lineHeightMultiple: CGFloat = 0.0) {
guard let labelText = self.text else { return }
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpacing
paragraphStyle.lineHeightMultiple = lineHeightMultiple
let attributedString:NSMutableAttributedString
if let labelattributedText = self.attributedText {
attributedString = NSMutableAttributedString(attributedString: labelattributedText)
} else {
attributedString = NSMutableAttributedString(string: labelText)
}
// Line spacing attribute
attributedString.addAttribute(NSAttributedStringKey.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
self.attributedText = attributedString
}
}
Now call extension function
let label = UILabel()
let stringValue = "How\nto\nimplement\nUILabel\nline\nspacing\nusing\nxib?"
// Pass value for any one argument - lineSpacing or lineHeightMultiple
label.setLineSpacing(lineSpacing: 2.0) . // try values 1.0 to 5.0
// or try lineHeightMultiple
//label.setLineSpacing(lineHeightMultiple = 2.0) // try values 0.5 to 2.0
Or using label instance (Just copy & execute this code to see result)
let label = UILabel()
let stringValue = "How\nto\nimplement\nUILabel\nline\nspacing\nusing\nxib?"
let attrString = NSMutableAttributedString(string: stringValue)
var style = NSMutableParagraphStyle()
style.lineSpacing = 24 // change line spacing between paragraph like 36 or 48
style.minimumLineHeight = 20 // change line spacing between each line like 30 or 40
// Line spacing attribute
attrString.addAttribute(NSAttributedStringKey.paragraphStyle, value: style, range: NSRange(location: 0, length: stringValue.characters.count))
// Character spacing attribute
attrString.addAttribute(NSAttributedStringKey.kern, value: 2, range: NSMakeRange(0, attrString.length))
label.attributedText = attrString
Type it exactly the same way you desire in any text editor like TextEdit etc and then paste in the text section of your label in xib.
But make sure height of your label is more to fit the content and also increase the number of lines. For this case it can be 3 or more.

Resources