UILabel Line Spacing - ios

I am trying to set the line spacing in a UILabel as specified by Mike Slutsky here. It works correctly for the text I specify from the Storyboard. When I try to set the UILabel.text in code, it reverts back to the default line spacing. Can someone help me understand how to either:
Keep it from reverting to default, and use the settings I specified on the Storyboard or
Set the value in code.
I've seen a lot of examples around using NSAttribute and NSParagraph, but since it's now possible to set in the Storyboard, I would expect their may be a more straightforward answer. Many thanks for the help!
I set the "Height Multiple" as illustrated in the above link, and my only code is as follows:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var textView: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
textView.text = "Some example text"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
If I remove the textView.text line, it displays correctly, otherwise it's set back to the default line spacing (or Height Multiple).

Here's what's going on. You set things up in the storyboard with custom line spacing. This means, even though you may not know it, that in the storyboard you have set the label's attributedText.
But if you then come along and set the label's text in code, as you are doing, you throw away the attributedText and therefore all the attributes that it had. That is why, as you rightly say, things revert to the default look of the label.
The solution is: instead of setting the label's text, set its attributedText. In particular, fetch the label's existing attributedText; assign it into an NSMutableAttributedString so you can change it; replace its string while keeping the attributes; and assign it back to the label's attributedText.
So for example (I have called my label lab - your textView is a bad choice, as a text view is whole different animal):
let text = self.lab.attributedText
let mas = NSMutableAttributedString(attributedString:text)
mas.replaceCharactersInRange(NSMakeRange(0, count(mas.string.utf16)),
withString: "Little poltergeists make up the principle form of material manifestation")
self.lab.attributedText = mas

UILabel has
#property(nonatomic, copy) NSAttributedString *attributedText
since iOS 6.0,
This property is nil by default. Assigning a new value to this property also replaces the value of the text property with the same string data, albeit without any formatting information. In addition, assigning a new a value updates the values in the font, textColor, and other style-related properties so that they reflect the style information starting at location 0 in the attributed string.
If you set textView.text = "Some example text" again, you will loose your attributes. You should only pick one of them and not switching between them if you are sure what you are doing

Here is #matt 's answer in Obj. C:
NSMutableAttributedString *mas = [self.lab.attributedText mutableCopy];
[mas replaceCharactersInRange:NSMakeRange(0, [mas.string length])
withString:#"Little poltergeists make up the principle form of material manifestation"];
self.lab.attributedText = mas;
Hope this helps someone!

Swift 3
Just copy & execute this code to see result
let label = UILabel()
let stringValue = "UILabel\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

Related

How to align Right-Justify UILabel?

Remark:
Implementing:
myLabel.textAlignment = .right
does not solves my problem, that is not what I am asking for.
What I am trying to achieve is to let the alignment for the label to be Right-Justify.
To make it more clear:
That's how left alignment looks:
And that's how justify alignment looks:
if you are not seeing any difference between the tow screenshots, you could recognize it by the right (trailing) constraint of the label, you will notice in the second screenshot the whole width of the label has been filled by the text.
As shown in the second screenshot, letting the label to be justified will make it Left-Justify by default, i.e the last line alignment is left.
How can I make let the label to be justified to the right? In other words, I want the text to be just like the 2nd screenshot except that I want the last short line to be shifted to the right.
If you wondering what is the purpose of doing such a thing, it would be appropriate for right to left languages.
Thanks to #user6788419 for mentioning the appropriate answer.
For achieving it, you should work with NSMutableParagraphStyle:
An object that enables changing the values of the subattributes in a
paragraph style attribute.
It gives you the ability to the alignment and baseWritingDirection simultaneously, which leads to the desired output:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var lblDescription: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let text: NSMutableAttributedString = NSMutableAttributedString(string: "In vertebrates, it is composed of blood cells suspended in blood plasma. Plasma, which constitutes 55% of blood fluid, is mostly water (92% by volume), and contains dissipated proteins...")
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .justified
paragraphStyle.baseWritingDirection = .rightToLeft
paragraphStyle.lineBreakMode = .byWordWrapping
text.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, text.length))
lblDescription.attributedText = text
}
}
The output would be:
There is not ENUM available for right justified!
You have to choose from NSTextAlignmentLeft,NSTextAlignmentRight,NSTextAlignmentCenter,NSTextAlignmentJustified,NSTextAlignmentNatural.
You have added screenshot of xcode's interface builder! once check it in simulator or device.
If you want space at left or right side in label with justified text then you can play with edge insets.
For example Left padding in label.

