How to determine the number of lines being used in UILabel (Swift)? - swift

cell.bodyText.numberOfLines = 0
cell.bodyText.text = newsBody
cell.bodyText.sizeToFit()
I set the number of lines to 0 because the number of lines can vary (I am retrieving news articles and since every news article is obviously different in length, numberOfLines is 0)
If I do not use sizeToFit() after assinging newsBody to the label, and check the label height (cell.bodyText.frame.size.height), I get really large numbers (when I tested it, an article that had 25 lines of text, the height was apparently 4000). If I use sizeToFitand then check the height, I get 21 no matter how short or long the label is. (21 is the height I set the UILabel in the storyboard).
How can I get an accurate representation of how many lines are in the UILabel or even just the height of the label?

I assume you're doing this work in tableView(_:cellForRowAtIndexPath:). That's not reliable for querying final layout. Try using the delegate method tableView(_:willDisplayCell:forRowAtIndexPath:). That's called immediately before displaying the cell, and is the appropriate place to work out any final layout issues since everything has been applied at that point.

Related

UILabel Minimum Number Of Lines

If you take a close look an an iMessage conversation cell, you’ll notice that the preview text is always two lines long. This can’t be a hard coded row height because the rows adjust to dynamic type. How can you always force a label to take up a certain number of lines even if there isn’t enough text to do so?
Set the label's numberOfLines to 2 and end the label's text with a linefeed \n. Set the wrapping to word wrap to prevent the ellipses from appearing.
This guarantees that the label text consists of at least 2 lines worth of material. Thus it can never be less than 2 lines, and since the maximum number of lines is 2, it can never be more than 2 lines. Thus it will always be (wait for it) 2 lines.
One way I think this could be possible is having a UIView as a parent of the UILabel.
Fix the height of the UIView based on device size class. Let's say for example 50 points for Width = Compact and Height = Regular.
Embed UILabel in UIView
Set number of Lines = 0 for UILabel
Now match UILabel leading , trailing ,top edge with the superview and leave the height as it is , also don't set the bottom constraints.
Select the superview i.e the UIView and UILabel together and select Equal Heights.
Open the constraint window and change label height less than or equal to SuperView height.
Short Message
Long Message
Height Constraint of the UILabel with respect to SuperView.

How to Calculate Text Size Prior to Creating a CustomCell

I'm creating a CustomCell that contains a UILabel, by default the UILabel will have two lines of text with wrap around enabled, but there are occasions when the text will require three lines.
The font type and size is fixed and cannot be changed, and I trying to identify a way of calculating the length of the NSString/UILabel prior to creating the UITableView/CustomCell so that the cell height can be set correctly. The text that will be displayed will be made up of a number of different words e.g. 'Your name is XXXXX XXXXX and your birthday is..' and the XXXXXXX is the element that is variable.
Hopefully this makes sense, one idea I have considered is creating a method that contains a UILabel that is never displayed and populating it with the required text and then checking if 2 or 3 lines are used, but not sure how to do this.
Is there a more elegant method of achieving this?
There are several UIKit functions for calculating with size of a string with various linebreak modes and fonts. See this doc for details.
In particular sizeWithFont:constrainedToSize:lineBreakMode: may be useful for this.
I believe NSStrings sizeWithFont: constrainedToSize: lineBreakMode: is the method you are looking for. This returns a CGSize which you can get the height of to determine your cell's height (and set the label's frame to the correct size obviously).
Note: you should pass a CGSize parameter in to the constrainedToSize: part that is a larger height than what you intend it to be but the correct maximum width of the label

How to use NSString's sizeWithFont and drawInRect to workout how much of a string to draw

