Dynamic Row Height and sizeWithAttributes Warning - ios

OK, im using the sizeWithFont: constrainedToSize capability, and I'm getting a compiler warning. I've read that I need to use sizeWithAttributes instead (link here: Replacement for deprecated sizeWithFont: in iOS 7?).
I'm having a very hard time understanding how to implement sizeWithAttributes dynamically, so that the height of my cell row changes to the content height (please see below).
Any ideas on how to get rid of this warning? Thanks!
int topPadding = cell.menuItemTitle.frame.origin.x;
int bottomPadding = cell.frame.size.height-
(topPadding+cell.menuItemTitle.frame.size.height);
NSString *text = [[menuItems objectAtIndex:indexPath.row] valueForKey:#"title"];
CGSize maximumSize = CGSizeMake(cell.menuItemTitle.frame.size.width, 9999);
CGSize expectedSize = [text sizeWithFont:cell.menuItemTitle.font
constrainedToSize:maximumSize lineBreakMode:cell.menuItemTitle.lineBreakMode];
return topPadding+expectedSize.height+bottomPadding;

Your topPadding should be origin.y instead of origin.x. You can use the following to get the size of the text.
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
NSDictionary *attrDict = [NSDictionary dictionaryWithObjectsAndKeys:label.font,
NSFontAttributeName,
paragraphStyle,
NSParagraphStyleAttributeName,
nil];
CGRect expectedRect = [text boundingRectWithSize:maximumSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrDict context:nil];
CGSize expectedSize = expectedRect.size;

Related

Setting dynamic UILabel Height

I have an app with a table view that needs to support dynamic cell heights. In the cell's layoutSubviews method, I am generating a frame for my UILabel controls, which are the only dynamic sized controls in the cells.
For some reason, the width being returned from the following method is less than what it should be and the text gets truncated, but only on short text, such as one word. The width should be maintained as the width that is passed in as the initial frame.
That said, what my method needs to accomplish is adjusting the size of the label to fit all the text while maintaining a preset width.
Here's the code I am using:
- (CGRect)getLabelSizeForText:(NSString*)text withInitialRect:(CGRect)labelFrame andFontSize:(CGFloat)fontSize{
CGSize constrainedSize = CGSizeMake(labelFrame.size.width, MAXFLOAT);
NSDictionary *attributesDict = [NSDictionary dictionaryWithObjectsAndKeys:[UIFont systemFontOfSize:fontSize], NSFontAttributeName, nil];
CGSize requiredSize = [text boundingRectWithSize:constrainedSize
options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading
attributes:attributesDict context:nil].size;
CGRect adjustedFrameRect = CGRectMake(labelFrame.origin.x, labelFrame.origin.y, requiredSize.width, requiredSize.height);
return adjustedFrameRect;
}
This works for me,
+ (CGSize)textSizeForText:(NSString *)text {
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat width = screenWidth - kPaddingRight;
CGSize maxSize = CGSizeMake(width, CGFLOAT_MAX);
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = 2.5;
NSDictionary *dict = #{NSParagraphStyleAttributeName : paragraphStyle, NSFontAttributeName: [UIFont systemFontOfSize:15] };
[attributedString addAttributes:dict range:NSMakeRange(0, text.length)];
CGRect paragraphRect = [attributedString boundingRectWithSize:maxSize options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];
return paragraphRect.size;
}

Lost with the replacement for deprecated sizeWithFont: in iOS 7

I have a warning that sizeWithFont: is deprecated, i have try to replace it to sizeWithAttributes: but everything i try is not working.
The code is supposed to tell me the expected size of a UILabel and the cCell is the cell and the label fro the IB.
Thanks for all your help.
CGSize maximumLabelSize = CGSizeMake(210, FLT_MAX);
expectedLabelSize = [labelText sizeWithFont:cCell.lblHotelResponse.font constrainedToSize:maximumLabelSize lineBreakMode:cCell.lblHotelResponse.lineBreakMode];
You need to use sizeWithAttributes: instead.
NSDictionary *attributeDict = #{NSFontAttributeName:cCell.lblHotelResponse.font};
CGSize expectedLabelSize = [labelText sizeWithAttributes:attributeDict];
Another way is:
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:labelText attributes:#{NSFontAttributeName: cCell.lblHotelResponse.font}];
CGRect rect = [attributedString boundingRectWithSize:maximumLabelSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];
CGSize labelSize = rect.size;
References:
sizeWithAttributes:
boundingRectWithSize:options:attributes:

Contenting deprecated sizeWithFont:constrainedToSize to boundingRectWithSize:options:attributes:context:

How can I convert
CGSize labelHeighSize = [text sizeWithFont: [UIFont systemFontOfSize:16] constrainedToSize:maximumSize lineBreakMode:NSLineBreakByTruncatingTail];
to
CGSize labelHeighSize = [text boundingRectWithSize:maximumSize options: attributes: context:
First of all the method:
- (CGRect) boundingRectWithSize:(CGSize)size
options:(NSStringDrawingOptions)options
attributes:(NSDictionary *)attributes
context:(NSStringDrawingContext *)context;
returns CGRect not the CGSize so you need to use CGRect.
EDIT
according to apple docs see here, it says
This option is ignored if NSStringDrawingUsesLineFragmentOrigin is not
also set. In addition, the line break mode must be either
NSLineBreakByWordWrapping or NSLineBreakByCharWrapping for this option
to take effect. The line break mode can be specified in a paragraph
style passed in the attributes dictionary argument of the drawing
methods.
Below is the sample code that you can use:
NSString *text = #"Some text to measure";
UIFont *labelFont = [UIFont systemFontOfSize:16];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc]init];
//set the line break mode
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
NSDictionary *attrDict = [NSDictionary dictionaryWithObjectsAndKeys:labelFont,
NSFontAttributeName,
paragraphStyle,
NSParagraphStyleAttributeName,
nil];
//assume your maximumSize contains {255, MAXFLOAT}
CGRect lblRect = [text boundingRectWithSize:(CGSize){225, MAXFLOAT}
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attrDict
context:nil];
CGSize labelHeighSize = lblRect.size;

