Swift changing line spacing leaves bottom white space - ios

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

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.

Add highlight/background to only text using Swift

I want to highlight or add a background only on a text on a label that is not center-aligned.
I already tried Attributed Strings (https://stackoverflow.com/a/38069772/676822) and using regex but didn't get near a good solution.
NSAttributedString won't work because my label is not centered and it doesn't contain line breaks. It's just a long text that takes multiple lines.
This is what I'm trying to accomplish:
Note: It's not "Evangelizing\nDesign\nThinking" it's "Evangelizing Design Thinking"
Thanks!
As far as I have tried its not possible to get what you want simply with attributed text because using:
let attributedText = NSMutableAttributedString(string: "Evangelizing Desing Thinking",
attributes: [
.font: UIFont.systemFont(ofSize: 14),
.backgroundColor: UIColor.gray
]
)
Will add extray gray background at the end of each line. My previous answer was not good neither because it only adds a gray background on each word, not on spaces, and as #Alladinian noticed, ranges can be wrong in some cases.
So here is a hack you can use to achieve what you want. It uses multiple labels but it can be easily improved by putting labels in a custom view. So, in your viewDidLoad / CustomView function add:
// Maximum desired width for your text
let maxLabelWidth: CGFloat = 80
// Font you used
let font = UIFont.systemFont(ofSize: 14)
// Your text
let text = "Eva ngel izing Des ing a Thin king"
// Width of a space character
let spaceWidth = NSString(string: " ").size(withAttributes: [NSAttributedString.Key.font: font]).width
// Width of a row
var currentRowWidth: CGFloat = 0
// Content of a row
var currentRow = ""
// Previous label added (we keep it to add constraint betweeen labels)
var prevLabel: UILabel?
let subStrings = text.split(separator: " ")
for subString in subStrings {
let currentWord = String(subString)
let nsCurrentWord = NSString(string: currentWord)
// Width of the new word
let currentWordWidth = nsCurrentWord.size(withAttributes: [NSAttributedString.Key.font: font]).width
// Width of the row if you add a new word
let currentWidth = currentRow.count == 0 ? currentWordWidth : currentWordWidth + spaceWidth + currentRowWidth
if currentWidth <= maxLabelWidth { // The word can be added in the current row
currentRowWidth = currentWidth
currentRow += currentRow.count == 0 ? currentWord : " " + currentWord
} else { // Its not possible to add a new word in the current row, we create a label with the current row content
prevLabel = generateLabel(with: currentRow,
font: font,
prevLabel: prevLabel)
currentRowWidth = currentWordWidth
currentRow = currentWord
}
}
// Dont forget to add the last row
generateLabel(with: currentRow,
font: font,
prevLabel: prevLabel)
Then you have to create the generateLabel function:
#discardableResult func generateLabel(with text: String,
font: UIFont,
prevLabel: UILabel?) -> UILabel {
let leftPadding: CGFloat = 50 // Left padding of the label
let topPadding: CGFloat = 100 // Top padding of (first) label
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(label)
label.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: leftPadding).isActive = true
if let prevLabel = prevLabel {
label.topAnchor.constraint(equalTo: prevLabel.bottomAnchor).isActive = true
} else {
label.topAnchor.constraint(equalTo: self.view.topAnchor, constant: topPadding).isActive = true
}
label.font = font
label.text = text
label.backgroundColor = .gray
return label
}
Previous answer:
As Yogesh suggested, you can use attributed string:
// Init label
let label = UILabel(frame: CGRect(x: 50, y: 50, width: 90, height: 120))
self.view.addSubview(label)
label.lineBreakMode = .byTruncatingTail
label.numberOfLines = 0
label.backgroundColor = .white
// Create attributed text
let text = "Evangelizing Desing Thinking"
let attributedText = NSMutableAttributedString(string: text,
attributes: [
.font: UIFont.systemFont(ofSize: 14)
]
)
// Find ranges of each word
let subStrings = text.split(separator: " ")
let ranges = subStrings.map { (subString) -> Range<String.Index> in
guard let range = text.range(of: subString) else {
fatalError("something wrong with substring") // This case should not happen
}
return range
}
// Apply background color for each word
ranges.forEach { (range) in
let nsRange = NSRange(range, in: text)
attributedText.addAttribute(.backgroundColor, value: UIColor.gray, range: nsRange)
}
// Finally set attributed text
label.attributedText = attributedText

UILabel vertical alignment

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

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.

How control line-spacing of UILabels?

I'm using CustomCell instead of UITableViewCell on UITableView.
I put two UILables on CustomCell.
Refered this site: here , but I failed...
Question : How can I control line spacing of UILabels?
Starting from iOS 6 you can set an attributed string to the UILabel. Check the following :
NSMutableParagraphStyle *paragrahStyle = [[NSMutableParagraphStyle alloc] init];
[paragrahStyle setLineSpacing:40];
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragrahStyle range:NSMakeRange(0, [labelText length])];
cell.label.attributedText = attributedString;
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 to\ncontrol\nthe\nline spacing\nin UILabel"
// 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 to\ncontrol\nthe\nline spacing\nin UILabel"
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
Swift 3
Just copy & execute this code to see result.
let label = UILabel()
let stringValue = "How to\ncontrol\nthe\nline spacing\nin UILabel"
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
attrString.addAttribute(NSParagraphStyleAttributeName, value: style, range: NSRange(location: 0, length: stringValue.characters.count))
label.attributedText = attrString
From Interface Builder:

Resources