Space characters being removed from end of String - UILabel Swift - ios

I have the following code for a Table View Cell in Swift
let rcap = cell.viewWithTag(613) as! UILabel
rcap.text = "Capacity: \(room.capacity) " // I added space at the end
The space characters at the end of the string, are removed when shown on screen.
If I add space characters at the beginning of the string there is no issue.
At the moment I am using this 'full stop' hack, but it is not good enough:
rcap.text = "Capacity: \(room.capacity) ."
Any ideas?
I also tried:
rcap.text = "Capacity: \(room.capacity) " + " "

Adding a constraint to the label seems like the better solution to me.
It allows you to define a well-defined distance between the label and
the margin of the table view cell.
The width of a space is dependent on the font and might even change if the text in the label is shrunk, causing non-aligned texts in the
table view.
Having said that, you can prevent the trailing space from being
removed by appending a "ZERO WIDTH NON-JOINER" character (U+200C):
rcap.text = "Capacity: \(room.capacity) \u{200c}"
But I consider that more as a "trick" than the proper solution to the
problem.
Update: It seems that this "trick" does not work any more in iOS 10,
so a layout constraint should be used instead, as initially suggested.

For 2020
This issue is critical when working with monospaced fonts and doing layout, such as, say, user codes which may have alignment spaces at the end in monospace.
\u{200c} does seem to work perfectly:
Please note that the solution of #MartinR does in fact work perfectly, today, 2020 A.D., iOS 13, Xcode 11.
.text = "FATTIE KODE " + reservation + reservation.neededTrailingSpaces + \u{200c}"
.text = "FATTIE KODE " + reservation + reservation.neededTrailingSpaces + \u{200c}"
.text = "FATTIE KODE " + reservation + reservation.neededTrailingSpaces + \u{200c}"
You can now absolutely normally set font sizes, etc etc. and it all works properly.
Bonus code sample!
Here's a simple UILabel that pads it out, so you need do nothing else:
class PaddyLabel: UILabel {
// pad always to 20 characters...
override var text: String? {
get {
return super.text
}
set {
if newValue == nil {
super.text = nil
return
}
var t = newValue!
while t.count < 20 { t += " " }
t += "\u{200c}"
super.text = t
}
}
So in the above three yellow examples, you would just do this
.text = "FATTIE KODE " + reservation
and not have to bother adding the needed spaces manually.
Note that even if you are not using monospace fonts, a typical real-world use case is when you want to "pile together" UILabels - imagine them being, say, one word each - and have the absolutely correct space between them regardless of font size etc, that is to say as if it's just the one sentence in the one UILabel.
The only way to actually do that is to have UILabel "actually include the space".
If it does not work in a future iOS...
Perhaps this will (again?) break in a future iOS.
The only other solution I know is this. To begin with, it's ridiculously hard to just add padding to a UILabel. It is explained here:
Adding padding manually to a UILabel?
How to actually reliably add damned padding to a UILabel
In fact, using a similar technique, it would be possible to force the extra width on one end using intrinsicContentSize , drawText and textRect. To know how much width to add, you would have to calculate that using a phantom example text, with the number of characters in question. Doing that calculation would call for the usual annoying techniques for calculating likely text rendering size. But because that sucks, fortunately we can use the amazing tip of #MartinR (as usual!)

I have created custom class for UILabel, take a look:
import UIKit
#IBDesignable class CustomLable: UILabel {
#IBInspectable var topInset: CGFloat = 5.0
#IBInspectable var bottomInset: CGFloat = 5.0
#IBInspectable var leftInset: CGFloat = 7.0
#IBInspectable var rightInset: CGFloat = 7.0
override func drawText(in rect: CGRect) {
let insets = UIEdgeInsets.init(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
super.drawText(in: rect.inset(by: insets))
}
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
return CGSize(width: size.width + leftInset + rightInset,
height: size.height + topInset + bottomInset)
}
}
You can than assign a class to UILabel from Storyboard and provide space you need.

Try putting zero-width space instead of the period. It can be written using left Alt and 0173 on numeric keyboard. Or you can copy it here and delete the quotes: "­"
So your code for setting the text will look like this (with the zero-width space at the end):
rcap.text = "Capacity: \(room.capacity) ­"
Non-breaking space (\u{00a0}) should work too and its usage is as \u{200c}'s in Martin's and Fattie's answer

rcap.text = String.localizedStringWithFormat("Capacity: %# ", rrom.capacity)
try above code it may works