'sizeWithFont:constrainedToSize:lineBreakMode:'is deprecated:

Converting a project from iOS5.0 to iOS7 / iOS6 on Xcode 5. The code below is giving a compile time warning:
'sizeWithFont:constrainedToSize:lineBreakMode:'is deprecated: first deprecated in ios 7.0 - Use - boundingRectWithSize:options:attribiutes:context
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
self.lblHidden.frame = CGRectMake(58, 228, 945, 9999);
self.lblHidden.text = detailShareObj.pDesc;
CGSize size = [detailShareObj.pDesc sizeWithFont:self.lblHidden.font constrainedToSize:self.lblHidden.frame.size lineBreakMode:NSLineBreakByWordWrapping];
return 228.0+size.height+20;
}
else if (indexPath.section == 1)
{
NSString *tempPointStr = (self.shortDescArray)[indexPath.row];
self.lblHidden.frame = CGRectMake(58, 0, 945, 9999);
self.lblHidden.text = tempPointStr;
CGSize size = [tempPointStr sizeWithFont:self.lblHidden.font
constrainedToSize:self.lblHidden.frame.size
lineBreakMode:NSLineBreakByWordWrapping];
return 50.0f;
}
I tried some of the suggestion give elsewhere but nothing is up to rescue if some one can help by giving the corrections required in the code will be greatly appreciated.
I wouldn't just mask the deprecated function warning. They deprecated it for a reason. I believe the function was deprecated because that series of NSString+UIKit functions were based on the UIStringDrawing library, which wasn't thread safe. If you tried to run them not on the main thread (like any other UIKit functionality), you'll get unpredictable behaviors. In particular, if you ran the function on multiple threads simultaneously, it'll probably crash your app. This is why in iOS 6, they introduced a the boundingRectWithSize:... method for NSAttributedStrings. This was built on top of the NSStringDrawing libraries and is thread safe.
If you look at the new NSString boundingRectWithSize:... function, it asks for an attributes array in the same manner as a NSAttributeString. If I had to guess, this new NSString function in iOS 7 is merely a wrapper for the NSAttributeString function from iOS 6.
On that note, if you were only supporting iOS 6 and iOS 7, then I would definitely change all of your NSString's sizeWithFont:... to the NSAttributeString's boundingRectWithSize. It'll save you a lot of headache if you happen to have a weird multi-threading corner case! Here's how I converted NSString's sizeWithFont:constrainedToSize::
What used to be:
NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
CGSize size = [text sizeWithFont:font
constrainedToSize:(CGSize){width, CGFLOAT_MAX}];
Can be replaced with:
NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
NSAttributedString *attributedText =
[[NSAttributedString alloc]
initWithString:text
attributes:#
{
NSFontAttributeName: font
}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
CGSize size = rect.size;
Please note the documentation mentions:
In iOS 7 and later, this method returns fractional sizes (in the size
component of the returned CGRect); to use a returned size to size
views, you must use raise its value to the nearest higher integer
using the ceil function.
So to pull out the calculated height or width to be used for sizing views, I would use:
CGFloat height = ceilf(size.height);
CGFloat width = ceilf(size.width);
If you want it compatible with both iOS7 and the versions below it, try this one (with ARC):
CGSize size;
if ([tempPointStr respondsToSelector:
#selector(boundingRectWithSize:options:attributes:context:)])
{
NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
paragraphStyle.alignment = NSTextAlignmentLeft;
NSDictionary * attributes = #{NSFontAttributeName : self.lblHidden.font,
NSParagraphStyleAttributeName : paragraphStyle};
size = [tempPointStr boundingRectWithSize:self.lblHidden.frame.size
options:NSStringDrawingUsesFontLeading
|NSStringDrawingUsesLineFragmentOrigin
attributes:attributes
context:nil].size;
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
size = [tempPointStr sizeWithFont:self.lblHidden.font
constrainedToSize:self.lblHidden.frame.size
lineBreakMode:NSLineBreakByWordWrapping];
#pragma clang diagnostic pop
}
Note: It's just an example for your else-if case, maybe you need to do some modification depend on what you want it be. ;)
For iOS7, replace:
CGSize size = [tempPointStr sizeWithFont:self.lblHidden.font
constrainedToSize:self.lblHidden.frame.size
lineBreakMode:NSLineBreakByWordWrapping];
With:
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc]init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; //set the line break mode
NSDictionary *attrDict = [NSDictionary dictionaryWithObjectsAndKeys:self.lblHidden.font, NSFontAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
CGSize size = [tempPointStr boundingRectWithSize:self.lblHidden.frame.size
options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin
attributes:attrDict context:nil].size;
You can use:
UIFont *font = [UIFont boldSystemFontOfSize:16];
CGRect new = [string boundingRectWithSize:CGSizeMake(200, 300)
options:NSStringDrawingUsesFontLeading
attributes:#{NSFontAttributeName: font}
context:nil];
CGSize stringSize= new.size;
If you're targeting iOS 6.0+, you can still use sizeWithFont:constrainedToSize:lineBreakMode:. Just make sure that your project's iOS Deployment Target is set for 6.0, and the compiler won't give you these warnings.
(You can find this by clicking on the blue project tab (usually at the top of the left, project navigator pane) within the "info" section).
If you're only targeting iOS 7.0+, you should use the new method boundingRectWithSize:options:attributes:context.
You can find the Apple docs on this new method here.
The boundingRectWithSize:options:attributes:context has the problem, that it does not calculates the height correctly if the String contains "\n" (line breaks). Therefore this code calculates the size for each line separately for a given width (inWidth):
NSArray *brokenByLines=[string componentsSeparatedByString:#"\n"];
CGFloat height=0.0;
CGFloat maxWidth=0.0;
for (NSString* actString in brokenByLines) {
CGRect tSize=[actString boundingRectWithSize:CGSizeMake(inWidth, 600) options:(NSStringDrawingUsesLineFragmentOrigin | NSLineBreakByWordWrapping) attributes:#{NSFontAttributeName: inFont} context:nil];
if (maxWidth<tSize.size.width) {
maxWidth=tSize.size.width;
}
height+=tSize.size.height;
}
CGSize size= CGSizeMake(ceil(maxWidth), ceil(height));

Replacement for deprecated -sizeWithFont:constrainedToSize:lineBreakMode: in iOS 7?

In iOS 7, the method:
- (CGSize)sizeWithFont:(UIFont *)font
constrainedToSize:(CGSize)size
lineBreakMode:(NSLineBreakMode)lineBreakMode
and the method:
- (CGSize)sizeWithFont:(UIFont *)font
are deprecated. How can I replace
CGSize size = [string sizeWithFont:font
constrainedToSize:constrainSize
lineBreakMode:NSLineBreakByWordWrapping];
and:
CGSize size = [string sizeWithFont:font];
You could try this:
CGRect textRect = [text boundingRectWithSize:size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:FONT}
context:nil];
CGSize size = textRect.size;
Just change "FONT" for an "[UIFont font....]"
As we cant use sizeWithAttributes for all iOS greater than 4.3 we have to write conditional code for 7.0 and previous iOS.
1) Solution 1:
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")) {
CGSize size = CGSizeMake(230,9999);
CGRect textRect = [specialityObj.name
boundingRectWithSize:size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:[UIFont fontWithName:[AppHandlers zHandler].fontName size:14]}
context:nil];
total_height = total_height + textRect.size.height;
}
else {
CGSize maximumLabelSize = CGSizeMake(230,9999);
expectedLabelSize = [specialityObj.name sizeWithFont:[UIFont fontWithName:[AppHandlers zHandler].fontName size:14] constrainedToSize:maximumLabelSize lineBreakMode:UILineBreakModeWordWrap]; //iOS 6 and previous.
total_height = total_height + expectedLabelSize.height;
}
2) Solution 2
UILabel *gettingSizeLabel = [[UILabel alloc] init];
gettingSizeLabel.font = [UIFont fontWithName:[AppHandlers zHandler].fontName size:16]; // Your Font-style whatever you want to use.
gettingSizeLabel.text = #"YOUR TEXT HERE";
gettingSizeLabel.numberOfLines = 0;
CGSize maximumLabelSize = CGSizeMake(310, 9999); // this width will be as per your requirement
CGSize expectedSize = [gettingSizeLabel sizeThatFits:maximumLabelSize];
The first solution is sometime fail to return proper value of height. so use another solution. which will work perfectly.
The second option is quite well and working smoothly in all iOS without conditional code.
Here is simple solution :
Requirements :
CGSize maximumSize = CGSizeMake(widthHere, MAXFLOAT);
UIFont *font = [UIFont systemFontOfSize:sizeHere];
Now As constrainedToSizeusage:lineBreakMode: usage is deprecated in iOS 7.0:
CGSize expectedSize = [stringHere sizeWithFont:font constrainedToSize:maximumSize lineBreakMode:NSLineBreakByWordWrapping];
Now usage in greater version of iOS 7.0 will be:
// Let's make an NSAttributedString first
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:stringHere];
//Add LineBreakMode
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
[paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping];
[attributedString setAttributes:#{NSParagraphStyleAttributeName:paragraphStyle} range:NSMakeRange(0, attributedString.length)];
// Add Font
[attributedString setAttributes:#{NSFontAttributeName:font} range:NSMakeRange(0, attributedString.length)];
//Now let's make the Bounding Rect
CGSize expectedSize = [attributedString boundingRectWithSize:maximumSize options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;
Below are two simple methods that will replace these two deprecated methods.
And here are the relevant references:
If you are using NSLineBreakByWordWrapping, you don't need to specify the NSParagraphStyle, as that is the default:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSParagraphStyle_Class/index.html#//apple_ref/occ/clm/NSParagraphStyle/defaultParagraphStyle
You must get the ceil of the size, to match the deprecated methods' results.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/NSString_UIKit_Additions/#//apple_ref/occ/instm/NSString/boundingRectWithSize:options:attributes:context:
+ (CGSize)text:(NSString*)text sizeWithFont:(UIFont*)font {
CGSize size = [text sizeWithAttributes:#{NSFontAttributeName: font}];
return CGSizeMake(ceilf(size.width), ceilf(size.height));
}
+ (CGSize)text:(NSString*)text sizeWithFont:(UIFont*)font constrainedToSize:(CGSize)size{
CGRect textRect = [text boundingRectWithSize:size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName: font}
context:nil];
return CGSizeMake(ceilf(textRect.size.width), ceilf(textRect.size.height));
}
In most cases I used the method sizeWithFont:constrainedToSize:lineBreakMode: to estimate the minimum size for a UILabel to accomodate its text (especially when the label has to be placed inside a UITableViewCell)...
...If this is exactly your situation you can simpy use the method:
CGSize size = [myLabel textRectForBounds:myLabel.frame limitedToNumberOfLines:mylabel.numberOfLines].size;
Hope this might help.
UIFont *font = [UIFont boldSystemFontOfSize:16];
CGRect new = [string boundingRectWithSize:CGSizeMake(200, 300) options:NSStringDrawingUsesFontLeading attributes:#{NSFontAttributeName: font} context:nil];
CGSize stringSize= new.size;
[Accepted answer works nicely in a category. I'm overwriting the deprecated method names. Is this a good idea? Seems to work with no complaints in Xcode 6.x]
This works if your Deployment Target is 7.0 or greater. Category is NSString (Util)
NSString+Util.h
- (CGSize)sizeWithFont:(UIFont *) font;
- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size;
NSString+Util.m
- (CGSize)sizeWithFont:(UIFont *) font {
NSDictionary *fontAsAttributes = #{NSFontAttributeName:font};
return [self sizeWithAttributes:fontAsAttributes];
}
- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size {
NSDictionary *fontAsAttributes = #{NSFontAttributeName:font};
CGRect retVal = [self boundingRectWithSize:size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:fontAsAttributes context:nil];
return retVal.size;
}
UIFont *font = [UIFont fontWithName:#"Courier" size:16.0f];
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
paragraphStyle.alignment = NSTextAlignmentRight;
NSDictionary *attributes = #{ NSFontAttributeName: font,
NSParagraphStyleAttributeName: paragraphStyle };
CGRect textRect = [text boundingRectWithSize:size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributes
context:nil];
CGSize size = textRect.size;
from two answers 1 + 2

Resources