centering text in a UILabel with an NSAttributedString - ios

Going through some basic improvements to a application I am working on. Still new to the iOS swift development scene. I figured that the lines of text in my code would automatically be centered because I set the label to center. After a little bit of research I discovered this is not the case. How would I align code like this to center:
let atrString = try NSAttributedString(
data: assetDetails!.cardDescription.dataUsingEncoding(NSUTF8StringEncoding)!,
options: [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType],
documentAttributes: nil)
assetDescription.attributedText = atrString

You need to create a paragraph style specifying center alignment, and set that paragraph style as an attribute on your text. Example playground:
import UIKit
import PlaygroundSupport
let style = NSMutableParagraphStyle()
style.alignment = NSTextAlignment.center
let richText = NSMutableAttributedString(string: "Going through some basic improvements to a application I am working on. Still new to the iOS swift development scene. I figured that the lines of text in my code would automatically be centered because I set the label to center.",
attributes: [ NSParagraphStyleAttributeName: style ])
// In Swift 4, use `.paragraphStyle` instead of `NSParagraphStyleAttributeName`.
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 400))
label.backgroundColor = UIColor.white
label.attributedText = richText
label.numberOfLines = 0
PlaygroundPage.current.liveView = label
Result:
Since you're parsing an HTML document to create your attributed string, you'll need to add the attribute after creation, like this:
let style = NSMutableParagraphStyle()
style.alignment = NSTextAlignment.center
let richText = try NSMutableAttributedString(
data: assetDetails!.cardDescription.data(using: String.Encoding.utf8)!,
options: [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType],
documentAttributes: nil)
richText.addAttributes([ NSParagraphStyleAttributeName: style ],
range: NSMakeRange(0, richText.length))
// In Swift 4, use `.paragraphStyle` instead of `NSParagraphStyleAttributeName`.
assetDescription.attributedText = richText
Update for Swift 4
In Swift 4, attribute names are now of type NSAttributeStringKey and the standard attribute names are static members of that type. So you can add the attribute like this:
richText.addAttribute(.paragraphStyle, value: style, range: NSMakeRange(0, richText.length))

In Swift 4.1 :
let style = NSMutableParagraphStyle()
style.alignment = NSTextAlignment.center
lbl.centerAttributedText = NSAttributedString(string: "Total Balance",attributes: [.paragraphStyle: style])
(edited for code block)

You can use this utility function to do all common configuration for label
#discardableResult
public func DULabel(text: String, frame: CGRect = .zero, parent:UIView? = nil , font:UIFont, textColor:UIColor = .black, numOfLines:Int = 0 ,textAlignment: NSTextAlignment = .center,lineSpaceing:CGFloat = 0, cb: ((UILabel)->Void)? = nil )-> UILabel! {
let label = UILabel()
label.frame = frame
label.font = font
label.textColor = textColor
label.textAlignment = textAlignment
label.numberOfLines = numOfLines
if( lineSpaceing == 0 ){
label.text = text
}
else {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpaceing
paragraphStyle.alignment = textAlignment
let attrString = NSMutableAttributedString(string: text)
attrString.addAttribute(.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attrString.length))
label.attributedText = attrString
}
if let parent = parent {
parent.addSubview(label)
}
cb?(label)
return label
}

Related

UITextField: Leading Truncate

