NSMutableParagraphStyle is not called - ios

I have a UILabel on xib file and I want to increase the line spacing of the texts.
I tried to write, but with the code below only text alignment is called and line spacing remains the same. Why "paragraphStyle.lineSpacing" is not called?
class PlaySheetCellLeft: UITableViewCell {
#IBOutlet var LBLTitle:UILabel!
var message:[String:Any]? {
didSet{
guard let msg = self.message else { return }
self.LBLTitle.text = title
}
override func awakeFromNib() {
super.awakeFromNib()
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 10
let attrString = NSMutableAttributedString()
attrString.addAttribute(NSParagraphStyleAttributeName, value:paragraphStyle, range:NSMakeRange(0, attrString.length))
LBLTitle.attributedText = attrString
LBLTitle.textAlignment = NSTextAlignment.center
}
}

You're creating a range with an empty attributed string, so the style isn't being set on anything.
When you create your range, you're actually creating a range like this: NSMakeRange(0, 0).
You should pass your string into the creation of the mutable attributed string like this: NSMutableAttributedString(string: "your string")
You'll also need to set this when you set the text of your label, which by the looks of your code isn't in awakeFromNib

Related

How do I avoid this while changing the character spacing of UITextView?

I am trying to change the character spacing of uitextview. However it changes whole appearance.
Screenshots:
Before adding the code
After adding the code
Code (as extension):
extension UITextView {
func addCharacterSpacing(kernValue: Double = 1.15) {
if let textValue = text, textValue.count > 0 {
let attributedString = NSMutableAttributedString(string: textValue)
attributedString.addAttribute(NSAttributedStringKey.kern, value: kernValue, range: NSRange(location: 0, length: attributedString.length - 1))
attributedText = attributedString
}
}
}
If you are using NSMutableAttributedString, so you should customize it fully in the same way with paragraph style and fonts for it or any other settings.
From the screenshots I see a problem with paragraph style, setup it manually to the textView:
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = .center
textView.attributedText = NSAttributedString(
string: "string",
attributes: [.paragraphStyle: paragraph])

Is is possible to mix different alignments in an NSAttributedString?

I have a simple question concerning NSMutableAttributedString (or NSAttributedString).
Is it possible to do the following.
Supposing the string I want to display is: "Hello\nWorld"
(Hello on the first line an World on the second)
Is it possible to have the first line (Hello) left aligned and the second line (World) right aligned?
If the answer is YES, how can I do that?
This can be achieved using NSParagraphStyle:
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let paragraphStyle1 = NSMutableParagraphStyle()
paragraphStyle1.alignment = .left
let paragraphStyle2 = NSMutableParagraphStyle()
paragraphStyle2.alignment = .right
let attrString = NSMutableAttributedString.init(string: "Hello \n World")
if let range = attrString.string.range(of: "Hello") {
let nsrange = attrString.string.nsRange(from: range)
attrString.addAttributes([NSAttributedStringKey.paragraphStyle: paragraphStyle1], range: nsrange)
}
if let range = attrString.string.range(of: "World") {
let nsrange = attrString.string.nsRange(from: range)
attrString.addAttributes([NSAttributedStringKey.paragraphStyle: paragraphStyle2], range: nsrange)
}
self.label.textAlignment = .right
self.label.attributedText = attrString
}
Output:

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.

Is it possible to set a different colour for one character in a UILabel?

