Modifying line spacing property in NSParagraphStyle causes cursor elongation - ios

I have a subclassed UITableViewCell that contains a UITextView. I've added NSParagraphStyle as an attribute to the string in a subclassed NSTextStorage. In the following code, I've increased the space between each row in the UITextView.
swift
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 11
myCustomTextStorage.appendAttributedString(NSAttributedString(string: someText, attributes: [NSParagraphStyleAttributeName: paragraphStyle]))
The cursor height elongates till the height of the next line as shown below. This only happens on the rows before the last row.
I've looked at several posts on SO regarding this issue, including this post; however, none of the proposed solutions seem to be working for me.
I've read through the TextKit documentation but haven't found any solution for this issue.
Is there a way to reduce the cursor height?

This works as designed and it's the same on the Mac. It's meant to provide the user with visual feedback of both which line they're on and the height of that line. You should think hard before changing this just because you don't like the way it looks.
That said, the method in the SO post you linked above is the general approach for adjusting the cursor (though the adjusted rect's origin may need to be adjusted as well). What about it didn't work for you? It'd be better to start from there.

an answer related to this issue is given on this thread. just pasting here for future reference.
iOS - UITextView lineSpacing make cursor height not same
"you could change cursor height by subclassing the UITextView, then override the caretRectForPosition:position function. For Example :
(CGRect)caretRectForPosition:(UITextPosition *)position {
CGRect originalRect = [super caretRectForPosition:position];
originalRect.size.height = 18.0;
return originalRect;
}
"

Related

Calculation Frame of a UILabel Subclass

The bottom line is that I'm trying to reproduce the UI that iMessage has.
For the Label:
The special padding of that type of text made me create a custom UILabel. Here's the code under drawTextInRect:
[super drawTextInRect:UIEdgeInsetsInsetRect(rect, UIEdgeInsetsMake(0, 15.0, 5.0, 15.0))];
No mystery for now.
The problem comes when my cell (that contain that label has to calculate height).
The Label gets a rounding effect on the label like so:
cell.message.layer.cornerRadius = 18;
[cell.message sizeToFit];
Apparently I can't get the proper height and width of that label. I'm using sizeToFit and then I mesure the possible sizes with "sizeWithFont:" (deprecated in iOS 7) and "boundingRectWithSize:".
The only way the text can show properly is adding manually an undetermined amount of size to height and width once the calculations are made.
The best I can get then is a screen that may look good but still has some problems and not draws properly the texts.
The link has a screen of some of the screens not showing properly.
The only answer I've been able to see looking at code from other people is that they at some point make their own calculations based on letter size.
Anyone with this problem check : https://github.com/jessesquires/MessagesTableViewController
It was the only source I could find, at the end for some cases the boundingRectWithSize is not good enough.

What is the most concise and semantic way to lay out a paragraph of text of variable length for IOS7?

As far as I know, laying out paragraphs can be done with:
a) UITextView
Resizing the textview to fit the content in didlayoutsubviews
Example here: Weird thing occurs when using sizetofit of a UITextView in IOS7
b) UILabel
Setting the Lines to 0, using sizetofit
Example here: Multiple lines of text in UILabel
However, I am unable to get either of these to work in IOS7 (having previously used them in ios6.x). There must be a definitive and clear way to just lay out a paragraph, its such a seemingly simple task.To be specific, this is just a paragraph of text that is:
non-editable
variable length
Works consistently whether using storyboards or code only
So please, what is the way to do this?
UITextView works fine on iOS 7. If you don't use Auto Layout, then calling sizeToFit on UITextView object should be enough. If you do use Auto Layout, then make a height constraint on UITextView object and set its constant in code in the following way:
CGSize sizeThatFits = [self.textView sizeThatFits:CGSizeMake(yourAvailableWidth, MAXFLOAT)];
self.textViewHeightConstraint.constant = ceilf(sizeThatFits.height);
I've seen some problems with UILabel recently, e.g. Lines missing from tall UILabel when embedding NSTextAttachment
With the UILabel, I was able to to get this working by:
setting the lines to 0
setting my line break mode to word wrap
ensuring that the height constraint is set to a "greater than or equal to"

UILabel with NSAttributedString is clipping content