Currently, my text within a UITextField is truncating at the tail (eg. "Hello, Wor..."). I want the to truncate the text from the leading edge (eg. "...lo, World!").
I believe using NSMutableParagraphStyle's lineBreakMode with NSMutableAttributedString might work
let longString = "Hello World"
// Just for demo purposes, you can use auto layout etc
let textfield = UITextField(frame: CGRect(x: 40, y: 200, width: 30, height: 100))
textfield.layer.borderWidth = 2
textfield.layer.borderColor = UIColor.gray.cgColor
// Use a paragraph style to define the truncation method
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byTruncatingHead
// Create an attributed string and apply the paragraph style
let attributedString = NSMutableAttributedString(string: longString)
attributedString.addAttribute(.paragraphStyle,
value: paragraphStyle,
range:NSMakeRange(0, attributedString.length))
// Set the text field's attributed text as the attributed string
textfield.attributedText = attributedString
This gives something like this, which is what I think you want:
You just have to update the text attributes (e.g., defaultTextAttributes) with a .paragraphStyle with a lineBreakMode of .byTruncatingHead.
I would not just replace the .paragraphStyle, though. I would fetch it, update its lineBreakMode, and then update the text attributes with that:
let oldStyle = textField.defaultTextAttributes[.paragraphStyle, default: NSParagraphStyle()] as! NSParagraphStyle
let style = oldStyle.mutableCopy() as! NSMutableParagraphStyle
style.lineBreakMode = .byTruncatingHead
textField.defaultTextAttributes[.paragraphStyle] = style
Here it is with .byTruncatingHead, .byTruncatingMiddle and .byTruncatingTail, respectively:

Underline covers text in NSAttributedString

I'm trying to create an attributed string but the underline covers my text instead of appearing behind it:
Is there a way to fix this? I'm using the following code:
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 10.0
let attributes = [NSForegroundColorAttributeName: UIColor.white,
NSUnderlineStyleAttributeName: NSUnderlineStyle.styleThick.rawValue,
NSUnderlineColorAttributeName: UIColor.red,
NSParagraphStyleAttributeName: paragraphStyle]
let attributedString = NSAttributedString(string: "Story", attributes: attributes)
Thanks!
EDIT:
To give more context:
I'm displaying the attributed string on a UILabel placed in a .xib file:
view.textLabel.attributedText = attributedString
The label has the following font:
System Bold 32.0
I'm running the code on iPhone 6 - iOS 10.3 simulator.
EDIT 2:
I should have mentioned that the label may, at some point, contain more than one line of text. That's why the numberOfLines is set to 0.
EDIT 3:
If anybody encounters this problem -- it seems that there is a lot of difference in how underline is drawn on iOS 9 vs 10 as well as UILabel vs UITextView. I've ended up having to draw the underline myself by subclassing NSLayoutManager.
Yes, there is such problem as you have described. It shows up when you use multiline UILabel, so not only setting numberOfLines to 0, but type more than 1 line in it.
Example
let selectedStringAttributes: [String: Any]
= [NSFontAttributeName: UIFont.boldSystemFont(ofSize: 28),
NSForegroundColorAttributeName: UIColor.green,
NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue,
NSUnderlineColorAttributeName: UIColor.green]
let label = UILabel(frame: CGRect(x: 100, y: 100, width: 500, height: 100))
label.numberOfLines = 0
label.attributedText = NSAttributedString(string: "String to test underline", attributes: selectedStringAttributes)
And everything will look pretty good.
But if you want to use such text:
label.attributedText = NSAttributedString(string: "String to\ntest underline", attributes: selectedStringAttributes)
or label's width is too short, than:
So the reason for such behaviour is of course bug in NSAttributedString. As it mentioned in radar there is a workaround
You should add this attribute to your NSAttributedString
NSBaselineOffsetAttributeName: 0
And magic will happen.
Instead of using NSAttributedString you can draw border below the label with x space using this.
let space:CGFloat = 10
let border = CALayer()
border.backgroundColor = UIColor.red.cgColor
border.frame = CGRect(x: 0, y: (label?.frame.size.height)! + space, width: (label?.frame.size.width)!, height: 1)
label?.layer.addSublayer(border)
On my machine, showing your attributed string in a black-backgrounded UILabel, it makes a quite nice-looking display:
The red thick underline is nicely separated from the text, and is interrupted to allow the descender of the "y" to pass through it.
NOTE You cannot combine the font of the UILabel (set in Interface Builder) with its attributedText. You must set the entire label's text formatting in the attributedText. So, my code looks like this:
let attributes : [String:Any] = [NSForegroundColorAttributeName: UIColor.white,
NSUnderlineStyleAttributeName: NSUnderlineStyle.styleThick.rawValue,
NSUnderlineColorAttributeName: UIColor.red,
NSFontAttributeName: UIFont.boldSystemFont(ofSize: 32)]
let attributedString = NSAttributedString(string: "Story", attributes: attributes)
lab.backgroundColor = .black
lab.attributedText = attributedString
(You will notice that I removed your stipulation of the paragraph line spacing; there is only one line, so this stipulation adds nothing. However, I get the same result even if I restore it.)
So this is my solution to this issue.
I think it is "cleaner" and easier.
Post me if you dont understand :)
class BottomLineTextField: UITextField {
var bottomBorder = UIView()
override func awakeFromNib() {
super.awakeFromNib()
setBottomBorder()
}
func setBottomBorder() {
self.translatesAutoresizingMaskIntoConstraints = false
bottomBorder = UIView.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
hasError = false
bottomBorder.translatesAutoresizingMaskIntoConstraints = false
addSubview(bottomBorder)
bottomBorder.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
bottomBorder.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
bottomBorder.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
bottomBorder.heightAnchor.constraint(equalToConstant: 1).isActive = true // Set underline height
}
}