Related

Get truncated text from UILabel in Swift [duplicate]

I have a single line UILabel. It has width = screen width and the content now is (the content of UILabel can change)
You have 30 seconds to make an impression during an interview
Currently, my UILabel is truncated tail and the word "duration" is not complete
self.nameLabel.lineBreakMode = NSLineBreakByTruncatingTail;
What I want is I want my UILabel still truncating tail and only display complete word.
Like the image below
Any help or suggestion would be great appreciated.
You can do something like this:
let labelWidth = CGRectGetWidth(label.bounds)
let str = "You will have 30 seconds till you give us a good impression" as NSString
let words = str.componentsSeparatedByString(" ")
var newStr = "" as NSString
for word in words{
let statement = "\(newStr) \(word) ..." as NSString
let size = statement.sizeWithAttributes([NSFontAttributeName:label.font])
if size.width < labelWidth {
newStr = "\(newStr) \(word)"
}
else{
break
}
}
newStr = newStr.stringByAppendingString(" ...")
self.label.text = newStr as String
Idea is: we split words and try check the width while appending from the beginning + the string "..." till we found the a word that will exceed the size, in the case we stop and use this new string
Ideally this is not possible,with default UILabel, when you set lineBreakMode to TruncatingTail, depending on the space required by the letter/word the OS will truncate it, one solution to fix the issue you can use following properties depending on your match.
Minimum Font Scale -- Use this property to specify the smallest multiplier for the current font size that yields an acceptable font size to use when displaying the label’s text. If you specify a value of 0 for this property, the current font size is used as the smallest font size.
Minimum Font Size -- When drawing text that might not fit within the bounding rectangle of the label, you can use this property to prevent the receiver from reducing the font size to the point where it is no longer legible.
i am not sure but try it:
nameLabel.adjustsFontSizeToFitWidth = NO;
nameLabel.lineBreakMode = NSLineBreakByWordWrapping;
OR
If you are using storyboard follow these steps i tried this and it working fine
Open Attribute Inspector
Change Line Breaks to Truncate Tail then
Change AutoShrink to Minimum Font Size
here are my screenshots of label after and before applying these properties
new output

iOS 13: How can I tweak the leading / descend / line height of a custom font in UIKit / SwiftUI

