UILabel negative line spacing - ios

I've got a UILabel and I want to make the line spacing less than 0 (so the text moves closer together rather than further away). What am I doing wrong?
UIFont* customFont = [UIFont fontWithName:#"BebasNeue" size:70];
NSString * text = #"Their \nIdeas";
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text];
NSMutableParagraphStyle *paragrahStyle = [[NSMutableParagraphStyle alloc] init];
paragrahStyle.lineSpacing = -30;
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragrahStyle range:NSMakeRange(0, [text length])];
UILabel *lbl1 = [[UILabel alloc] init];
lbl1.frame = CGRectMake(120, 0, viewWidth, 300);
lbl1.backgroundColor = [UIColor clearColor];
lbl1.textColor = grayColor;
lbl1.numberOfLines = 2;
lbl1.attributedText = attributedString;
lbl1.userInteractionEnabled = NO;
lbl1.font = customFont;
[view addSubview:lbl1];
[lbl1 setTransform:CGAffineTransformMakeRotation(0.35)];

NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:labelText];
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
CGFloat minMaxLineHeight = (font.pointSize - font.ascender + font.capHeight);
NSNumber *offset = #(font.capHeight - font.ascender);
NSRange range = NSMakeRange(0, labelText.length);
[style setMinimumLineHeight:minMaxLineHeight];
[style setMaximumLineHeight:minMaxLineHeight];
if (isCentered) {
[style setAlignment:NSTextAlignmentCenter];
}
[attrString addAttribute:NSParagraphStyleAttributeName
value:style
range:range];
[attrString addAttribute:NSBaselineOffsetAttributeName
value:offset
range:range];
This should get you near zero spacing on the text and should be safe to use with any font type and size.

Please see the following documentation from Apple setLineSpacing - NSMutableParagraphStyle , value must not be negative
setLineSpacing:
Sets the distance in points added between lines within the paragraph to aFloat.
- (void)setLineSpacing:(CGFloat)aFloat
Discussion
This value must be nonnegative.
There are also methods related to the minimum and maximum height for lines… probably setting line space to 0 and modifying the height could help you to achieve the effect you want-

Building off of Eric Murphey's answer, I've created an extension in Swift 4 syntax.
extension UILabel{
public func zeroLineSpace(){
let s = NSMutableAttributedString(string: self.text!)
let style = NSMutableParagraphStyle()
let lineHeight = self.font.pointSize - self.font.ascender + self.font.capHeight
let offset = self.font.capHeight - self.font.ascender
let range = NSMakeRange(0, self.text!.count)
style.maximumLineHeight = lineHeight
style.minimumLineHeight = lineHeight
style.alignment = self.textAlignment
s.addAttribute(.paragraphStyle, value: style, range: range)
s.addAttribute(.baselineOffset, value: offset, range: range)
self.attributedText = s
}
}
Simply call it like so
myUiLabel.zeroLineSpace()

Just a minor tweak to make things more configurable.
extension UILabel {
public func setLineHeight(lineHeight: CGFloat, textAlignment: NSTextAlignment ) {
guard let text = self.text else { return }
let attributeString = NSMutableAttributedString(string: text)
let styleLineHeight = self.font.pointSize - self.font.ascender + self.font.capHeight + lineHeight
let style = NSMutableParagraphStyle()
style.alignment = textAlignment
style.maximumLineHeight = styleLineHeight
style.minimumLineHeight = styleLineHeight
let offset = self.font.capHeight - self.font.ascender + lineHeight * 0.5
let range = NSMakeRange(0, text.count)
attributeString.addAttribute(.paragraphStyle, value: style, range: range)
attributeString.addAttribute(.baselineOffset, value: offset, range: range)
self.attributedText = attributeString
}
}

I cleaned up Christ Stillwell's answer a little bit:
extension UILabel {
public func zeroLineSpace() {
guard let text = self.text else { return }
let attributedString = NSMutableAttributedString(string: text)
let style = NSMutableParagraphStyle()
let lineHeight = self.font.pointSize - self.font.ascender + self.font.capHeight
let offset = self.font.capHeight - self.font.ascender
let range = NSRange(location: 0, length: attributedString.length)
style.maximumLineHeight = lineHeight
style.minimumLineHeight = lineHeight
style.alignment = self.textAlignment
attributedString.addAttribute(.paragraphStyle, value: style, range: range)
attributedString.addAttribute(.baselineOffset, value: offset, range: range)
self.attributedText = attributedString
}
}