UILabel with attributedText in custom UITableViewCell: Color not rendering until reuse (iOS10)

I have a convenience extension like this:
extension NSMutableAttributedString {
func append(string: String, attributes: [String: Any]) {
let attributed = NSMutableAttributedString(string: string)
let range = NSRange(location: 0, length: string.utf16.count)
for (attribute, value) in attributes {
attributed.addAttribute(attribute, value: value, range: range)
}
append(attributed)
}
}
I'm styling text in my UILabel thusly:
let normalAttributes = [NSForegroundColorAttributeName: darkGray]
let lightAttributes = [NSForegroundColorAttributeName: lightGray]
let text = NSMutableAttributableString()
text.append("Part 1", attributes: normalAttributes)
text.append("Part 2", attributes: lightAttributes)
All of this within a custom UITableViewCell class. What's happening is that the text is rendering with the base color in the NIB file, rather than the custom foreground color as per my attributed string - until I do something (like scroll around) that causes cell reuse, after which suddenly the colors render correctly.
I can't seem to find anything wrong with the lifecycle of the object, and there's no other place that's messing with this label. The label, fwiw, is an IBOutlet, so I'm not creating a new one each time or anything strange.
Turns out it's the same problem as:
Attributed string in tableviewcell not showing bold text until the cell is dequeued and reloaded
So it's solved by setting the font & color to nil on the label before setting the attributeText. Annoying, but easy enough to do that work-around.
As #Kiril Savino suggested. Setting up Font & Color of UILabel to nil is do easy work-around to get it done.
But one more thing I noticed today is Default Font used from Storyboard or XIB.
It should be System Font from Storyboard or XIB to make this work.
If you have used other Custom Font, then it won't work.
So, make sure Default Font of UILabel should System Font.
Thank you.

How to add indents to Multiple lines of UILabel?

While I found how to add indentations to first line (FirstLineHeadIndent) and to the rest of lines (HeadIndent), I cannot find how to add indents to only first two/three lines in order to achieve something like this:
PS: This is not a duplicate, because I'm not asking how to indent only first line, as one user suggested.
Using TextKit Framework in ios
CGRect checkBoxFrame = [self.textView convertRect:self.checkView.bounds fromView:self.checkView];
checkBoxFrame.origin.x -= self.textView.textContainerInset.left;
checkBoxFrame.origin.y -= self.textView.textContainerInset.top;
UIBezierPath *checkBoxPath = [UIBezierPath bezierPathWithOvalInRect:checkBoxFrame];
self.textView.textContainer.exclusionPaths = #[checkBoxPath];
It will exclude the image path inside content of TextView
You need to set your UILabel text as Attributed string in storyboard.
Then you can edit the indentation of each line, and you can also paste any text you've created with text editor and it will keep its indentation as well as other attributes.
You can of course manipulate these attributes programmatically, here is an example:
#IBOutlet weak var label: UILabel!
let text = "\tfirst line\n \tsecond line\nthird line\nforth line"
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.tabStops = [NSTextTab(textAlignment: NSTextAlignment.left, location: 15, options: [:])]
paragraphStyle.headIndent = 10
label.attributedText = NSAttributedString(string: text, attributes: [NSParagraphStyleAttributeName: paragraphStyle])
Here is an example of how to configure it:
Here is how to configure indentation:
Here is the example on the simulator:
You would need to use the stringattribute property of the UILabel.AttributedText.
its actually of type NSMutableAttributedString so I first casted
label.AttributedText to the mutable type then I could manipulate it.‡
To do this you would use the following:
var mutable = Control.AttributedText as NSMutableAttributedString;
UIStringAttributes uiString=new UIStringAttributes();
Then you need to set an indent on the first line (Do this the way you already know how to), and then you would set the headIndent of its paragraph style like below.
this is converted from objective-c so might not be perfect: ‡
NSMutableParagraphStyle paragraphStyle = new NSMutableParagraphStyle();
paragraphStyle.headIndent = 14;
NSDictionary attributes (){
StyleAttributeName = paragraphStyle;
};
mutable.AddAttribute(attributes);
Control.attributedText = mutable;
I believe something like this in combination with your 'FirstLineHeadIndent' code should do the trick.
‡ How to manipulate NSAttributedString
‡ Objective-C second line with indent

