How to make height of OHAttributedLabel scale with content height? - objective-c

I use an OHAttributedLabel called demoLbl for displaying text with formatted areas. This label is laid out with Interface Builder and is connected to a property in my ViewController. After setting the attributedText to the label I want all the text to be displayed in the label.
If I don't resize the label then the text is cropped at the end of the label so the rest of the text is missing.
If I use [demoLbl sizeToFit]; then the height of the label is larger or smaller in height than the text (about 10 point, varying with the text's length) thus giving me blank areas at the bottom of my view (after scrolling) plus the width of the label is increased by about 2 points.
If I calculate the height of the original text (NSString) before putting it in a NSAttributedString and adding it to the label's attributedText property then the calculated height is way too small for setting it as the label's height.
Is there a hack or trick I can apply so that the label's height is adjusted according to the NSAttributedString's height?
PS: To be more specific I wanted to add OHAttributedLabel as a tag but it's not allowed to me yet.

I'm the author of OHattributedLabel.
I made some fixes recently about my computation of the size. Please check it out it will probably solve your issue.
I also added a method named sizeConstrainedToSize:fitRange: in NSAttributedString+Attributes.h that returns the CGSize of a given NSAttributedString (quite the same way UIKit's sizeWithFont:constrainedToSize: works, but for Attributed strings and CoreText and not plain stings an UIKit)
Actually OHAttributedLabel's sizeThatFits: calls this method itself now.

You can see if this category gives you a more reliable height.
https://gist.github.com/1071565
Usage
attrLabel.frame.size.height = [attrLabel.attributedString boundingHeightForWidth:attrLabel.frame.size.width];

I added this code to the implementation of the OHAttributedLabel class:
// Toni Soler - 02/09/2011
// Overridden of the UILabel::sizeToFit method
- (void)sizeToFit
{
// Do not call the standard method of the UILabel class, this resizes the frame incorrectly
//[super sizeToFit];
CGSize constraint = CGSizeMake(self.frame.size.width, 20000.0f);
CGRect frame = self.frame;
frame.size = [self sizeThatFits:constraint];
[self setFrame:frame];
}
// End Toni Soler - 02/09/2011
Thank you Olivier for sharing your code!

Related

AutoLayout Scrolling UITextView sizeThatFits not working

In my UIViewController, I have a non-editable attributed UITextView with scrolling enabled, and I would like to resize the height of the scrolling text to accommodate all screen size widths without having extra white space. I am using AutoLayout and set up outlets for the textview and its height constraint. In the view controller's viewDidLoad method I use the sizeThatFits method to update the textview height, but the resulting size is too small.
- (void)viewDidLoad{
[super viewDidLoad];
NSLog(#"INIT HEIGHT: %f, myTextView.frame.size.height);
CGSize sizeToFit =
[myTextView sizeThatFits: CGSizeMake(myTextView.frame.size.width, MAXFLOAT)];
myTextViewHeightConstraint.constant = sizeToFit.height;
[myTextView updateConstraints];
[myTextView layoutIfNeeded];
NSLog(#"NEW HEIGHT: %f", myTextView.frame.size.height);
}
The log indicates the height was indeed changed from my default of 4000 to 2502, but with 2502 I only see about half of my text via scrolling. I am avoiding nesting the text view in a scroll view, as one solution suggests, since this is discouraged in documentation. What am I missing? Thanks in advance.
I found that the frame of a UITextView will automatically size to its content, so dynamically setting it is not necessary. sizeThatFits is a UIView method and doesn't modify the size of the text content, but rather the size of the window that the text appears in. The only code I needed was to scroll the text to the top:
[myTextView scrollRectToVisible: CGRectMake(0,0,1,1) animated:NO];
This, as well as any Autolayout constraint updates shouldn't be made in viewDidLoad, but rather in viewDidLayoutSubviews or viewWillAppear. Best placement could probably be explained better - I'm open to comments.

sizeWithFont:constrainedToSize doesn't work with custom font

I'm trying to resize a UITextView to the size the text within it.
The problem is that Im using a custom font and it the text doesnt fit within the UITextView.
NSString *textToFit = #"pretty long text";
UIFont *customFont = [UIFont fontWithName:#"Museo-100" size:15];
CGSize sizeText = [textToFit sizeWithFont:customFont constrainedToSize:CGSizeMake(textFrame.size.width, 1000)];
Where textFrame is the frame of the UITextView I want to adjust its height.
Im trying different fonts and also different files of the same font and still it never adjusts its height to the height that the text fills.
I've been searching and I dont find a solution. I've tried a workaround using a UILabel and the method textRectForBounds, but still no success, something on this lines.
UILabel *auxLabel = [[UILabel alloc]init];
auxLabel.numberOfLines = 0;
auxLabel.font = [UIFont fontWithName:#"Museo-100" size:15];
auxLabel.text = //THE TEXT I WANT TO FIT IN
CGRect textSize = CGRectMake(0.0, 0.0, textDescription.frame.size.width, FLT_MAX);
CGRect frame = [auxLabel textRectForBounds:textSize limitedToNumberOfLines:0];
I think
UIView : sizeToFit
Should solve your problem.
sizeToFit Resizes and moves the receiver view so it just encloses its
subviews.
Discussion: Call this method when you want to resize the current view so that it uses the most appropriate amount of space.
Specific UIKit views resize themselves according to their own internal
needs. In some cases, if a view does not have a superview, it may size
itself to the screen bounds. Thus, if you want a given view to size
itself to its parent view, you should add it to the parent view before
calling this method.
https://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/uiview/uiview.html#//apple_ref/occ/instm/UIView/sizeToFit

UITextView size for UITableViewCell

I have a tableview, one of the rows contains a cell that contains a UITextView. I need to know the size of the textview because I need it to fit the cell and return that size in 'heightForRowAtIndexPath' method. Using the NSString method for size only works for labels, not for textviews. What are my options?
Thanks
Does the UITextView already have the proper size, or do you need to resize it as well to exactly contain its text? There are no out-of-the-box methods to do that I believe.
If it already has the proper size, you can just get the height from its frame:
CGRect frame = textView.frame;
CGSize size = frame.size;
CGFloat = size.height;

UIScrollView: handling of dynamic content?

I've a Storyboard with a UIScrollView which contains two UILabels, a UIImageView and a UITextView. The content of the UIImageView and UITextView is dynamic and so are their height.
Currently I'm doing this inside my viewDidLoad to adjust the size of the UITextView after the dynamic text is inserted:
CGRect frame = self.textView.frame;
frame.size.height = self.textView.contentSize.height;
self.textView.frame = frame;
Is this the way to change its height?
My next problem is to set the content size for the UIScrollView, to activate the scrolling. Is there a smart way to get the height of all its content or do I have to calculate the height for each element and set the sum of this as the content size of the UIScrollView?
IF you had no space in between your objects, you could make a for loop in your scrollView.subviews and add up all the heights to set as the contentSize.
As you probably don't have everything tight together, you're probably better by getting the bottom most object and adding up it's frame.origin.y and it's frame.size.height (maybe you want to have some extra space in here, but that's up to you) and that will give you your contentSize.height to keep everything in there.

Getting NSTextView to perfectly fit its contents

I have a view that contains a button and a textview. When the button is clicked, the textview's hidden status will change and be shown on the view. Springs and struts have been configured so the textview expands vertically with the view. All this is done in IB
I then insert text into the textview programmatically, but I need the textview to show all its contents without the user needing to scroll.
This is the code I use to calculate the height of the text in the textview:
- (float) getTextViewHeight {
//based on http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/TextLayout/Tasks/StringHeight.html%23//apple_ref/doc/uid/20001809-CJBGBIBB
[textview.textContainer setLineFragmentPadding:0.0];
[textview.layoutManager glyphRangeForTextContainer:textview.textContainer];
return [textview.layoutManager usedRectForTextContainer:self.interactionData.textContainer].size.height;
}
With or without that call to -sizeToFit on the textview, it will either be too big or too small (depending on its contents).
I need to get the height of the textview with all the contents showing so I can adjust the view's size.
I know I could probably use a NSTextField as a label, but I need a NSTextView for its added functionality (specifically using the enclosing scrollview's rulerview).
Does anybody have any suggestions?
NSTextView generally will resize itself if its string over-runs the container width. I think this is because the contained cell has a default behavior for text over-run, called "Line Wrap" or something. My gut feeling is you could just ask the TextView for it's height after it's been loaded and adjust the containing view accordingly, all without needing a layout manager. And obviously make sure the auto-resizing mask is set (oh, you're doing this in IB so no worries there). I could be wrong, and I didn't do any tests... but yeah, you could try it! :P
Her's how I do it and it works well:
// Help text.
NSBundle* mainBundle = [NSBundle mainBundle];
NSString* path = [mainBundle pathForResource: #"category-analysis-help" ofType: #"rtf"];
NSAttributedString* text = [[NSAttributedString alloc] initWithPath: path documentAttributes: NULL];
[helpText setAttributedStringValue: text];
NSRect bounds = [text boundingRectWithSize: NSMakeSize(helpText.bounds.size.width, 0) options: NSStringDrawingUsesLineFragmentOrigin];
helpContentView.frame = NSMakeRect(0, 0, helpText.bounds.size.width + 20, bounds.size.height + 20);
helpContentView is just a container for helpText to add some marging around the text. helpText resizes with its container.
It should be obvious that for the correct height a fixed width is necessary, since the height depends on what fits on the lines.
If you want to omit the scroll view entirely (e.g., make a text view that is attached to another superview and sizes itself to fit its text), you might take a look at NSText. It is, AFAICT, basically a NSTextView without the superview (scroll view parts), and can automagically resize itself.