I'm using a custom font and somehow the rendering screws up the line height, potentially because of misconfigured descent or leading (?), so that g's and j's are cut off in the last line of the rendered text. I think it might be a problem with this particular font, because Sketch is also exposing similar issues with the font in question, but I feel like I don't understand quite enough about typographic measurements or fonts. I found this Apple documentation page on Typographic Concepts quite insightful.
I looked into the font itself with the test version of FontLab, which I have used for the first time btw - so I've little clue really what I'm looking at. It does seem like the g is going below the configured descent, which seems to be what the last line is. (?) (See: Character view in FontLab, showing the descend of the g)
Via lineSpacing I could adjust the distance between just the lines itself to fix this in the first few lines. I know iOS 14 is going to bring a way to modify the leading of a Text in SwiftUI. But I need to target iOS 13, so that doesn't help.
I've also tried SwiftUI's Text, a normal UILabel.text and UILabel.attributedText with a customized paragraph style, but nothing I adjust there seems to mitigate the problem.
The view is not even clipping. Just adding padding to the frame does not help at all. It increases the distance, but the g's and j's are still cut.
What can I do? Subclass UILabel and overwrite the intrinsicContentSize to add some extra space, when there is a g and j in the last line? That feels a) dirty and b) given that padding didn't help, it might not fix the problem?
Is the font itself the problem here? Can I patch the font somehow without making it worse?
Is there any way to modify the leading or the descend height of the font, when I use lower level APIs? Seems like I could go down to CoreText, as CTFontCreateCopyWithAttributes(_:_:_:_:) is a candidate, if I could just modify via attributes the leading, line space or the descend? Can or monkey-patch / swizzle things without shooting myself in the knee? Should I just file a radar a feedback?
You need to use NSAttributedString instead of String to control the line spacing of UILabel. Here is sample code
let style = NSMutableParagraphStyle()
style.lineSpacing = 20
let string = NSMutableAttributedString(string: "The quick brown fox jumps over the lazy dog, The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog")
string.addAttribute(.paragraphStyle, value: style, range: NSMakeRange(0, string.length))
let label = UILabel(frame: CGRect(x: 20, y: 100, width: 300, height: 500))
label.attributedText = string
label.numberOfLines = 0
self.view.addSubview(label)
Out put
I know what you are asking as I have faced the same issues with custom fonts. I am going to offer two solutions. In my own project I went the way of your suggestion in overriding intrinsicContentSize and adding a padding multiplier for height and width. In my case the fonts were user facing so I had a struct that held all the relevant information. FYI Chalkduster is in the system and clips. I also believe that this is all due to the font file itself.
Solution 1:
Example:
struct UserFont{
var name : String
var displayName : String
var widthMultiplier : CGFloat
var heightMultiplier : CGFloat
}
Then in my UILabel I have it subclassed to use both of these metrics
#IBDesignable
class MultiplierUILabel: UILabel {
#IBInspectable var widthPaddingMultiplier : CGFloat = 1
#IBInspectable var heightPaddingMultiplier : CGFloat = 1
override var intrinsicContentSize: CGSize{
return CGSize(width: super.intrinsicContentSize.width * widthPaddingMultiplier, height: super.intrinsicContentSize.height * heightPaddingMultiplier)
}
}
This to me was the simplest implementation as I found the font and multiplier scale accordingly.
Solution 2:
You might be able to get the draw to occur slightly higher by measuring the glyph bounds and adjusting the origin y. For example this fixes the clipping on Chalkduster font that is included in the system.
#IBDesignable
class PaddingUILabel: UILabel {
override func drawText(in rect:CGRect) {
//hello
guard let labelText = text else { return super.drawText(in: rect) }
//just some breathing room
let info = boundsForAttrString(str: labelText, font: self.font!, kern: .leastNormalMagnitude)
let glyph = info.glyph
var newRect = rect
let glyphPadding = -(glyph.origin.y)
if glyphPadding - info.descent > 1 && info.descent != 0{
newRect.origin.y -= glyphPadding/2
}else{
if info.descent != 0{
newRect.origin.y += (info.descent - glyphPadding)/2
}
}
super.drawText(in: newRect)
}
func boundsForAttrString(str:String,font:UIFont,kern:CGFloat)->(glyph:CGRect,descent:CGFloat){
let attr = NSAttributedString(string: str, attributes: [.font:font,.kern:kern])
let line = CTLineCreateWithAttributedString(attr)
var ascent : CGFloat = 0
var descent : CGFloat = 0
var leading : CGFloat = 0
CTLineGetTypographicBounds(line, &ascent, &descent, &leading)
let glyph = CTLineGetBoundsWithOptions(line, .useGlyphPathBounds).integral
return (glyph,leading != 0 ? descent : 0)
}
}
Result of Solution 2:
System
PaddingUILabel using glyph bounds

How to make multi-line UILabel text fit within predefined width without wrapping mid-word