How to Increase Line spacing in UILabel in Swift

I have a label which has few lines of text and I want to increase the spacing between the lines. There are similar questions asked by others but the solutions don't solve my problems. Also my label may or may not contain paragraphs. I am new to Swift. Is there a solution using storyboard? Or only through NSAttributedString its possible?
Programatically add LineSpacing to your UILabel using following snippet.
Earlier Swift version
let attributedString = NSMutableAttributedString(string: "Your text")
// *** Create instance of `NSMutableParagraphStyle`
let paragraphStyle = NSMutableParagraphStyle()
// *** set LineSpacing property in points ***
paragraphStyle.lineSpacing = 2 // Whatever line spacing you want in points
// *** Apply attribute to string ***
attributedString.addAttribute(NSParagraphStyleAttributeName, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
// *** Set Attributed String to your label ***
label.attributedText = attributedString
Swift 4.0
let attributedString = NSMutableAttributedString(string: "Your text")
// *** Create instance of `NSMutableParagraphStyle`
let paragraphStyle = NSMutableParagraphStyle()
// *** set LineSpacing property in points ***
paragraphStyle.lineSpacing = 2 // Whatever line spacing you want in points
// *** Apply attribute to string ***
attributedString.addAttribute(NSAttributedStringKey.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
// *** Set Attributed String to your label ***
label.attributedText = attributedString
Swift 4.2
let attributedString = NSMutableAttributedString(string: "Your text")
// *** Create instance of `NSMutableParagraphStyle`
let paragraphStyle = NSMutableParagraphStyle()
// *** set LineSpacing property in points ***
paragraphStyle.lineSpacing = 2 // Whatever line spacing you want in points
// *** Apply attribute to string ***
attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
// *** Set Attributed String to your label ***
label.attributedText = attributedString
From Interface Builder:
Programmatically:
SWift 4 & 4.2
Using label extension
extension UILabel {
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)
}
// (Swift 4.2 and above) Line spacing attribute
attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
// (Swift 4.1 and 4.0) 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 = "Set\nUILabel\nline\nspacing"
// 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 = "Set\nUILabel\nline\nspacing"
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
let label = UILabel()
let stringValue = "Set\nUILabel\nline\nspacing"
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
You can control the line spacing in storyboard.
Same question.
Recent solution for Swift 5.0
private extension UILabel {
// MARK: - spacingValue is spacing that you need
func addInterlineSpacing(spacingValue: CGFloat = 2) {
// MARK: - Check if there's any text
guard let textString = text else { return }
// MARK: - Create "NSMutableAttributedString" with your text
let attributedString = NSMutableAttributedString(string: textString)
// MARK: - Create instance of "NSMutableParagraphStyle"
let paragraphStyle = NSMutableParagraphStyle()
// MARK: - Actually adding spacing we need to ParagraphStyle
paragraphStyle.lineSpacing = spacingValue
// MARK: - Adding ParagraphStyle to your attributed String
attributedString.addAttribute(
.paragraphStyle,
value: paragraphStyle,
range: NSRange(location: 0, length: attributedString.length
))
// MARK: - Assign string that you've modified to current attributed Text
attributedText = attributedString
}
}
And the usage:
let yourLabel = UILabel()
let yourText = "Hello \n world \n !"
yourLabel.text = yourText
yourLabel.addInterlineSpacing(spacingValue: 1.5)
Swift 4 and Swift 5
extension NSAttributedString {
func withLineSpacing(_ spacing: CGFloat) -> NSAttributedString {
let attributedString = NSMutableAttributedString(attributedString: self)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byTruncatingTail
paragraphStyle.lineSpacing = spacing
attributedString.addAttribute(.paragraphStyle,
value: paragraphStyle,
range: NSRange(location: 0, length: string.count))
return NSAttributedString(attributedString: attributedString)
}
}
How to use
let example = NSAttributedString(string: "This is Line 1 \nLine 2 \nLine 3 ").withLineSpacing(15)
testLabel.attributedText = example
You can use this reusable extension:
extension String {
func lineSpaced(_ spacing: CGFloat) -> NSAttributedString {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = spacing
let attributedString = NSAttributedString(string: self, attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])
return attributedString
}
}
Dipen's answer updated for Swift 4
let attr = NSMutableAttributedString(string: today)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 2
attr.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, attr.length))
label.attributedText = attr;
extension UILabel {
var spacing: CGFloat {
get {return 0}
set {
let textAlignment = self.textAlignment
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = newValue
let attributedString = NSAttributedString(string: self.text ?? "", attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])
self.attributedText = attributedString
self.textAlignment = textAlignment
}
}
}
let label = UILabel()
label.text = "test"
label.spacing = 10
//Swift 4:
func set(text:String,
inLabel:UILabel,
withLineSpacing:CGFloat,
alignment:NSTextAlignment){
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = withLineSpacing
let attrString = NSMutableAttributedString(string: text)
attrString.addAttribute(NSAttributedStringKey.paragraphStyle,
value:paragraphStyle,
range:NSMakeRange(0, attrString.length))
inLabel.attributedText = attrString
inLabel.textAlignment = alignment
}
Create LabelStyle
struct LabelStyle {
let font: UIFont
let fontMetrics: UIFontMetrics?
let lineHeight: CGFloat?
let tracking: CGFloat
init(font: UIFont, fontMetrics: UIFontMetrics? = nil, lineHeight: CGFloat? = nil, tracking: CGFloat = 0) {
self.font = font
self.fontMetrics = fontMetrics
self.lineHeight = lineHeight
self.tracking = tracking
}
func attributes(for alignment: NSTextAlignment, lineBreakMode: NSLineBreakMode) -> [NSAttributedString.Key: Any] {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = alignment
paragraphStyle.lineBreakMode = lineBreakMode
var baselineOffset: CGFloat = .zero
if let lineHeight = lineHeight {
let lineHeightMultiple = lineHeight / font.lineHeight
paragraphStyle.lineHeightMultiple = lineHeightMultiple
baselineOffset = 1 / lineHeightMultiple
let scaledLineHeight: CGFloat = fontMetrics?.scaledValue(for: lineHeight) ?? lineHeight
paragraphStyle.minimumLineHeight = scaledLineHeight
paragraphStyle.maximumLineHeight = scaledLineHeight
}
return [
NSAttributedString.Key.paragraphStyle: paragraphStyle,
NSAttributedString.Key.kern: tracking,
NSAttributedString.Key.baselineOffset: baselineOffset,
NSAttributedString.Key.font: font
]
}
}
Create custom Label class and use our style
public class Label: UILabel {
var style: LabelStyle? { nil }
public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if previousTraitCollection?.preferredContentSizeCategory != traitCollection.preferredContentSizeCategory {
updateText()
}
}
convenience init(text: String?, textColor: UIColor) {
self.init()
self.text = text
self.textColor = textColor
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
updateText()
}
private func commonInit() {
font = style?.font
adjustsFontForContentSizeCategory = true
}
private func updateText() {
text = super.text
}
public override var text: String? {
get {
guard style?.attributes != nil else {
return super.text
}
return attributedText?.string
}
set {
guard let style = style else {
super.text = newValue
return
}
guard let newText = newValue else {
attributedText = nil
super.text = nil
return
}
let attributes = style.attributes(for: textAlignment, lineBreakMode: lineBreakMode)
attributedText = NSAttributedString(string: newText, attributes: attributes)
}
}
}
Create concrete Label
public final class TitleLabel {
override var style: LabelStyle? {
LabelStyle(
font: UIFont.Title(),
lineHeight: 26.21,
tracking: 0.14
)
}
}
and use it
#IBOutlet weak var titleLabel: TitleLabel!
In addition to using attributed strings & paragraph styles, for small adjustemnts, font descriptors can also come in handy.
For instance:
let font: UIFont = .init(
descriptor: UIFontDescriptor
.preferredFontDescriptor(withTextStyle: .body)
.withSymbolicTraits(.traitLooseLeading)!,
size: 0
)
This will create a font with a looser leading, resulting in a text with a slightly larger line height (it adds 2 points) than the default system font. traitTightLeading can also be used for the opposite effect (it reduces the leading of the font by 2 points).
I wrote a blog post comparing the approaches here: https://bootstragram.com/blog/line-height-with-uikit/.
This solution worked for swift 5
this is reference to answer of https://stackoverflow.com/a/62116213/13171606
I Made some changes for "NSMutableAttributedString" and included the full example, i think it will help u all
Note: Please Adjust Color and Font style if found any error.
Extension
extension NSAttributedString {
func withLineSpacing(_ spacing: CGFloat) -> NSMutableAttributedString {
let attributedString = NSMutableAttributedString(attributedString: self)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byTruncatingTail
paragraphStyle.lineSpacing = spacing
attributedString.addAttribute(.paragraphStyle,
value: paragraphStyle,
range: NSRange(location: 0, length: string.count))
return NSMutableAttributedString(attributedString: attributedString)
}
}
Implementation Example
let myAttributedText = NSMutableAttributedString(string: "Please enter the required details to change your AAAAAAAAA AAAAA AAAAA. Maximum AAAAA can be AAA AA AAA AA.\n\nNote: If you do not have a AAAAA AAAA then please AAAAAAA us at 111-111-111 or send us an email AAAA AAAA AAA AAAAAAAAAA AAAAA address at xxxxxxxxxxxxxxxxxxxxxxxxxxxx.", attributes: [
.font: UIFont.systemFont(ofSize: 14),
.foregroundColor: UIColor.gray,
.kern: 0.0]).withLineSpacing(8)
myAttributedText.addAttributes([
.font: UIFont.systemFont(ofSize: 14),
.foregroundColor: UIColor.blue],
range: NSRange(location: 174, length: 11))
myAttributedText.addAttributes([
.font: UIFont.systemFont(ofSize: 14),
.foregroundColor: UIColor.blue],
range: NSRange(location: 248, length: 28))
UILable
let myLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.numberOfLines = 0
label.attributedText = myAttributedText //Here is your Attributed String
return label
}()