I'm drawing multiple 'pages' of imagery using a CGContext in the iOS. I've used sizeWithFont and drawInRect combinations extensively in my app. What I need to do is split a large chunk of text across multiple pages. I can size it and work out whether or not it needs another page but how do I know where to chop it? Do I have to do an ugly loop to check word by word until I find a string length that perfectly fits the page and then chop the string at that point? Is there a smarter way?
Any ideas?
Thanks.
The NSString additions to UIKit for drawing text, you can pre-determine the exact amount of space required to render a given text for a given font. If splitting the text into pages, you could use this method.
– sizeWithFont:constrainedToSize:lineBreakMode:
Assuming the font and line break mode is known, create a CGSize having the same width as your page, and use a sufficiently number for height. This would be the maximum size that we are constraining the text into.
CGSize maximumSize = CGSizeMake(pageWidth, 999999999);
CGSize expectedSize = [veryLongString sizeWithFont:theFont constrainedToSize:maximumSize lineBreakMode:theLineBreakMode];
expectedSize will tell us the size that the text will take assuming if it could extend vertically infinitely (well close to). To find the number of pages required, just divide the total height by the height of one page.
NSInteger totalPages = ceil(expectedSize.height / heightOfOnePage);
You would also want to adjust the height of one page to make sure that the last line of text doesn't get clipped. For that to happen, the height of the page should be a multiple of the font's line height. Say the initial page height is 300px, and the font-height is 16px, then there will be some clipping as 300/16 = 18.75 which is not a whole number.
NSInteger linesWithoutClipping = floor(initialPageHeight / theFont.lineHeight);
CGFloat optimalPageHeight = linesWithoutClipping * theFont.lineHeight;
Taking the floor value 18 and multiplying with the font line height 16, we get an optimal page height of 288 to ensure there's no clipping.
Note that lineHeight was introduced in iOS 4.0, but you could calculate it yourselves if needed for older versions.
The way I get around this problem is to split by line returns.
NSArray * paragraphs = [text componentsSeparatedByString:#"\n"];
You still have to do all the work to determine page breaks and alike but I have found this the best workaround so far.

Multiple UILabel(s) repositioning according to wordwrap

I have this interface with multiple UILabels.
On view loading i populate white labelled values with some data from a db.
The problem is, some of that fields are potentially too long for the interface, so i'd like to compute the total height of one label once the text is word wrapped and reposition the 2 labels below (shifting the Y coordinate) accordingly to the previous label's height.
All of this should go inside a UIScrollView to let the user scroll those labels vertically.
Any chance i can do this easily with some control i still don't know, or do i have to do it manually?
Thanks
You'll need to use the NSString UIKit Additions to compute the height you need to set on your UILabel, and then adjust the other controls appropriately.
Specifically, I think you want to use sizeWithFont:forWidth:lineBreakMode: to get the rect for your UILabel.
Alternatively, you could use a UIWebView and display the information as HTML. I don't know if it's necessarily less work, but you'll get a layout that automatically adjusts to the size of its contents.

Creating a fixed formatted cell in UITableview

I want to have a tableview create rows that look like this:
value1 item1 container1
value10 item10 container10
value100 item100 container100
value2 item2 container2
What I am trying to show is that the first word (value) will have a set length of 12 and then the second word (item) will have a set length of 10 and then the last word (container) is just tagged on at the end.
I am pulling these from a SQLite database and don't want to use multiple lines, but read in a strictly formatted structure like this.
You can layout a custom UITableViewCell in Interface Builder, where you drag two UILabel views onto the Content View and set their size appropriately (Notice that the letters may vary in width, so even though you know it's 10 chars in length, you don't know the maximum width, please keep that in mind)
Then you just fill the open space at the right of a cell with another UILabel, layout it to cover the open space and set it to autoresize it's width and set the right margin to be fixed.
There are quite a few tutorials available on how to use the custom cell in your tableView, I can recommend you this screencast. It explains how you can initialize the custom cell and how you can access the custom labels.
It sounds like you want something like an old-fashion text display in which then nth character in row zero always lines up the nth character in every row.
Even using carefully positioned labels in a custom tableviewcell, you will have to strictly control the specific font and its size if you want all the characters to line up in fixed width column. You will need a fixed width font to begin with and you will have to set the size precisely.
You might want to consider whether this is necessary. iPhone users are used to propionate width text displays. Very precise columns of text might make it difficult to discern rows. I would test first with just a simple table before spending the time tweaking the columns.