How to find the position of the last text line in a multiline UILabel or otherwise have UILabel have 0 padding - ios7

I have a UILabel that has both -numberOfLines set to 3 and text-size auto shrink and I need to align another UIView to this UILabel's last line of text. That is, I might need to align to the y position of line 0, 1 or 2, depending on the text inside the label (and the distance between these lines of text may vary depending on whether the text is long enough that it triggered font resizing).
But:
UILabel doesn't expose a contentSize
the label's bounds extend past the last line of text (there seems to be a content inset), so aligning to the bounds won't work.
subclassing UILabel and doing something like this:
- (void)drawTextInRect:(CGRect)rect {
UIEdgeInsets insets = {0., 0., -30., 0.};
return [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}
just happens to work for the case where I have 3 lines and the font size was auto shrunk, but I still can'r figure out a generic way of subtracting insets for the general case, regardless of text size. And I don't seem to be able to use -boundingRectWithSize:options:context: either: it either returns a single line equivalent rect or, If I play around with the options, a a rect the same size of the original label (that is, including the extra insets I'm trying to get rid of). Mind you, the idea behind removing any insets is that if I have no way of knowing where the last line of text is, at least I can remove any insets in the label so that the last line of text aligns with the label's bounds.origin.y + bounds.size.height.
Any thoughts?

I don't know if the problem was that originally I was using boundingRectWithSize on non-attributed text or what but now this seems to work:
NSString *text = <get text from CoreData>;
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:#{NSFontAttributeName: self.titleLabel.font}];
CGRect rect = [attributedText boundingRectWithSize:self.titleLabel.frame.size
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
if (!rect.size.height || rect.size.height > self.titleLabel.frame.size.height) {
attributedText = [[NSAttributedString alloc] initWithString:text attributes:#{NSFontAttributeName: [UIFont boldSystemFontOfSize:self.titleLabel.font.pointSize * self.titleLabel.minimumScaleFactor]}];
rect = [attributedText boundingRectWithSize:self.titleLabel.frame.size
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
}
self.titleLabel.frame = rect;
self.titleLabel.attributedText = attributedText;
While this doesn't really find the position of the bottom of the last line of text in the UILabel (the label still adds some padding at the bottom... not sure if to account for descenders), it adjusts the label's bounds close enough to the bottom that I can at least align based on bounds.origin.y + bounds.size.height and it looks good enough.

Related

NSTextView and determining glyph position

I am trying to define y axis position for certain lines and have label next to them in an NSTextView. I am using glyphRangeForCharacterRange.
It kind of works, but the text view can have a LOT of text (even ~4000-5000 lines) and is wrapped with insets. As I scroll to stuff that is out of view, the results are wrong. It seems to return values without wrapping, or so I figure. If I do an update on the positions while I have scrolled to corresponding text, they are calculated correctly.
This is the code without additional offsets:
NSRange characterRange = NSMakeRange(startPosition, length);
NSRange range = [[self.textView layoutManager] glyphRangeForCharacterRange:characterRange actualCharacterRange:nil];
label.frame = rect;
NSRect rect = [[self.textView layoutManager] boundingRectForGlyphRange:range inTextContainer:[self.textView textContainer]];
The same problem occurs if I try to set the label positions after the document awakes, so I calculate the labels on first keypress, which isn't too nice.
What would be the best way around this?
EDIT
The answer to this problem was just to ensure the layout for the NSLayoutManager before retrieving glyph coordinates.
[[_textView layoutManager] ensureLayoutForTextContainer:[_textView textContainer]];

Can we know the space between text and the top of the UILabel?

With UILabels there seems to be a permanent border of emptiness around the text. Trying things like
label.layoutMargins = UIEdgeInsetsZero;
doesn't do anything and the gap between the top of the text and the top of the label seems to be proportionate to the font size.
Is there a way to make the gap go away? Or is there a way to know how big the gap is?
This should give you the gap value:
CGSize labelSize=label.frame.size;
CGSize textSize = [label.text sizeWithFont:label.font constrainedToSize:CGSizeMake(labelSize.width, MAXFLOAT) lineBreakMode:UILineBreakModeWordWrap];
// Assuming that the text is centered inside the label
CGFloat topGap = (labelSize.height-textSize.height)/2

Calculate UILabel height gives wrong values

I am trying to "forecast" what the size of a label is going to be (width is known, only height).
I am trying to use this:
CGSize possibleSize = [text sizeWithFont:[UIFont fontWithName:#"American Typewriter" size:16]
constrainedToSize:CGSizeMake(self.collectionView.frame.size.width ,9999)
lineBreakMode:NSLineBreakByWordWrapping];
Which gives very inaccurate results (even changing the fonts and the size won't make it better, for example I get the height 30 instead of 80).
I have read that other people also don't get good results with it. Am I using it right?
I have also tried:
UILabel *test=[[UILabel alloc] initWithFrame:self.collectionView.frame];
test.text=[dic objectForKey:#"text"];
test.font=[UIFont fontWithName:#"American Typewriter" size:12];
[test sizeToFit];
NSLog(#"%f",test.frame.size.height);
I have to know what the height is going to be, and this method is not even close.
Is there some other way that gives reasonable results?
This sizeWithFont method is now deprecated, this new method works best
NSString *content = **Whatever your label's content is expected to be**
CGSize maximumLabelSize = CGSizeMake(self.label.frame.size.width, 9999);
NSDictionary *stringAttributes = [NSDictionary dictionaryWithObject:[UIFont fontWithName:#"American Typewriter" size:16] forKey: NSFontAttributeName];
CGSize expectedLabelSize = [content boundingRectWithSize:maximumLabelSize options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin attributes:stringAttributes context:nil].size;
CGFloat labelHeight = expectedLabelSize.height;
where labelHeight is the height that the label will be calculated from the amount of text due to load into the label.
I hope this helps, cheers, Jim.
#matt you are onto something, but I'll add that you should set the number of lines on that label to 0, before you calculate sizeWithFont.
You may also try to replace
CGSizeMake(self.collectionView.frame.size.width ,9999)
with
CGSizeMake(self.collectionView.bounds.size.width ,FLT_MAX)
the key element being "bounds" instead of frame.
Lastly, ensure you're not getting nothing for [dic objectForKey:#"text"].
Use
NSAssert(dic[#"text"]);
if ([dic[#"text"] isEqualToString:""]) {
; //empty string
}
First of all you need to allow a UILabel to be multiline, by default it is one line (set number of line to 0)
yourLabel.numberOfLines = 0; // means - label can be multiline
Secondly, it looks like you are calculating size for a bigger font that the label has in fact. Consider using the same size so set the calculations correctly.
Moreover if you support only iOS 7 and newer consider usage of sizeWithAttributes method introduced in iOS 7 or boundingRectWithSize:options:attributes:context - replacement for the method you use from iOS 7 and further for text size calculations.
Finally (advice) if you need that height value only for setting height of the label maybe you should consider using auto layout (it will be much easier to deal with).

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

How to fit a text with various length in a UITableViewCell?

What I have is:
a NSString which can have any length between 1 and 400 characters
a UITableViewCell (custom layout)
I tried using an UILabel with multiple lines, set the text, and call sizeToFit. That doesn't work always, most of the time the UILabel just clips off the part of the string that doesn't fit. Also, due the varying length of the text I'd need differently sized UITableViewCells, and at the time "tableView: cellForRowAtIndexPath:" is called I don't know what the height will be.
So what I need is a non-scrolling UI element which is able to display text and resizes its height (the width should remain constant) to exactly fit the text. As mentioned the sizeToFit method produces mostly garbage.
You can use SizeWithFont: to calculate the desired height for your cell and store it in an Array so that you can return that height in HeightForRowAtIndexPath. If you need to update the text, just have a method that re-calculates the height, saves it to the array, and updates the table. Something like:
CGSize constraintSize;
constraintSize.width = 290.0f;
constraintSize.height = MAXFLOAT;
NSString *text = #"YOUR TEXT"
CGSize theSize = [text sizeWithFont:[UIFont systemFontOfSize:15.0f] constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
NSLog(#"height: %f",theSize.height);
will give you the height.
This configuration should give you something simillar to what you see when you enter a loooong number in the phone app -
label.minimumFontSize = 4; //a very small font size
label.adjustsFontSizeToFitWidth = YES;
label.lineBreakMode = UILineBreakModeWordWrap;// change to what works for you
label.numberOfLines = 0;
See lineBreakMode Documentation