I used UiLabel with byTruncatingTail attribute.
I want to fix the ellipsis from the middle to the bottom vertically.
It is now stated as follows.
But I want to have the ellipsis placed on the floor as follows.
My code is as follows.
let paragraphStyle = NSMutableParagraphStyle()
let range = NSRange(location: 0, length: mutableAttributedString.length)
paragraphStyle.lineSpacing = 2
paragraphStyle.alignment = .center
paragraphStyle.lineBreakMode = .byTruncatingTail
mutableAttributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: range)
titleLabel.attributedText = mutableAttributedString
Is this possible?
I am getting wrong height for an UILabel if I use NSAttributedString that has custom kern and lineSpacing.
Here is how I set the custom kern and line spacing:
override func viewDidLoad() {
super.viewDidLoad()
let shortText = "Single line"
self.label.attributedText = self.getAttributedText(text: shortText, kern: 0.2, lineSpacing: 8)
self.label2.attributedText = self.getAttributedText(text: shortText, kern: 0, lineSpacing: 8)
}
private func getAttributedText(text: String, kern: CGFloat, lineSpacing: CGFloat) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: text)
let style = NSMutableParagraphStyle()
style.lineSpacing = lineSpacing
let attributes: [NSAttributedStringKey : Any] =
[.paragraphStyle : style,
.kern: kern]
attributedString.addAttributes(attributes,
range: NSMakeRange(0, attributedString.length))
return attributedString
}
And here is what I get:
The first label (the one that has custom kern), has its height wrong. It's exactly 8 points taller than it should be - that's the custom line height that I am using.
This only happens for single line labels. If I use text that is on a couple of lines, it works as expected.
This is a bug with NSAttributedStringKey.kern. As a workaround, you can calculate the number of lines of your UILabel with the suggestions in this answer. If it has one line only, set lineSpacing to 0.
private func getAttributedText(text: String, kern: CGFloat, lineSpacing: CGFloat) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: text)
let font = UIFont.systemFont(ofSize: 16)
let attributes: [NSAttributedStringKey : Any] = [.kern: kern,
.font: font]
attributedString.addAttributes(attributes, range: NSMakeRange(0, attributedString.length))
let maxSize = CGSize(width: [custom width], height: CGFloat.greatestFiniteMagnitude)
let sizeOfLabel = attributedString.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, context: nil)
if sizeOfLabel.height > font.lineHeight {
let style = NSMutableParagraphStyle()
style.lineSpacing = lineSpacing
attributedString.addAttribute(.paragraphStyle, value: style, range: NSMakeRange(0, attributedString.length))
}
return attributedString
}
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
}()
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
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
}