Related

UILabel - How to add space between lines in Swift 3

I've UILabel with paragraphic information (multiline text) and I want add some spaces between lines, similar to this image.
Please help me to do it. I've tried to checkout all documentation of apple developer regarding Label and line spacing but could find.
From Interface Builder (Storyboard/XIB):
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
let label = UILabel()
let stringValue = "Sample text"
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
#IBOutlet weak var myLabel: UILabel!
let textForLabel = “Lorem Ipsum is simply dummy text of the printing and
typesetting industry. Lorem Ipsum has been the industry’s standard dummy text
ever since the 1500s, when an unknown printer took a galley of type and
scrambled it to make a type specimen book. It has survived not only five
centuries, but also the leap into electronic typesetting, remaining
essentially unchanged.”
let paragraphStyle = NSMutableParagraphStyle()
//line height size
paragraphStyle.lineSpacing = 1.4
let attrString = NSMutableAttributedString(string: titleText)
attrString.addAttribute(NSParagraphStyleAttributeName, value:paragraphStyle,
range:NSMakeRange(0, attrString.length))
myLabel.attributedText = attrString
myLabel.textAlignment = NSTextAlignment.Center

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 to increase the character spacing in UILabel

I am creating an app in >=iOS6. And I want to change the character spacing in UILabel. I have added the custom font "FUTURABT HEAVY" in my app but the Character are too close to eachother.
I have find the good code here to increase the character spacing. But if i tried to change it than my text became left- align in stead of center.
Please help me with this situation.
You should probably use NSAttributedString with NSKernAttributeName attribute
Here is a small example:
UILabel *label = [[UILabel alloc] initWithFrame:self.view.bounds];
NSString *string = #"Some important text";
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
float spacing = 5.0f;
[attributedString addAttribute:NSKernAttributeName
value:#(spacing)
range:NSMakeRange(0, [string length])];
label.attributedText = attributedString;
[self.view addSubview:label];
Swift extension for this
extension UILabel {
func addCharactersSpacing(spacing:CGFloat, text:String) {
let attributedString = NSMutableAttributedString(string: text)
attributedString.addAttribute(NSAttributedString.Key.kern, value: spacing, range: NSMakeRange(0, text.count-1))
self.attributedText = attributedString
}
}
So you can use it
MyLabel.addCharactersSpacing(5, text: "Some Text")
Swift 4
extension UILabel {
func setCharacterSpacing(characterSpacing: CGFloat = 0.0) {
guard let labelText = text else { return }
let attributedString: NSMutableAttributedString
if let labelAttributedText = attributedText {
attributedString = NSMutableAttributedString(attributedString: labelAttributedText)
} else {
attributedString = NSMutableAttributedString(string: labelText)
}
// Character spacing attribute
attributedString.addAttribute(NSAttributedStringKey.kern, value: characterSpacing, range: NSMakeRange(0, attributedString.length))
attributedText = attributedString
}
}
Swift 3
let label = UILabel()
let stringValue = "Sample text"
let attrString = NSMutableAttributedString(string: stringValue)
attrString.addAttribute(NSKernAttributeName, 2: style, range: NSRange(location: 0, length: stringValue.characters.count))
label.attributedText = attrString
Here the code for Swift 5 with a handy extension :
extension UILabel {
func addCharactersSpacing(spacing: CGFloat, txt: String) {
let attributedString = NSMutableAttributedString(string: txt)
attributedString.addAttribute(NSAttributedString.Key.kern, value: spacing, range: NSRange(location: 0, length: txt.count))
self.attributedText = attributedString
}
}
Swift 4.2 with UILabel extension and #IBInspectable
extension UILabel {
#IBInspectable var letterSpacing: CGFloat {
get {
var range:NSRange = NSMakeRange(0, self.text?.count ?? 0)
let nr = self.attributedText?.attribute(NSAttributedString.Key.kern, at: 0, effectiveRange: &range) as! NSNumber
return CGFloat(truncating: nr)
}
set {
let range:NSRange = NSMakeRange(0, self.text?.count ?? 0)
let attributedString = NSMutableAttributedString(string: self.text ?? "")
attributedString.addAttribute(NSAttributedString.Key.kern, value: newValue, range: range)
self.attributedText = attributedString
}
}
}
NSString *strDigit= #"001";
NSString *strCrushImpact =[NSStringstringWithFormat:#"%d",[strDigit intValue]];
// Set space in between character
float spacing = 3.0f;
NSMutableAttributedString *attributedStrDigit = [[NSMutableAttributedString alloc] initWithString:strWin];
[strCrushImpact addAttribute:NSKernAttributeName value:#(spacing)
range:NSMakeRange(0, [strDigit length])];
label.attributedText = attributedStrDigit;

How do I get word wrap information with the new iOS 7 APIs?

I noticed that iOS 7 introduces new classes related to text layout such as NSLayoutManager, NSTextStorage, and NSTextContainer. How can I use these in order to get information about word wrapping on an NSString?
For example, say I have a long NSString which I put in a UILabel. If I enable multiple lines on the UILabel, it would produce a string such as the following:
The quick brown fox jumps
over the lazy dog.
That's great, but I can't access the line breaks in code (e.g. after the word jumps I would want it to return \n or something similar). I would want to know at which character indexes the line breaks occur. I know we can do this with CoreText, but since we have these new classes in iOS 7, I was wondering how we can use them instead.
Example:
CGFloat maxWidth = 150;
NSAttributedString *s =
[[NSAttributedString alloc]
initWithString:#"The quick brown fox jumped over the lazy dog."
attributes:#{NSFontAttributeName:[UIFont fontWithName:#"GillSans" size:20]}];
NSTextContainer* tc =
[[NSTextContainer alloc] initWithSize:CGSizeMake(maxWidth,CGFLOAT_MAX)];
NSLayoutManager* lm = [NSLayoutManager new];
NSTextStorage* tm = [[NSTextStorage alloc] initWithAttributedString:s];
[tm addLayoutManager:lm];
[lm addTextContainer:tc];
[lm enumerateLineFragmentsForGlyphRange:NSMakeRange(0,lm.numberOfGlyphs)
usingBlock:^(CGRect rect, CGRect usedRect,
NSTextContainer *textContainer,
NSRange glyphRange, BOOL *stop) {
NSRange r = [lm characterRangeForGlyphRange:glyphRange actualGlyphRange:nil];
NSLog(#"%#", [s.string substringWithRange:r]);
}];
swift translation:
guard let font1: UIFont = textView.font else { return }
var lines: [String] = []
let maxWidth: CGFloat = textView.frame.width
let s: NSAttributedString = NSAttributedString.init(string: textView.text, attributes: [.font: font1])
let tc: NSTextContainer = NSTextContainer.init(size: CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude))
let lm: NSLayoutManager = NSLayoutManager.init()
let tm: NSTextStorage = NSTextStorage.init(attributedString: s)
tm.addLayoutManager(lm)
lm.addTextContainer(tc)
lm.enumerateLineFragments(forGlyphRange: NSRange(location: 0, length: lm.numberOfGlyphs)) { (rect: CGRect, usedRect: CGRect, textContainer: NSTextContainer, glyphRange: NSRange, Bool) in
let r: NSRange = lm.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
let str = s as NSAttributedString
let s2 = str.attributedSubstring(from: r)
print(s2)
lines.append(s2.string)
}
I created an extesion of UITextView with the answer of #brontea, it returns the text of the textView formatted with line breaks to save the string the same way the user sees it:
extension UITextView {
func textFormattedWithLineBreaks() -> String? {
guard let font: UIFont = self.font else { return "" }
var textPerLine: [String] = []
let maxWidth: CGFloat = self.frame.width
let attributedString: NSAttributedString = NSAttributedString.init(string: self.text, attributes: [.font: font])
let textContainer: NSTextContainer = NSTextContainer.init(size: CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude))
let layoutManager: NSLayoutManager = NSLayoutManager.init()
let textStorage: NSTextStorage = NSTextStorage.init(attributedString: attributedString)
textStorage.addLayoutManager(layoutManager)
layoutManager.addTextContainer(textContainer)
layoutManager.enumerateLineFragments(forGlyphRange: NSRange(location: 0, length: layoutManager.numberOfGlyphs)) { (rect: CGRect, usedRect: CGRect, textContainer: NSTextContainer, glyphRange: NSRange, Bool) in
let range: NSRange = layoutManager.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
let string = attributedString as NSAttributedString
let stringWithRange = string.attributedSubstring(from: range)
print(stringWithRange.string)
textPerLine.append(stringWithRange.string)
}
return textPerLine.map{ "\($0)\n" }.joined()
}
}

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