NSKernAttributeName space at end of line in an NSAttributedString

When using NSKernAttributeName it puts a space at the end of each line, is there any way to fix this? I can set the attributed to be in the range of:
NSRange(location: 0, length: self.text!.characters.count-1)
But I don't want to set this for every line.
This is the test code in the a playground I am using
//: Playground - noun: a place where people can play
import UIKit
import XCPlayground
var text = "Hello, playground\nhow are you?"
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.paragraphSpacing = 50
paragraphStyle.alignment = NSTextAlignment.Left
paragraphStyle.lineBreakMode = NSLineBreakMode.ByTruncatingTail
let attributes = [
NSParagraphStyleAttributeName: paragraphStyle
, NSKernAttributeName: 20
]
let attributedString = NSAttributedString(string: text, attributes: attributes)
let label = UILabel()
label.attributedText = attributedString
label.numberOfLines = 0
label.textColor = UIColor.greenColor()
label.backgroundColor = UIColor.orangeColor()
label.sizeToFit()
label.center = CGPoint(x: 500, y: 100)
var text2 = "What's up\nWhere are you?"
let attributedString2 = NSAttributedString(string: text2, attributes: attributes)
let label2 = UILabel()
label2.attributedText = attributedString2
label2.numberOfLines = 0
label2.textColor = UIColor.greenColor()
label2.backgroundColor = UIColor.orangeColor()
label2.sizeToFit()
label2.center = CGPoint(x: 500, y: 250)
var text3 = "Hello"
let attributedString3 = NSAttributedString(string: text3, attributes: attributes)
let label3 = UILabel()
label3.attributedText = attributedString3
label3.numberOfLines = 0
label3.textColor = UIColor.greenColor()
label3.backgroundColor = UIColor.orangeColor()
label3.sizeToFit()
label3.center = CGPoint(x: 500, y: 400)
let holderView = UIView(frame: CGRect(x: 0, y: 0, width: 1000, height: 500))
holderView.backgroundColor = UIColor.lightGrayColor()
holderView.addSubview(label)
holderView.addSubview(label2)
holderView.addSubview(label3)
XCPlaygroundPage.currentPage.liveView = holderView
With the result looking like this:
You can see the spaces at the end of each of the lines.
This is actually the definition of how kerning works; it adjusts the space between the kerned character and where the next character will be. Whether a next character proceeds to be drawn or not is irrelevant.
Standard Attributes
The kerning attribute indicates how much the following character should be shifted from its default offset as defined by the current character’s font; a positive kern indicates a shift farther along and a negative kern indicates a shift closer to the current character.
If it helps, think about doing this in a word processor. If kerning is on, and you type a character, where would you expect the insertion point to be then? The expected answer would be "offset from the just typed character by the amount of kern" as that's what happens in the default case of kern being 0, correct? Well, that's exactly what's happening here: if you kern the last character of a string, the string therefore includes the last kern.
So the correct thing to do here is to wrap up your dropLast() logic in an extension and call it a day.
Create an extension
import UIKit
extension UILabel {
#IBInspectable var kerning: Float {
get {
var range = NSMakeRange(0, (text ?? "").characters.count)
guard let kern = attributedText?.attribute(NSKernAttributeName, atIndex: 0, effectiveRange: &range),
value = kern as? NSNumber
else {
return 0
}
return value.floatValue
}
set {
var attText:NSMutableAttributedString?
if let attributedText = attributedText {
attText = NSMutableAttributedString(attributedString: attributedText)
} else if let text = text {
attText = NSMutableAttributedString(string: text)
} else {
attText = NSMutableAttributedString(string: "")
}
let range = NSMakeRange(0, attText!.length)
attText!.addAttribute(NSKernAttributeName, value: NSNumber(float: newValue), range: range)
self.attributedText = attText
}
}
}
This was answered here