I've been working on a small project using Xcode. It uses a lot of labels, textfields, etc. I've finished with most of the layout, the constrains, and the forms, titles, etc. After which, the client announces that for all required fields, there should be a red asterisk next to the label.
Call me lazy, but I'd rather not go back to all of my forms, add in a lot of labels with asterisks on them, and re-do my auto-layout to accommodate the new labels.
So, is there a way to change the colour of a specific character (in this case, the asterisk) in a UILabel, while the rest of the text stays black?
You can use NSMutableAttributedString.
You can set specific range of your string with different color, font, size, ...
E.g:
var range = NSRange(location:2,length:1) // specific location. This means "range" handle 1 character at location 2
attributedString = NSMutableAttributedString(string: originalString, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 18.0)!])
// here you change the character to red color
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: range)
label.attributedText = attributedString
Ref: Use multiple font colors in a single label - Swift
You can change a lot of attribute of String.
Ref from apple: https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Classes/NSMutableAttributedString_Class/index.html
let text = "Sample text *"
let range = (text as NSString).rangeOfString("*")
let attributedString = NSMutableAttributedString(string:text)
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor() , range: range)
//Apply to the label
myLabel.attributedText = attributedString;
UILabel have an .attributedText property of type NSAttributedString.
Declaration
#NSCopying var attributedText: NSAttributedString?
You let your asterix * have a single .redColor() attribute (NSForegroundColorAttributeName), whereas the rest of the new label simply uses the same text as before, however also contained in an NSAttributedTextString.
An example follows below using a function to repeatedly update your existing labels to attributed strings prefixed with a red *:
class ViewController: UIViewController {
#IBOutlet weak var myFirstLabel: UILabel!
#IBOutlet weak var mySecondLabel: UILabel!
let myPrefixCharacter = "*"
let myPrefixColor = UIColor.redColor()
// ...
override func viewDidLoad() {
super.viewDidLoad()
// ...
/* update labels to attributed strings */
updateLabelToAttributedString(myFirstLabel)
updateLabelToAttributedString(mySecondLabel)
// ...
}
func updateLabelToAttributedString(label: UILabel) {
/* original label text as NSAttributedString, prefixed with " " */
let attr = [ NSForegroundColorAttributeName: myPrefixColor ]
let myNewLabelText = NSMutableAttributedString(string: myPrefixCharacter, attributes: attr)
let myOrigLabelText = NSAttributedString(string: " " + (label.text ?? ""))
/* set new label text as attributed string */
myNewLabelText.appendAttributedString(myOrigLabelText)
label.attributedText = myNewLabelText
}
// ...
}
Swift 4
(Note: notation for attributed string key is changed in swift 4)
Here is an extension for NSMutableAttributedString, that add/set color on string/text.
extension NSMutableAttributedString {
func setColor(color: UIColor, forText stringValue: String) {
let range: NSRange = self.mutableString.range(of: stringValue, options: .caseInsensitive)
self.addAttribute(NSAttributedStringKey.foregroundColor, value: color, range: range)
}
}
Now, try above extension with UILabel and see result
let label = UILabel()
label.frame = CGRect(x: 40, y: 100, width: 280, height: 200)
let red = "red"
let blue = "blue"
let green = "green"
let stringValue = "\(red)\n\(blue)\n&\n\(green)"
label.textColor = UIColor.lightGray
label.numberOfLines = 0
let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: stringValue)
attributedString.setColor(color: UIColor.red, forText: red) // or use direct value for text "red"
attributedString.setColor(color: UIColor.blue, forText: blue) // or use direct value for text "blue"
attributedString.setColor(color: UIColor.green, forText: green) // or use direct value for text "green"
label.font = UIFont.systemFont(ofSize: 26)
label.attributedText = attributedString
self.view.addSubview(label)
func updateAttributedStringWithCharacter(title : String, uilabel: UILabel) {
let text = title + "*"
let range = (text as NSString).range(of: "*")
let attributedString = NSMutableAttributedString(string:text)
attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.red , range: range)
uilabel.attributedText = attributedString }
I know this is an old post, but i want to share my approach (which is based on the answer from dfri) i just made it a function for convenience.
func lastCharOnColor(label: UILabel, color: UIColor, length: Int) {
//First we get the text.
let string = label.text
//Get number of characters on string and based on that get last character index.
let characterCounter = string?.characters.count
let lastCharacterIndex = characterCounter!-1
//Set Range
let range = NSRange(location: lastCharacterIndex, length: length)
let attributedString = NSMutableAttributedString(string: string!, attributes: nil)
//Set label
attributedString.addAttribute(NSForegroundColorAttributeName, value: color, range: range)
label.attributedText = attributedString
}
I use this function to just set the last character from a label to a certain color like this:
lastCharOnColor(label: self.labelname, color: UIColor.red, length: 1)
Hope this helps anyone.
Here is an extension for simply making mandatory labels by appending a red *
Swift 5
extension UILabel {
func makeTextMandatory() {
let text = self.text ?? "" + " *"
let range = (text as NSString).range(of: " *")
let attributedString = NSMutableAttributedString(string:text)
attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.red , range: range)
self.attributedText = attributedString
}
}
Usage :
dobLabel.makeTextMandatory()

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.

Resources