I have a UILabel carefully laid out in Interface Builder with proper height and width constraints. The number of lines is set to 4. The wrapping is set to word wrap. The text is "CHECKED". The font size is very large and thus it only fits "CHECKE" and the "D" is on the second line. Writing "Checked" instead of "CHECKED" lets the font shrink (as intended) so that the whole word fits. But (the text is user given and it can be expected that the user writes fully uppercase words) having uppercase words the label does not break it/shrink the font as expected.
Do you have a suggestion as to what I might have missed? Capitalising the words (thusly only having the first letter uppercase) does work, but is not what the client wants.
Updated question
The problem seems to be unrelated to having uppercase or lowercase text. My problem could be solved by an answer to the following question:
How to make (ideally with the help of only Interface Builder) the UILabel text shrink trying to fit full words within all available lines without wrapping the text mid-word?
If the text "CHECKED" is too wide for a label (with more than 1 line available) it should shrink the font size instead of breaking the "D" and wrapping the single letter to the next line.
If the text is "one CHECKED two" and the single word "CHECKED" is already too wide for a label (with more than 1 line available) it should break between all words and shrinking the font size so that "CHECKED" still fits the middle line.
Avoiding:
one
CHECKE
D two
Thank you very much!
Here is a UILabel subclass that will find the largest word in the labels text, use the boundingRect function of NSString to see how large that one word will be with the current font, and drop the font size until it fits the width.
class AutosizingMultilineLabel: UILabel {
override func layoutSubviews() {
super.layoutSubviews()
self.adjustFontToFitWidth()
}
func adjustFontToFitWidth() {
guard let currentFont = self.font else { return }
let minimumFontSize: CGFloat = floor(self.minimumScaleFactor * currentFont.pointSize)
var newFontSize = currentFont.pointSize
var theNewFont = currentFont
if let text = self.text, let longestWord = text.components(separatedBy: " ").max(by: {$1.count > $0.count})?.replacingOccurrences(of: "\n", with: "") {
let nsString = longestWord as NSString
while newFontSize > minimumFontSize {
theNewFont = currentFont.withSize(newFontSize)
let boundingRect = nsString.boundingRect(with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude),
options: NSStringDrawingOptions.usesLineFragmentOrigin,
attributes: [.font: theNewFont],
context: nil)
if ceil(boundingRect.size.width) <= self.bounds.size.width {
break
}
newFontSize -= 1
}
self.font = theNewFont
}
}
}
When the word is bigger than the line, word wrap doesn't work. If it doesn't fit on this line, it won't fit on the next line. (same word, same size, same line size). To make it fit, the label will start putting letters on the next line.
If you allow multiple lines on your label, the OS will try to fill the lines before adjusting the font size.
I think you're just running into a limitation on Autoshrink.
In Interface Builder:
add a new UILabel with Width: 230 and Height: 280
set the Font to System 44.0
set Line Break: Truncate Tail
set Autoshrink: Minimum Font Scale at 0.15
set the text of the label to test CHECKED lines
Now, drag the handle on the right edge of the label left and right... when it gets too narrow, the word CHECKED will break onto the next line.
Change CHECKED to checked and do the same thing. You should see the same behavior.
Now, try dragging the Bottom edge up and down. With either CHECKED or checked, you should see the Font Size auto shrink.
So... to do what you're trying to do, you might have to skip Autoshrink and instead do some code calculations.
Edit: further visual of what goes on...
Start with above values, but set the Height of the label to 170 - gives it just a little vertical padding.
Now, drag the left edge to make it narrower.
When you reach the end of the word CHECKED, and keep going, you will see the font shrink until it gets small enough that there is space for it to wrap to a 4th line.
I think you're going to need some code to get exactly what you need.

elipsis dots after UITextfield for secured text entry

I have multiple uitextfields setup and they are all connected with IBOutlets. I have one textfield that is a password and I have the 'Secure text Entry' selected. when I have this check I get this
Any ideas why this happens? If i deselect the secured entry the textfield rises fine depending on the size of the password with no ellipsis dots.
It does not matter how long the password is. Same thing.
If i don't have the security text selected it works fine
Any idea why? It can be a width issue because it does autosize. But why does the 'secure text entry' cause the issue?
I have faced the same problem. I think it is a bug of UITextfield. It calculates size for the text but not for the secure text(dots). I have the problem when the text includes slim characters like 1, l etc...
As workaround I have subclassed the UITextfield class and overridden intrinsicContentSize function. You might need to adjust letter spacing. I couldn't find how to get it dynamically depending on font.
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
if !self.isSecureTextEntry {
return size
}
var width = size.width
if let font = self.font,
let charCount = self.text?.count {
width = "•".size(withAttributes: [NSAttributedString.Key.font : font]).width * CGFloat(charCount)
width += (CGFloat(charCount)+1) * 4.5 // this magic number is for letter spacing
}
return CGSize(width: width, height: size.height)
}

Inset text in UILabel from left

I have a UILabel which I am using to display multiple lines of text. At the moment when the text is displayed, it is right up against the left hand side of the label which doesn't look too great. I would like the text to be inset slightly from the left.
This is my code so far:
if notes.objectAtIndex(indexPath.row) as NSString == "" {
cell.notesLabel.text = "No notes to display."
cell.notesLabel.textAlignment = NSTextAlignment.Center
} else {
cell.notesLabel.textAlignment = NSTextAlignment.Left
}
I was looking at some Objective-C examples but I couldn't get them to work and I don't really think they were what I was looking for.
Also, I was trying to do the same thing with a different label and in that case I assumed I could have just added " " to the end of the string (as it is a single line label) to move it in from the right, but I was surprised to see that this doesn't work?
Thanks.
To inset the text from the left edge, you should create a UILabel subclass, and override drawTextInRect:,
class RDLabel: UILabel {
override func drawTextInRect(rect: CGRect) {
let newRect = CGRectOffset(rect, 10, 0) // move text 10 points to the right
super.drawTextInRect(newRect)
}
}

Resources