How to adjust a UILabel line spacing programmatically in Swift?

I have a multiline UILabel as shown here:
I achieved this using the following code:
label.lineBreakMode = .ByWordWrapping
label.numberOfLines = 2
I'm trying to "decrease" the line spacing between the 1st line and 2nd line, and I tried to use the following code:
let text = label.attributedText
let mas = NSMutableAttributedString(attributedString:text!)
mas.replaceCharactersInRange(NSMakeRange(0, mas.string.utf16.count),
withString: label.text!)
label.attributedText = mas
However, it does not seem to work.
Thanks
Programmatically with 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\nadjust\na\nUILabel\nline\nspacing\nprogrammatically\nin\nSwift"
// 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\nadjust\na\nUILabel\nline\nspacing\nprogrammatically\nin\nSwift"
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
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:
You're on the right track with NSAttributedString. You need to set the line spacing of the paragraph style:
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 30 // Whatever line spacing you want in points
attributedString.addAttribute(NSParagraphStyleAttributeName, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
label.attributedText = attributedString;
Do this in the storyboard.....
func updateLabel(with title: String) {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineHeightMultiple = 0.8
paragraphStyle.alignment = .center
let string = NSAttributedString(string: title, attributes: [.paragraphStyle: paragraphStyle])
label.attributedText = string
}

Resources