Xcode UILabel bug? Line spacing cropping text with a UILabel - ios

I have some designs I'm following for an iOS project. The font used is Avenir with relatively tight line spacing.
Some of these labels will have dynamic text, so I can't just make the label's size larger since the size should be determined by the content.
By default line spacing for a UILabel ends up pretty large.
If I adjust the Line Height Multiple or the Max Height, the text along the top ends up cropped.
It should behave like this (Affinity Designer)...
Is there a way to handle this?
Thanks for your help!

This works for me. By adding
minimumLineHeight
let string = NSMutableAttributedString(string: venue.name)
let style = NSMutableParagraphStyle()
style.lineHeightMultiple = 0.68
style.minimumLineHeight = nameLabel.font.lineHeight
string.addAttribute(NSAttributedString.Key.paragraphStyle,
value: style,
range: NSMakeRange(0, venue.name.count))
nameLabel.attributedText = string

Unfortunately the UILabel has several quirks when it comes to vertical adjustments. A somewhat hacky solution is to move the baseline of the first line down as needed. Depending on if your string ends with a newline, and the amount of tightening you do, you might need to add one or two extra newlines also, otherwise the rendering engine will clip the last line.
The code snippet assumes that self.label already has an attributed string assigned to it, and that it has line separator character 0x2028 between the lines. This is usually true when entering multi-line text in IB.
// 0x2028 is the unicode line separator character
// Use \n instead if it is what you have
// or calculate the length of the first line in some other way
NSInteger lengthOfFirstLine = [self.label.text componentsSeparatedByString:#"\u2028"][0].length;
NSMutableAttributedString *s = [[NSMutableAttributedString alloc] initWithAttributedString:self.label.attributedText];
// Add two more blank lines so that the rendering engine doesn't clip the last line
[s appendAttributedString:[[NSAttributedString alloc] initWithString:#"\n\n"]];
// Move the baseline offset for the first line down
// the other lines will adjust to this
// 50 is a value you will have to find what looks best for you
[s addAttribute:NSBaselineOffsetAttributeName value:#(-50) range:NSMakeRange(0, lengthOfFirstLine)];
self.label.attributedText = s;

Related

How to keep a space at the end of the string in ios, objective C without changing its UI(position)

I have created a label programmatically.it's with is equal to the device width and I have aligned it to right.so it shows the text from right.
like this
titleLabel.textAlignment = NSTextAlignmentRight;
then I gave a text to the label.
titleLabel.text = #"Flight Summary";
but I want to keep a space after y letter in summary, without decreasing the width of the label.I tried with using string format like this.
titleLabel.text =[NSString stringWithFormat:#"%# ", #"Flight Summary "];
but nothing happned.how can I do that.hope your help for this.thanx.
To reduce the complexity of subclassing, you can take UIView and UIlabel, set frame of UIView to screen width and take UILabel frame as screenwidth - 8 (or whatever pixels is appropriate for you). Manage the frames and add both to mainview, this way you will be able to achieve the look.
Try this
titleLabel.text = #"abcd exadgdf \u{200c}""
You can also do one more thing. You can use attributed string and set attributed text to titlelabel without formatting string like this
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:#"ABCD "];
titleLabel.attributedText = attrString;

UILabel Vertical align missing space

I've used UILabel for showing text with vertical alignment. I'm showing text as character by character with vertical alignment. Actually It's an moving text by alignment with CABasicAnimation. But my problem here is
Each line should show one character, but when I try stretch label width to
show single character, Some character is wiped partially. see screen
shot of xib(sample with small text).. Or try to extend frame size, some row show single character and some shows double character.
Space is missing between words. see screenshot.
How can I fix it with my xib? Answer also acceptable with programmatically.
Note: Text used in this example "Some text with long".
You don't need vertical align. Just make the UILabel wider and input a \n after every character of the string.
NSString *longString = #"This is a very very long string";
NSString *labelString = #"";
self.label.text = labelString;
self.label.numberOfLines = 500; //just put a big number or calculate something
for (int n=longString.length - 1; n > -1; --n) {
labelString = [NSString stringWithFormat:#"%#\n%#", [longString substringWithRange:NSMakeRange(n, 1)], labelString];
self.label.text = labelString;
}

How can I horizontally center a single line of text in UITextView?

I have a UITextView which displays a fair amount of text. I need to horizontally center some, but not all, of the lines of text in the text view.
I've tried setting the NSParagraphStyleAttributeName with an NSParagraphStyle instance whose alignment property is NSTextAlignmentCenter on the range of the line to be centered. I'm also setting the font so the centered line is bold. When I do this using the code below, the relevant line is bolded, but remains left aligned.
if (shouldCenterLine) {
NSMutableParagraphStyle *centerStyle = [[NSMutableParagraphStyle alloc] init];
centerStyle.alignment = NSTextAlignmentCenter;
NSDictionary *attributes = #{NSParagraphStyleAttributeName : centerStyle,
NSFontAttributeName : boldFont};
[attributedString setAttributes:attributes range:lineRange];
}
If I apply this same paragraph style attribute to the entire contents of the text view, the text ends up centered as expected.
Is it possible to convince UITextView to center specific subranges of its text? Is the only solution to move to a UIWebView rendering generated HTML? Note that this is for an app still supporting iOS 6, so unfortunately I'm not able to use Text Kit.
Try including the line returns around the the line that you'd like centered within the range that you apply the style to.

How to wrap text around attachments using iOS7 Text Kit?

I am using the new Text Kit API to add attachments to some attributed text:
// create an attachment for each image
NSTextAttachment* ta = [NSTextAttachment new];
ta.image = [UIImage imageNamed:#"imageName"];
// add to the attributed text string
NSAttributedString* rep = [NSAttributedString attributedStringWithAttachment:ta];
[myAttributedTextString appendAttributedString:rep];
This works fine, I can see my image rendered in the output. However, I cannot find any way to specify the image alignment, or wrap text around the image.
Any ideas?
NOTE: Text attachments are different from exclusions paths - a text attachment is part of the 'model', i.e. it is part of the attributed text string that the layout manager performs text layout on. Whereas an exclusion path is part of the view.
NSTextAttachments are treated as a single character by NSAttributedString. So, in order to adjust their alignment you must do so as you would for text. It took me hours of fiddling with attachment.bounds (which I never could get to work properly) to finally figure this out. Here's an example of how to horizontally align an NSTextAttachment.
#def BETWEEN_SECTION_SPACING 10
// creates a text attachment with an image
NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
attachment.image = [UIImage imageNamed:#"sample_image.jpg"];
NSMutableAttributedString *imageAttrString = [[NSAttributedString attributedStringWithAttachment:attachment] mutableCopy];
// sets the paragraph styling of the text attachment
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init] ;
[paragraphStyle setAlignment:NSTextAlignmentCenter]; // centers image horizontally
[paragraphStyle setParagraphSpacing:BETWEEN_SECTION_SPACING]; // adds some padding between the image and the following section
[imageAttrString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [imageAttrString length])];
After this, you would append imageAttrString to an existing attributed string and perhaps append another after it. One quirk is that because the attachment is a character it is not treated as its own paragraph. In order for that to be the case you will need to surround it with \n (newline characters). Just append these to both sides of the attachment's attributed string.
Hope that helps, it took me ages to figure out.
Try setting the bounds property to the image size.
Defines the layout bounds of the receiver's graphical representation in the text coordinate system.
So it should be:
ta.bounds = (CGRect) { 0, 0, ta.image.size };
ta.bounds = (CGRect) { 0, yPadding, ta.image.size };
change yPadding you need.
It can be negative when image's height is large than line height.

iOS: sizeWithFont for single character is different than in a string?

I am trying to determine the precise position of a character in a UILabel, say:
(UILabel *)label.text = #"Hello!";
I'd like to determine the position of the 'o'. I thought that I could just sum the widths of all the preceding characters (or the whole preceding string) using sizeWithFont. The width value I get though is bigger by about 10% than what it should be. Summing the widths of individual letters (i.e. [#"H" sizeWithFont...] + [#"e" sizeWithFont...] + l... + l...) accumulates more error than [#"Hell" sizeWithFont...].
Is there a way of accurately determining the position of a single glyph in a string?
Many thanks.
Yes, but not in a UILabel and not using sizeWithFont:.
I recently worked with Apple Developer Support, and apparently sizeWithFont: is actually an approximation. It becomes less accurate when your text (1) wraps across multiple lines and (2) contains non-latin characters (i.e. Chinese, Arabic), both of which cause line spacing changes not captured by sizeWithFont:. So, don't rely on this method if you want 100% accuracy.
Here are two things you can do:
(1) Instead of UILabel, use a non-editable UITextView. This will support the UITextInput protocol method firstRectForRange:, which you can use to get the rect of the character you need. You could use a method like this one:
- (CGRect)rectOfCharacterAtIndex:(NSUInteger)characterIndex inTextView:(UITextView *)textView
{
// set the beginning position to the index of the character
UITextPosition *beginningPosition = [textView positionFromPosition:textView.beginningOfDocument offset:characterIndex];
// set the end position to the index of the character plus 1
UITextPosition *endPosition = [textView positionFromPosition:beginningPosition offset:1];
// get the text range between these two positions
UITextRange *characterTextRange = [textView textRangeFromPosition:beginningPosition toPosition:endPosition]];
// get the rect of the character
CGRect rectOfCharacter = [textView firstRectForRange:characterTextRange];
// return the rect, converted from the text input view (unless you want it to be relative the text input view)
return [textView convertRect:rectOfCharacter fromView:textView.textInputView];
}
To use it, (assuming you have a UITextView called myTextView already on the screen), you would do this:
myTextView.text = #"Hello!";
CGRect rectOfOCharacter = [self rectOfCharacterAtIndex:4 inTextView:myTextView];
// do whatever you need with rectOfOCharacter
Only use this method for determining the rect for ONE character. The reason for this is that in the event of a line break, firstRectForRange: only returns the rect on the first line, before the break.
Also, consider adding the method above as a UITextView category if you're gong to be using it a lot. Don't forget to add error handling!
You can learn more about how firstRectForRange: works "under the hood" by reading the Text, Web, and Editing Programming Guide for iOS.
(2) Create your own UILabel by subclassing UIView and using Core Text to render the strings. Since you're doing the rendering, you'll be able to get the positions of characters. This approach is a lot of work, and only worthwhile if you really need it (I, of course, don't know the other needs of your app). If you aren't sure how this would work, I suggest using the first approach.
Well fonts are smart now a day and take in respect the position of a character to its pervious character.
Here is an example on how the starting position of the letter o:
NSRange posRange = [hello rangeOfString:#"o"];
NSString *substring = [hello substringToIndex:posRange.location];
CGSize size = [substring sizeWithFont:[UIFont systemFontOfSize:14.0f]];
No you can do the same for the string including the letter o and substract the size found in the string without the letter o.
THis should give the an nice start position of the letter and the size.
in ios6 you can do using attributed string
NSMutableAttributedString *titleText2 = [[NSMutableAttributedString alloc] initWithString:strHello];
NSRange posRange = [hello rangeOfString:#"o"];
[titleText2 addAttributes:[NSDictionary dictionaryWithObject:[UIFont systemFontOfSize:14.0f] forKey:NSFontAttributeName] range:NameRange];
and set your textView with this attributed string

Resources