Attributed string with custom fonts in storyboard does not load correctly

We are using custom fonts in our project. It works well in Xcode 5. In Xcode 6, it works in plain text, attributed string in code. But those attributed strings set in storyboard all revert to Helvetica when running on simulator or device, although they look all right in storyboard.
I'm not sure if it's a bug of Xcode 6 or iOS 8 SDK, or the way to use custom fonts is changed in Xcode 6 / iOS 8?
The simplest answer that worked for is to drag the fonts into FontBook. If the fonts are in your project but not in your computer's FontBook, IB sometimes has trouble finding it. Weird, but has worked for me on several occasions.
The fix for me was to use an IBDesignable class:
import UIKit
#IBDesignable class TIFAttributedLabel: UILabel {
#IBInspectable var fontSize: CGFloat = 13.0
#IBInspectable var fontFamily: String = "DIN Light"
override func awakeFromNib() {
var attrString = NSMutableAttributedString(attributedString: self.attributedText)
attrString.addAttribute(NSFontAttributeName, value: UIFont(name: self.fontFamily, size: self.fontSize)!, range: NSMakeRange(0, attrString.length))
self.attributedText = attrString
}
}
Giving you this in the Interface Builder:
You can set up your attributedstring just as you normal do, but you'll have to set your fontsize and fontfamily once again in the new available properties.
As the Interface Builder is working with the custom font by default, this results in a what you see is what you get, which I prefer when building apps.
Note
The reason I'm using this instead of just the plain version is that I'm setting properties on the attributed label like the linespacing, which are not available when using the plain style.
You can add custom fonts to font book.
Step1: Click on manage fonts. It opens the font book.
Step2: Click on plus and add your fonts.
Next time when you click on font with attributed text newly added font also will show in the list. But make sure your custom font added in info.plist and bundle resources.
My solution is a bit of a work around. The real solution is for apple to fix Interface Builder.
With it you can mark all the bold and italic text in interface builder using a system font, then at runtime render your custom font. May not be optimal in all cases.
NSMutableAttributedString* ApplyCustomFont(NSAttributedString *attributedText,
UIFont* boldFont,
UIFont* italicFont,
UIFont* boldItalicFont,
UIFont* regularFont)
{
NSMutableAttributedString *attrib = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText];
[attrib beginEditing];
[attrib enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, attrib.length) options:0
usingBlock:^(id value, NSRange range, BOOL *stop)
{
if (value)
{
UIFont *oldFont = (UIFont *)value;
NSLog(#"%#",oldFont.fontName);
[attrib removeAttribute:NSFontAttributeName range:range];
if([oldFont.fontName rangeOfString:#"BoldItalic"].location != NSNotFound && boldItalicFont != nil)
[attrib addAttribute:NSFontAttributeName value:boldItalicFont range:range];
else if([oldFont.fontName rangeOfString:#"Italic"].location != NSNotFound && italicFont != nil)
[attrib addAttribute:NSFontAttributeName value:italicFont range:range];
else if([oldFont.fontName rangeOfString:#"Bold"].location != NSNotFound && boldFont != nil)
[attrib addAttribute:NSFontAttributeName value:boldFont range:range];
else if(regularFont != nil)
[attrib addAttribute:NSFontAttributeName value:regularFont range:range];
}
}];
[attrib endEditing];
return attrib;
}
Inspired by this post
Thanks to this thread, I've come to this solution:
private let fontMapping = [
"HelveticaNeue-Medium": "ITCAvantGardePro-Md",
"HelveticaNeue": "ITCAvantGardePro-Bk",
"HelveticaNeue-Bold": "ITCAvantGardePro-Demi",
]
func switchFontFamily(string: NSAttributedString) -> NSAttributedString {
var result = NSMutableAttributedString(attributedString: string)
string.enumerateAttribute(NSFontAttributeName, inRange: NSRange(location: 0, length: string.length), options: nil) { (font, range, _) in
if let font = font as? UIFont {
result.removeAttribute(NSFontAttributeName, range: range)
result.addAttribute(NSFontAttributeName, value: UIFont(name: fontMapping[font.fontName]!, size: font.pointSize)!, range: range)
}
}
return result
}
I have struggled with this bug: UILabel displays correctly in IB with custom font but does not display correctly on device or simulator (font is included in the project and is used in plain UILabels).
Finally found Attributed String Creator on (Mac) App Store. Generates code to be placed in your app in the appropriate place. Fantastic.
I am not the creator, just a happy user.
Met the same problem: the attribute font set for TextView in storyboard didn't work in run time with XCode 6.1 & iOS 8 SDK.
This is how I solved this issue, might be a workaround for you:
open the attribute inspector of your textview, change text to
"Plain"
click on the cross to delete the "wC hR"(red below)
change text to "Attributed", and then you can set the font and size
for your text.
run to check if it works
Try this it will work
In my case when i try to set "Silversky Technology" as Attributed text for label from interface builder its not show when i run in simulator but its show in interface builder. So i used one trick i made Silversky font with 1 pixel bigger then Technology text.
Attribute text have issue with same size of font so change size of 1 word this thing work with me.
May be this is xcode bug but this trick work for me.
Met the same problem: the attribute font for UILabel in storyboard didn't work in run time. Using this UIFont+IBCustomFonts.m works for me
https://github.com/deni2s/IBCustomFonts
The same problem.
Solved: Just check Selectable in TextView. Without this i have standard System font.
Double click and install the font to the system. It will work (Xcode 8.2)
#Hamidptb solution works, make sure to get the correct name of the font (once you've added it to Font Book)
Open the Font Book application, navigate to your font then press Command+I. The PostScript name is the font name you want to use here:
UILabel.appearance().font = UIFont(name: "PostScriptName", size: 17)
I was trying to get tableView cells with text having multiple paragraphs. The attributed strings seemed to be a way to get extra space between the paragraphs (something a bit nicer looking than doing two line-feeds in the string). Came across this and other posts when I discovered that the IB settings didn't apply at run time when you wanted to put different text in the cell.
The main thing I came up with was adding an extension to String (using Swift) to
create an attributed string with certain characteristics. Example here uses the Marker Felt font, as it is easily distinguishable from Helvetica. The example also shows a little extra bit of spacing between paragraphs to make them more distinct from each other.
extension String {
func toMarkerFelt() -> NSAttributedString {
var style = NSMutableParagraphStyle()
style.paragraphSpacing = 5.0
let markerFontAttributes : [NSObject : AnyObject]? = [
NSFontAttributeName : UIFont(name: "Marker Felt", size: 14.0)!,
NSParagraphStyleAttributeName: style,
NSForegroundColorAttributeName : UIColor.blackColor()
]
let s = NSAttributedString(string: self, attributes: markerFontAttributes)
return s
}
}
Then, in my custom tableViewCell, you send it the text you want and it converts it to an attributed string on the UILabel.
// MarkerFeltCell.swift
class MarkerFeltCell: UITableViewCell {
#IBOutlet weak var myLabel: UILabel!
func configureCellWithString(inputString : String) {
myLabel.attributedText = inputString.toMarkerFelt()
}}
In the view controller with the tableView, you should register your cell in viewDidLoad() -- I used a nib, so something like:
let cellName = "MarkerFeltCell"
tableView.registerNib(UINib(nibName: cellName, bundle: nil), forCellReuseIdentifier: cellName)
To get the cell to figure out how tall it should be, make a prototype cell that is used to get size info, and is never added into the tableView. So, in your
view controller's variables:
var prototypeSummaryCell : MarkerFeltCell? = nil
Then in (probably override - depending on your view controller) heightForRowAtIndexPath:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
// ...
if xib == "MarkerFeltCell" {
if prototypeCell == nil {
prototypeCell = tableView.dequeueReusableCellWithIdentifier(xib) as? MarkerFeltCell
}
let width : CGFloat = tableView.bounds.width
let height : CGFloat = prototypeCell!.bounds.height
prototypeCell?.bounds = CGRect(x: 0, y: 0, width: width, height: height)
configureCell(prototypeCell!, atIndexPath: indexPath)
prototypeSummaryCell?.layoutIfNeeded()
let size = prototypeSummaryCell!.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
let nextHeight : CGFloat = ceil(size.height + 1.0)
return nextHeight
} else { // ...
In the above code, the prototypeCell will be filled in the first time it is needed. The prototypeCell is then used to figure out the height for the cell after going through the autosizing process. You will need to round up the height with the ceil() function. I also added in some extra fudge factor.
The final code bit is how you configure the text for the cell. For this example, simply:
func configureCell(cell :UITableViewCell, atIndexPath indexPath: NSIndexPath) {
if let realCell = cell as? MarkerFeltCell {
realCell.configureCellWithString("Multi-line string.\nLine 2.\nLine 3.") // Use \n to separate lines
}
}
Also, here is a shot of the nib. Pinned the label to the edges of the cell (with margin desired), but used a "Greater Than or Equal" constraint, with a less than "Required" priority for the bottom constraint.
Set the label's font to Attributed. Actual IB font didn't matter.
The result in this case:
In case of attributed string you can add custom font in font list as -
Click on font icon this will display following dialog .In the following dialog you can add your own category or existing one for custom font.attributed font dialog
After it click on Manage Fonts it open the following dialog select category in you created or existing one . Click on + sign to add font in the category.
Manage font dialog
that's have a simple and quick solition and that's work in my case .
that solution is add a code line in didFinishLaunchingWithOptions func in AppDelegate.swift file :
for textViews :
UITextView.appearance().font = UIFont(name: "IranSans", size: 17)
for labels :
UILabel.appearance().font = UIFont(name: "IranSans", size: 17)
and for rest of UiView like this two ☝️
For anyone applying custom fonts to attributed string in code: Try setting it in viewDidLayoutSubviews. My mistake was doing it in viewDidLoad, it won't be applied there.

UITextField - Remove Ellipses on Text Overflow

I have a UItextfieldthat holds a person's middle name. I only want it to display the first initial, which it does, but i want it to hold their entire name. It's only large enough to show the one initial, but it adds that ellipses (...) after the letter.Is it possible to remove those when a uitextfield overflows? I haven't found anything online regarding someone with the same issue.
Thankyou for your help
I do see this truncation when the text field resigns first responder. I fixed this by setting the lineBreakMode in the NSParagraphStyle attribute to .byClipping. I happened to be using a subclass of UITextField so I overrode resignFirstResponder() to do this. My textField starts out empty so there is no attributedString to start with in viewDidLoad.
override func resignFirstResponder() -> Bool {
guard let newAttributedText = (attributedText?.mutableCopy() as? NSMutableAttributedString) else {
return super.resignFirstResponder()
}
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byClipping
newAttributedText.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedText?.length ?? 0))
attributedText = newAttributedText
return super.resignFirstResponder()
}
This might not work if you set the text in code. In that case, you might want to set the lineBreak mode in a common function that you call from both the override of resignFirstResponder() and after setting the text in code. You could make a set(text: String?) function and call the common function from there.
A UITextField shouldn't be truncating the text (because you can usually scroll/select that UI element).
A UILabel will truncate by default, you can set it to clip instead.

Resources