I am getting a warning: "'sizeWithFont:constrainedToSize:lineBreakMode:' is deprecated: first deprecated in iOS 7.0"
Can anyone please suggest me an alternative for this method?
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Calculate height based on cell content — cell content will stretch appropriately when the height is set
Post *post = self.articleComments[indexPath.row];
CGFloat width = tableView.frame.size.width - 71 - 15; // Width of comment text area
UIFont *commentFont = [UIFont fontWithName:#"SeroCompPro-Light" size:14];
CGFloat commentTextHeight = [post.text sizeWithFont:commentFont constrainedToSize:CGSizeMake(width, 10000) lineBreakMode:NSLineBreakByWordWrapping].height;
return commentTextHeight + 31 + 37;
}
The alternative is:
- (NSSize)sizeWithAttributes:(NSDictionary *)attributes
In your case:
[string sizeWithAttributes:#{NSFontAttributeName:[UIFont fontwithName:#"SeroCompPro-Light" size:14]}];
This function is deprecated in ios 7.
Instead of this function
sizeWithFont:constrainedToSize:lineBreakMode
use this function, Use
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context
boundingRectWithSize:options:attributes:context:
Calculates and returns the bounding rect for the receiver drawn using the given options and display characteristics, within the specified rectangle in the current graphics context.
Parameters
size
The size of the rectangle to draw in.
options
String drawing options.
attributes
A dictionary of text attributes to be applied to the string. These are the same attributes that can be applied to an NSAttributedString object, but in the case of NSString objects, the attributes apply to the entire string, rather than ranges within the string.
context
The string drawing context to use for the receiver, specifying minimum scale factor and tracking adjustments.
I used bellow code and it works:
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:#
{
NSFontAttributeName: font
}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){size.width, CGFLOAT_MAX}
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
result = CGSizeMake(ceilf(rect.size.width), ceilf(rect.size.height));
Problem at creating a NSAttributedString and get properly width height with ceilf method
Alternate solution for supporting IOS7 and lower version-
CGSize expectedLabelSize;
if ([self respondsToSelector:#selector(sizeWithAttributes:)])
{
expectedLabelSize = [subTitle sizeWithAttributes:#{NSFontAttributeName:subTitleLabel.font}];
}else{
expectedLabelSize = [subTitle sizeWithFont:subTitleLabel.font constrainedToSize:subTitleLabel.frame.size lineBreakMode:NSLineBreakByWordWrapping];
}
Related
I have a method that gives me the perfect size for a UITextView given a length of string (with the corresponding correct font size) :
- (NSInteger) heightOfLabel:(NSString*) string {
CGSize maximumLabelSize = CGSizeMake([[UIScreen mainScreen] bounds].size.width - 40, FLT_MAX);
CGSize expectedLabelSize = [[NSString stringTrimmedForLeadingAndTrailingWhiteSpacesFromString:string]
sizeWithFont:[UIFont systemFontOfSize:15]
constrainedToSize:maximumLabelSize
lineBreakMode:NSLineBreakByWordWrapping];
return expectedLabelSize.height + 5;
}
In fact, it still gives me a perfect fit, even in iOS7. Although now it comes up with a warning method that says I shouldn't use 'sizeWithFont:contrainedToSize:lineBreakMode'.
It now says I should be using -boundingRectWithSize:options:attributes:context:
This method isn't new to iOS7 and therefore i figure that it is okay to ask it on stack overflow, rather than going across to the official apple developers forum.
I have three questions:
1) Because it is deprecated, does that mean I should definitely replace it, despite it still working?
2) I have tried many different boundingRectWithSize: methods, with various variables but it is never perfect, it always seems to be slightly out (as many stackoverflow questions point out) - is there a perfect replacement with this none-deprecated method that does exactly the same as my previous method with as minimal hassle?
3) why remove this method? Is it because of the overlap with this other method?
After an hour of trial error I managed to make it work:
CGSize maximumLabelSize = CGSizeMake(tableView.width, MAXFLOAT);
NSStringDrawingOptions options = NSStringDrawingTruncatesLastVisibleLine |
NSStringDrawingUsesLineFragmentOrigin;
NSDictionary *attr = #{NSFontAttributeName: [UIFont systemFontOfSize:15]};
CGRect labelBounds = [string boundingRectWithSize:maximumLabelSize
options:options
attributes:attr
context:nil];
Update:
As Mr. T mentions in answer below : 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. ceilf function is recommended to use.
CGFloat height = ceilf(labelBounds.size.height);
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:CGSizeMake(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:CGSizeMake(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);
For linebreak issue:
- (CGFloat)heightNeededForText:(NSString *)text withFont:(UIFont *)font width:(CGFloat)width lineBreakMode:(NSLineBreakMode)lineBreakMode {
NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = lineBreakMode;
CGSize size = [text boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:#{ NSFontAttributeName: font, NSParagraphStyleAttributeName: paragraphStyle }
context:nil].size;
return ceilf(size.height);
}
Swift version of the Alexander of Norway's answer...
func heightNeededForText(text: NSString, withFont font: UIFont, width: CGFloat, lineBreakMode:NSLineBreakMode) -> CGFloat {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = lineBreakMode
let size: CGSize = text.boundingRectWithSize(CGSizeMake(width, CGFloat.max), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: [ NSFontAttributeName: font, NSParagraphStyleAttributeName: paragraphStyle], context: nil).size//text boundingRectWithSize:CGSizeMake(width, CGFLOAT_MA
return ceil(size.height);
}
In the code where you want to get the height just call the method like below...
let size = self.heightNeededForText(text as NSString, withFont: UIFont.systemFontOfSize(15.0), width: scrollView.frame.size.width - 20, lineBreakMode: NSLineBreakMode.ByWordWrapping) //Can edit the font size and LinebreakMode
I am developing an app, in which I am getting dynamic text and I have to increase height of label dynamically depending upon text and also increase and decrease height of table cell on basis on label height. So I wrote code both for iOS 6 and iOS7 and it works fine in iOS 6 but in iOS7 it not working fine. Below is code. Function that return height of text.
- (CGSize)getSizeOfText:(NSString *)text withFont:(UIFont *)font widthOftext:(int )txtWidth
{
CGSize boundingSize = CGSizeMake(txtWidth, 1000);
CGSize size;
if (MyDelegate.isIos7)
{
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
font, NSFontAttributeName,
nil];
CGRect frame = [text boundingRectWithSize:boundingSize
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:attributesDictionary
context:nil];
return frame.size;
}
else
{
CGSize requiredSize = [text sizeWithFont:font constrainedToSize:boundingSize lineBreakMode:UILineBreakModeWordWrap];
size=requiredSize;
}
return size;
}
There is bit change on return of height for iOS 6 and iOS 7.
Below First four label's text height difference for both them.
iOS6
42.000000
42.000000
21.000000
42.000000
iOS7
39.674072
39.674072
20.123291
39.674072
You can see bit difference of first four texts. But One major problem more in iOS is that It always show me text in just one line and truncate text even text height goes to 39 which may come in 2 lines, I also set number of lines to 20 and also tried to set number line to just 0 but didn't work.
Kindly guide me on this if anyone already this sort of problem. Thanks
Edited
I already posted this question earlier if anyone want to see screen shots of both iOS results so check there also.
Same Thread with Screen Shots
Try to set this property to your label
label.numberOfLines = 3;
it should solve the problem and let me know.
Set the height of the label to frame.size.height+1 or round it up.
and of course make sure the numberOfLines of your label is 3 (or 0 if you think it is possible to show more than 3).
Try in this way for dynamic change of height for cell using UITableView. This works for me for IOS7 and IOS6 also
At -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellText=yourtextstring;
UIFont *cellFont = [UIFont fontWithName:#"SourceSansPro-Bold" size:13.0];
CGSize constraintSize = CGSizeMake(300.0f, MAXFLOAT);//225
CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];
//NSLog(#"*** labelSize.height *** %f",labelSize.height);
return labelSize.height+250; //change this as per your need
}
At - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellText=yourtextstring;
UIFont *cellFont=[UIFont fontWithName:#"SourceSansPro-Regular" size:13.0];
CGSize constraintSize=CGSizeMake(300,MAXFLOAT);
CGSize txtViewSize=[cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];
CGRect frame=cell.yourlabel.frame;
// frame.size.width=300;
frame.size.height=txtViewSize.height+150;
cell.yourlabel.frame=frame;
cell.yourlabe.text=cellText;
return cell;
}
Hope it helps you..
NO need to compare for iOS 6 and iOS 7
use like below
CGFloat txtWidth = 250.0f;
NSString *font = #"Noteworthy-Bold"; //say u hav font
NSString *text = #"Some long text Some long text Some long text Some long text Some long text Some long text Some long text Some long text Some long text Some long text Some long text Some long text Some long text"; //say u hav some text
UIFont *labelFont = [UIFont fontWithName:font size:15];
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObject:labelFont forKey:NSFontAttributeName];
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:text attributes:attributesDictionary];
CGRect frame = [attributedText boundingRectWithSize:(CGSize){txtWidth, MAXFLOAT}
options: (NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
context:nil];//you need to specify the some width, height will be calculated
CGSize requiredSize = frame.size;
NSLog(#"wdth->%f height->%f",requiredSize.width,requiredSize.width);
//248.962509 height->248.962509 //iOS 7
//248.962494 height->248.962494 //iOS 6
//almost same
//also set 'numberOfLines` property of label
How can I make a simple design like in the image below where text length is dynamic? In the image below there are two sections Ingredients and Instructions with dynamic text length. There could be more sections.
Should I go for UIView with a UIScrollView or Table View? Any kind of help would be appreciated.
Try this approach.
1. Use UITextView this will give you possibility to scroll text inside UITextView if text will be very big.
2. Get the size of you text view
// return the size for UITextView for both IOS7 and IOS6
-(CGSize) getSizeWithMaxLabelSize:(CGSize) maxLabelSize forTextViewWithText:(NSString *) text
{
CGSize expectedLabelSize;
CGSize maximumLabelSize = maxLabelSize;
if (SYSTEM_VERSION_GREATER_THAN(#"6.2")) {
NSDictionary *stringAttributes = [NSDictionary dictionaryWithObject:[UIFont fontWithName:#"Arial" size:12] forKey: NSFontAttributeName];
CGRect rect =[text boundingRectWithSize:maximumLabelSize options:NSStringDrawingUsesLineFragmentOrigin attributes:stringAttributes context:nil];
expectedLabelSize = rect.size;
expectedLabelSize.height = ceilf(expectedLabelSize.height);
expectedLabelSize.width = ceilf(expectedLabelSize.width);
} else {
expectedLabelSize = [text sizeWithFont:[UIFont fontWithName:#"Arial" size:EPCommentViewFontSize] constrainedToSize:maximumLabelSize lineBreakMode:NSLineBreakByWordWrapping];
}
return expectedLabelSize;
}
3. Use this method (2) when you calculate cell height for you table
- tableView:cellForRowAtIndexPath:
You should go for UITextView/UILabel with UIScrollView. UILabel is preferable if the content is not editable and you do not need the text scrolling on the fixed area.
If your needs fulfilled by these, you need to go with UILabel with UIScrollView.
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));
I want to resize cell's height according to the label's height and label's height according to text. Or is there any way I can resize the cell's height according to the text entered in UITextView?
THIS METHOD IS DEPRECATED SINCE iOS 7.0.
There is a UITableView delegate method called heightForRowAtIndexPath that is called before you create a cell or a table.
You could use the NSIndexPath passed to it to get the text at a specific row and use the sizeWithFont method from UIStringDrawing.h to compute a CGSize for that row.
For example:
CGSize size = [text sizeWithFont:font
constrainedToSize:maximumLabelSize
lineBreakMode:UILineBreakModeWordWrap];
And finally you would return size.height.
--For iOS7--
Basing this off of Josh Vera's answer … place this in heightForRowAtIndexPath.
I have my table data stored in an NSArray *tableData.
// set the desired width of the textbox that will be holding the adjusted text, (for clarity only, set as property or ivar)
CGFloat widthOfMyTexbox = 250.0;
-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Get a reference to your string to base the cell size on.
NSString *cellText = [self.tableData objectAtIndex:indexPath.row];
//set the desired size of your textbox
CGSize constraint = CGSizeMake(widthOfMyTextBox, MAXFLOAT);
//set your text attribute dictionary
NSDictionary *attributes = [NSDictionary dictionaryWithObject:[UIFont systemFontOfSize:14.0] forKey:NSFontAttributeName];
//get the size of the text box
CGRect textsize = [cellText boundingRectWithSize:constraint options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
//calculate your size
float textHeight = textsize.size.height +20;
//I have mine set for a minimum size
textHeight = (textHeight < 50.0) ? 50.0 : textHeight;
return textHeight;
}
I haven't tested it for iOS<7, but I believe it should work for that as well.