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.
Related
I have a circumstance in my app whereby a label may be given a font size greater than it's height. This is to do with some rather complex architecture and layout code. Normally I would increase the label's height to accommodate the larger font but that is profoundly difficult to do in my scenario and I would rather avoid it.
The next logical thing is to turn clipsToBounds off, to allow the text sublayer to overflow the bounds of the label. Unfortunately this seems to have no effect in this case as the text is still clipped.
Am I missing something?
Looking at the documentation for UILabel:
https://developer.apple.com/documentation/uikit/uilabel/1620545-textrect
I think you need to override the method textRect(forBounds:limitedToNumberOfLines:) by explicitly increasing the rectangle returned by this method to the containing size of the label’s string rather than the bounds of the label.
(This solution does of course require you to subclass.)
Hope that helps.
You should be able to get the font height from font.lineHeight and then reduce the font size until the line height is less than the label height.
The reason (need citation) is that UILabel which is embeeded in UIButton cares extra glyph information embedded in font whereas an independent UILabel doesn't.
Solution
You can nest a separate UILabel on top of your UIButton and it will solve the problem. It's ugly but it works. There are few workarounds that you ought to try.
Workarounds
Depending on the scenario here is a small checklist that I found as accepted answer or useful for someone.
1) If you're using a UIButton Make sure you're using this method
[button setTitle forState:]
otherwise you'd need to use the following code to refresh the state
[myButton setNeedsLayout];
2) You might need to adjust your font size to fit the width of the label.
[yourLabel setAdjustsFontSizeToFitWidth:YES];
3) Although setting clipToBounds works in consecutive hierarchy, You might want not want to set individually on either Button or Label.
[yourButton setClipsToBounds:NO];
[yourButton.titleLabel setClipToBounds:NO];
There are few solutions that are pointing UIButton subclassing method which are essentially trying to add UIEdgeInset to button.
I have some ASCII syntax diagrams which must not have line breaks in the middle.
These don't have to be editable so I thought the best way is to use an UILabel with auto shrink option. But this option shrinks the text also if the content doesn't fit the height of the labels frame rectangle.
I just want to shrink only if the content doesn't fit the width. It would be absolutely fine to scroll vertically through the text.
What is the best way to do this with UILabel or any other UI element?
Use UITextView with 'editable' property set to false.
So let me rephrase your question. I guess what you want is a UILabel which can show multiple lines, but the longest line need to fit into the width of UILabel. If this is what you want, well the imagination is weird to me...
But anyway, I feel there's a conflict in your settings. First, allowing multiple lines implies you set "Lines" attribute (number of lines) as 0, which allows unlimited lines. But then Autoshrink will play no effect. I'm afraid it is not possible to be done by just setting the storyboard and instead, you need to write some code.
I guess people have raised related questions earlier, by which they want to dynamically change the font size when the text become too long. I guess you want to take a look about this:
Autoshrink on a UILabel with multiple lines
The last issue is you also want the scrolling effect (this is why I feel the outlooking will be weird.) But in short, to achieve this you need 1) dynamically change the UILabel height, most likely using the same technique as explained in the reference thread, and 2) wrap the UILabel in a scroll view. Maybe this can achieve what you want.
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.
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"
I'm working on an iOS app at the moment where I'm displaying lots of text in table view cells. The text in question is stored in an NSAttributedString can span an indefinite amount of lines and can have inline attachments such as images. The issue here is that I can't seem to get an accurate measure of a given cell's height to pass on to the table view. Is there a performant, simple way to calculate the height of an arbitrarily complex NSAttributedString contained in a UITextView?
You can call the boundingRectWithSize:options:context: method to find out its size.
[attributedString boundingRectWithSize:CGSizeMake(320.0f, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
You can substitute the 320.0f with whatever your expected width is for your text view.
Reference: NSAttributedString UIKit Additions Reference
As a footnote, NSString has similar categories outlined here.
I had many failed attempts at this until I came across the free Sensible TableView framework. The framework has what they call a TextViewCell that automatically resizes according to the text inside. Highly recommended.