I've got a UILabel set up with auto layout in such a way that its height is based on its intrinsic content size, such that it gets taller when there are more lines in it. I need it to be centered alongside other elements in the same view. With everything default, it works just fine.
However, I'm using a custom font that has a bit too much space in it. I've set up an NSMutableParagraphStyle, like so:
NSMutableParagraphStyle *headlineParagraphStyle = [NSMutableParagraphStyle new];
headlineParagraphStyle.lineSpacing = 0.0f;
headlineParagraphStyle.maximumLineHeight = 20.0f;
headlineParagraphStyle.hyphenationFactor = 0.0f;
headlineParagraphStyle.alignment = NSTextAlignmentLeft;
I'm then creating and setting an NSAttributedString as the UILabel's -attributedText:
NSString *uppercaseHeadline = self.currentStory.headline.uppercaseString;
NSAttributedString *attributedHeadline = [[NSAttributedString alloc] initWithString:uppercaseHeadline attributes:#{NSParagraphStyleAttributeName: headlineParagraphStyle}];
self.headlineLabel.attributedText = attributedHeadline;
The result is that the text looks okay, but it's shoved up above the top of the UILabel and clipped off at the top, while there's still extra space at the bottom of the label:
This also throws off the centering of other items against the text in this label, since you can see that the space between the two lines does not line up with the center of the label's frame.
How can I tell UILabel to recenter this text, so that the top doesn't clip and there isn't any space at the bottom?
I've realized I never came back and answered this question after iOS 7 was released and the NDA on that platform lifted. As of iOS SDK 7.0, it is possible to use the NSAttributedString attribute NSBaselineOffsetAttributeName, which did exactly what I needed to do.
It was available but "no longer supported" in iOS 6. However, when building with the iOS 7 SDK, it appeared to do the right thing.
Edit: In case it's unclear, I don't recommend doing this. If Apple says it's no longer supported, it's probably not a good idea to rely on it. It worked for me, but it's definitely not a long-term solution.
I just had a wild ride with this. For whatever reason, using lineHeightMultiple, maximumLineHeight, and/or minimumLineHeight blows the offset like this.
However, using lineSpacing (which is not an absolute value but a relative value) will change the spacing between lines without messing up the offset.
In iOS 10, my solution was to only set maximumLineHeight and to NOT set minimumLineHeight (otherwise, the top of the label gets clipped). Changing lineSpacing or lineHeightMultiple did not help.
I feel it's important to link to the 'bizarre interview': http://i.imgur.com/pFeqPHd.gif.
You may need to edit the font's ascender property, see here: UIButton custom font vertical alignment
in my case numberOfLines = 0 was needed
otherwise, counterintuitively, line after the newline were clipped

How do I prevent text from being cut off like in UITextView?

I have a UILabel and a UITextView both with the same text (the string "SnellRoundhand", in Snell Roundhand Bold font, point size 21). The text in the UITextView appears correctly, but the UILabel has its text cut off on the left and right sides. How do I get the text in the label to appear properly?
Some notes:
Expanding the frame of the label won't work because it might solve the cutoff issue on the right side of the text but not on the left side.
I can't take the cheap way out and center the text; the text must stay at whatever alignment it is in right now.
The reason I can't just change everything to UITextViews is because my app does some processing in the background and it crashes whenever it instantiates a UITextView. I'm hoping I can get around the issue by using UILabel instead to render the text.
In iOS 6, a very nice new feature of UILabel is that it supports attributed strings. Attributed strings can include paragraph margins. So you can add margins to your string, thus ensuring that there will be some extra space between the edges of the label and the drawing of the string.
This section of my book has sample code that adds margins to a string drawn in a UILabel:
http://www.apeth.com/iOSBook/ch23.html#_attributed_strings
If you want to run on a system earlier than iOS 6, you can subclass UILabel to force the text to be drawn inset from the edges of the label, as shown in this code (also from my book):
- (void)drawTextInRect:(CGRect)rect {
[super drawTextInRect:CGRectInset(rect, 5.0, 5.0)];
}
I don't have a good answer, but I can suggest a kludge that sorta does what you want done and might give you some useful ideas. The problem sure seems to suggest a fundamental problem with Label and TextView handling for funky fonts.
The concept is simple enough:
Size the TextField with left (or right) justification to just fit the contents.
Slightly enlarge the text field width.
Change the justification to center.
This will result in a field just wide enough to display the text without clipping (if you enlarge it the right amount). I know you said you couldn't change the text alignment, but doing it this way only moves the text a point or two and it ends up where it needs to be to display the full text. The field ends up the size, and the text in the position it ought to be. For instance:
self.textField.textAlignment = NSTextAlignmentLeft;
[self.textField sizeToFit];
CGRect frame = self.textField.frame;
frame.size.width += 4;
self.textField.frame = frame;
self.textField.textAlignment = NSTextAlignmentCenter;
This works. If it isn't useful to you directly I hope it gives you some ideas.
Something I tried that wasn't helpful was subclassing UITextField and overriding the textRectForBounds: to enlarge the area used to draw the text. I could move the text starting position slightly to the right, but it still clipped the left edge. Turning off the clipsSubviews property didn't help. Seems like Apple's problem here.
On iOS 6 the UITextView has a margin on each side, you can adjust the content inset to prevent the text from using those margins.
[textView setContentInset:UIEdgeInsetsMake(-8, -8, -8, -8)];

Determine padding and size of iOS UITextView

Project
I am attempting to replicate the native iMessage app. I have used AcaniChat as a foundation.
I wanted automatic highlighting, so I modified the code to use UITextView instead of UILabel. I realize there are options such as FancyLabel and Three20. However, UITextView does this natively.
Problem
I am having a difficult time getting the padding/size of UITextView right. I updated the contentInset property based on suggestions in other answers.
msgText.contentInset = UIEdgeInsetsMake(-11.0f, -8.0f, 0.0f, -8.0f);
I am also determining the size with the following:
CGSize size = [[(Message *)object text] sizeWithFont:[UIFont systemFontOfSize:kMessageFontSize]
constrainedToSize:CGSizeMake(kMessageTextWidth, CGFLOAT_MAX)
lineBreakMode:UILineBreakModeWordWrap];
Nonetheless, some text is still being cut off (see image below). Notably the phone number and email address (right) as well as the "that cut off?" (left)
I have verified this is not due to the dataDetectorsTypes property.
Question
I can solve this by increasing the CGRect of the UITextView. But I want to better understand the affects of margin/padding and size of the UITextView. I don't want to arbitrarily increase the size by 20.0f to make it work.
As such, what is the code or combination of code that I can reliably set the size of the message bubble?
Not a solution for your problems with UITextView but ...
I had similar problems and decided to give core text a chance. You have simply more control about what is happening.
You could have your string attributed using regex statements to make links/emails/phone numbers visible and have furthermore the chance to use even other fonts.

Resources