How to increase the character spacing in UILabel - ios

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;

Related

How do I change the letter spacing in a UILabel?

I want to change the spacing between digits in a UIKit UILabel so that it is equal.
With standard spacing, the label looks like this:
I'd like it to look like this:
How can this be achieved?
You can use the NSKernAttributeName attribute on an attributed string:
UILabel *label = [UILabel new];
NSMutableAttributedString *text = [[NSMutableAttributedString alloc]
initWithString:#"127"];
// The value paramenter defines your spacing amount, and range is
// the range of characters in your string the spacing will apply to.
// Here we want it to apply to the whole string so we take it from 0 to text.length.
[text addAttribute:NSKernAttributeName
value:#-0.5
range:NSMakeRange(0, text.length)];
[label setAttributedText:text];
The easiest way is to create a custom UILabel class and set the letter spacing from Storyboard.
open class CustomLabel : UILabel {
#IBInspectable open var characterSpacing:CGFloat = 1 {
didSet {
let attributedString = NSMutableAttributedString(string: self.text!)
attributedString.addAttribute(NSKernAttributeName, value: self.characterSpacing, range: NSRange(location: 0, length: attributedString.length))
self.attributedText = attributedString
}
}
}
On swift implementation looks like
let text = "TIPS, ARTICLES & BEST OFFERS"
label.attributedText = NSAttributedString(string: text, attributes: [.kern: 3.12])
You can use an NSAttributedString and play with the NSKernAttributeName attribute. The default value for this is 0 so you will want to set it to a negative number.
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSAttributedString_Class/#//apple_ref/doc/constant_group/Character_Attributes
in Obj-C you can do something like this:
NSMutableAttributedString* attrStr = [[NSMutableAttributedString alloc]initWithString: #"Test test test test "];
[attrStr addAttribute:NSKernAttributeName value:#(4.0) range:NSMakeRange(0, attrStr.length)];
label.attributedText = attrStr;
in Swift you could do something like this:
let myTitle = "my title"
let titleLabel = UILabel()
let attributes: NSDictionary = [
NSFontAttributeName:UIFont(name: "FONT_NAME", size: TEXT_SIZE),
NSKernAttributeName:CGFloat(2.0)
]
let attributedTitle = NSAttributedString(string: myTitle, attributes:attributes as? [String : AnyObject])
titleLabel.attributedText = attributedTitle
The NSKernAttributeName can be used.
But in correction to the other answers: Do not apply to the full text length, but (text.length - 1).
The negative or positive spacing is added to the letter and this is not required for the last one. Assume you would add a positive spacing it would end up in a spacing after the last letter. A centered string would not appear to be centered anymore. The same applies for negative spacing.
NSString *text = #"Sample Text";
UILabel *label = [UILabel new]
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: text];
[attributedString addAttribute:NSKernAttributeName value:[NSNumber numberWithDouble:-1.0] range:NSMakeRange(0, text.length-1)];
[label setAttributedTitle:attributedString forState:UIControlStateNormal];
Applied to full text length.
Applied to (text length - 1).
I've taken the answer from #J2K and translated it into a Swift 4.2 extension.
/// Set the text of the label but altering the kerning so that you can control the space between each characters.
///
/// - Parameters:
/// - text: New content of the label
/// - kerning: Set a value between 0 and 1 to lower the space between characters. Above 0, spacing will be increased. 0 disables kerning.
extension UILabel {
func set(text: String, withKerning kerning: CGFloat) {
let attributedString = NSMutableAttributedString(string: text)
// The value parameter defines your spacing amount, and range is
// the range of characters in your string the spacing will apply to.
// Here we want it to apply to the whole string so we take it from 0 to text.count.
attributedString.addAttribute(NSAttributedString.Key.kern, value: kerning, range: NSMakeRange(0, text.count))
attributedText = attributedString
}
}
If you want to apply the letter spacing attribute to all UILabels in Interface Builder without modifying anything (for particular font):
Swift 4:
/**
* Applies letter spacing to selected fonts in UILabels from IB.
*
* - author Alexander Volkov
* - version 1.0
*/
extension UILabel {
/// Applies letter spacing
open override func awakeFromNib() {
super.awakeFromNib()
applyLetterSpacing()
}
/// Applies letter spacing
///
/// - Parameter aDecoder: the decoder
/// - Returns: UILabel instance
open override func awakeAfter(using aDecoder: NSCoder) -> Any? {
let label = super.awakeAfter(using: aDecoder)
self.applyLetterSpacing()
return label
}
/// Applies letter spacing
func applyLetterSpacing() {
if font.fontName.contains("Oswald", caseSensitive: false) {
let characterSpacing: CGFloat = 1
let string = NSMutableAttributedString(string: self.text!)
string.addAttribute(.kern, value: characterSpacing, range: NSRange(location: 0, length: string.length - 1))
self.attributedText = string
}
}
}
NSString *myString = #"127";
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:myString];
float letterSpacing = -1.50f; // change spacing here
[attributedString addAttribute:NSKernAttributeName value:#(letterSpacing) range:NSMakeRange(0, [myString length])];
[myLabel setAttributedText:attributedString];
Also see this for more info and results: http://www.devsign.co/notes/tracking-and-character-spacing
I found this to be the best solution. It includes also UIButton, applies attributes that may have been set in interface builder and adapts to different fonts:
extension UILabel {
open override func awakeFromNib() {
super.awakeFromNib()
applyLetterSpacing()
}
func applyLetterSpacing() {
// return if empty text
if((text ?? "").isEmpty) {
return
}
// default spacing
var spacing = 1
if(font.familyName == "Source Serif Pro") {
// custom spacing for different font
spacing = 2
}
var attributes = attributedText?.attributes(at: 0, effectiveRange: nil) ?? [:]
attributes[.kern] = spacing
attributedText = NSAttributedString(string: text ?? "", attributes: attributes)
}
}
and for Buttons
extension UIButton {
open override func awakeFromNib() {
super.awakeFromNib()
applyLetterSpacing()
}
func applyLetterSpacing() {
if((title(for: .normal) ?? "").isEmpty) {
return
}
var attributes = attributedTitle(for: .normal)?.attributes(at: 0, effectiveRange: nil) ?? [:]
attributes[.kern] = 1
attributes[.foregroundColor] = titleColor(for: .normal)
setAttributedTitle(NSAttributedString(string: title(for: .normal) ?? "", attributes: attributes), for: .normal)
}
}
click on the label and go to your attributes inspector. Change text from plain to attributed. You you will several options for spacing. Hope this helps.

How to calculate height of nsattributed string with line spacing dynamically

im trying to calculate the height of a UILabel with LineSpacing attribute. The weird thing is that calculated value of the height of the normal label.text is lower then the label.attributedText with its lineheight. it looks like i'm doing something wrong, but cant find what, so please help :D.
The provided code is specially made for SO to make it compact and clear, it is implemented differently in my project.
extension NSAttributedString {
func heightWithWidth(width: CGFloat) -> CGFloat {
let maxSize = CGSize(width: width, height: CGFloat.max)
let boundingBox = self.boundingRectWithSize(maxSize, options: [.UsesLineFragmentOrigin, .UsesFontLeading, .UsesDeviceMetrics], context: nil)
return boundingBox.height
}
}
extension UILabel {
func getHeightWithGivenWidthAndLineHeight(lineHeight: CGFloat, labelWidth: CGFloat) -> CGFloat {
let text = self.text
if let text = text {
let attributeString = NSMutableAttributedString(string: text)
let style = NSMutableParagraphStyle()
style.lineSpacing = lineHeight
attributeString.addAttribute(NSParagraphStyleAttributeName, value: style, range: NSMakeRange(0, text.characters.count))
let height = attributeString.heightWithWidth(labelWidth)
self.attributedText = attributeString
return height
}
return 0
}
I call this by
let contentHeight = contentLabel.text! == "" ? 0 : contentLabel.getHeightWithGivenWidthAndLineHeight(3, labelWidth: labelWidth)
Working with normal strings (without spacing) works perfectly, when i use attributedstring with lineSpacing it fails to calculate the correct value.
You can just use UILabel's sizeThatFits. For example:
let text = "This is\nSome\nGreat\nText"
let contentHeight = contentLabel.text! == "" ? 0 : contentLabel.getHeightWidthGivenWidthAndLineHeight(6, labelWidth: labelWidth)
//returns 73.2
But just setting
contentLabel.attributedText = contentLabel.attributedString //attributedString is same as getHeightWidthGivenWidthAndLineHeight
let size = contentLabel.sizeThatFits(contentLabel.frame.size)
//returns (w 49.5,h 99.5)
Code for attributedString added to your extension, if you need to see that:
var attributedString:NSAttributedString?{
if let text = self.text{
let attributeString = NSMutableAttributedString(string: text)
let style = NSMutableParagraphStyle()
style.lineSpacing = 6
attributeString.addAttribute(NSParagraphStyleAttributeName, value: style, range: NSMakeRange(0, text.characters.count))
return attributeString
}
return nil
}
I updated my Extension this way to set the line height and return the new label height at the same time. Thanx to beyowulf
extension UILabel {
func setLineHeight(lineHeight: CGFloat, labelWidth: CGFloat) -> CGFloat {
let text = self.text
if let text = text {
let attributeString = NSMutableAttributedString(string: text)
let style = NSMutableParagraphStyle()
style.lineSpacing = lineHeight
attributeString.addAttribute(NSParagraphStyleAttributeName, value: style, range: NSMakeRange(0, text.characters.count))
self.attributedText = attributeString
return self.sizeThatFits(CGSize(width: labelWidth, height: 20)).height
}
return 0
}
}

Drawing a simple line in a UILabel

Is there a way to add a simple line through a UILabel. I have a 40px high UILabel and simply want to draw a black line horizontally through the middle (at 20px). Is there a way to do this without having to create an image and setting it as the background?
if your label consist text then you can use strike through in label like this.
Objective-C
NSString *newStringStrike = #"your text";
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:newStringStrike];
[attributeString addAttribute:NSStrikethroughStyleAttributeName
value:#1
range:NSMakeRange(0, [attributeString length])];
labelName.attributedText = attributeString;
Yes, there are couple of ways. For example, you can add 1-point height subview to the label:
Swift 3.0 update:
let lineView = UIView(
frame: CGRect(x: 0,
y: label.bounds.size.height / 2,
width: label.bounds.size.width,
height: 1
)
)
lineView.backgroundColor = UIColor.black
label.addSubview(lineView)
You should be able to subclass UILabel and overwrite the drawRect method.
You can do it by using UILabel
height=1 width=as you need
and make its background colour as black and put it on your 40px UILabel. Hope this gonna help you.
#Anil solanki's answer using Swift 3.1:
let newStringStrike = "your text"
let attributeString = NSMutableAttributedString(string: newStringStrike)
attributedString.addAttribute(NSStrikethroughStyleAttributeName, value: 1, range: NSMakeRange(0, attributedString.length))
labelName.attributedText = attributedString
in swift4:
let newStringStrike = "your text"
let attributeString = NSMutableAttributedString(string: newStringStrike)
attributeString.addAttribute(NSAttributedStringKey.strikethroughStyle, value: 1, range: NSMakeRange(0, attributeString.length))
yourLabel = attributeString
simply create an NSAttributedString and assign to your label
let newStringStrike = "your text"
let attributeString = NSMutableAttributedString(string: newStringStrike)
attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 1, range: NSMakeRange(0, attributeString.length))
yourLabel.attributedText = attributeString
Based on previous answers you can define something like this for swift 5
extension UILabel{
func setTextWithStrike(text: String)
{
let attributedString = NSMutableAttributedString(string: text)
attributedString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 1, range: NSMakeRange(0, attributedString.length))
self.attributedText = attributedString
}
}
and then use it like this:
labelWithStike.setTextWithStrike(text: "text with strike")

UILabel negative line spacing

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
}
}

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