Resizing UITableViewCells in iOS4 versus iOS3 - iphone

I'm using the following code to size a UITableViewCellStyleSubtitle cell to fit text that can run to more than one line:
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGRect frame = [UIScreen mainScreen].bounds;
CGFloat width = frame.size.width;
int section = indexPath.section;
int row = indexPath.row;
NSString *title_string = [[my_array_ objectAtIndex:row]
valueForKey:#"keyOne"];
NSString *detail_string = [[my_array_ objectAtIndex:row]
valueForKey:#"keyTwo"];
CGSize title_size = {0, 0};
CGSize detail_size = {0, 0};
if (title_string && ![title_string isEqualToString:#""]) {
title_size = [title_string sizeWithFont:
[UIFont systemFontOfSize:TITLE_FONT_SIZE]
constrainedToSize:CGSizeMake(width, 4000)
lineBreakMode:UILineBreakModeWordWrap];
}
if (detail_string && ![detail_string isEqualToString:#""]) {
detail_size = [detail_string sizeWithFont:
[UIFont systemFontOfSize:DETAIL_FONT_SIZE]
constrainedToSize:CGSizeMake(width, 4000)
lineBreakMode:UILineBreakModeWordWrap];
}
CGFloat title_height = title_size.height;
CGFloat detail_height = detail_size.height;
CGFloat content_size = title_height + detail_height;
CGFloat height;
switch (section) {
case 0:
height = content_size; //+ offset;
break;
default:
height = 44.0;
break;
}
return height;
}
This runs fine in iOS4.* and gives cells that accommodate the text. (If anything they're slightly too big but that's OK.)
But when I try this in iOS3.* the cell is messed up: The textLabel appears low down in the cell and the detailLabel text often then spills over into the next cell or is truncated with "...". The textLabel seems to appear in the middle of the cell, rather than at the top.
What is different between iOS3 and iOS4 that might cause this? What can I do to resolve this? Or, is there a good alternative to the above (say) that will work on both iOSes.
Thanking you kindly in advance.

In this piece of code if you're just calculating the right line height and giving it to the table. So I suppose the cell height will be correct in both cases (iOS 4 and iOS 3). What happens is that you're not doing anything to adjust the two labels in the cell contentView, so probably the iOS 4 cell is auto-adjusting, while iOS 3 no.
Now you may try to study the iOS 3 subtitle cell hierarchy and apply the right changes or you can make your own custom labels and add them to the contentView hierarchy: of course these custom labels will have their frame, number of lines, font, ... calculated in the same way you used in the line height code (so you should start refactoring your code by exporting the line height calculation in a common method).

Related

Monotouch - calculate UILabel height

I am trying to create a custom cell which consists a few UILabels.
The first label might take one or more rows, so I need to resize the label according to the number of lines (after setting the number of lines to 0, so multi-line will be enabled).
I have tried setting sizeToFit(), but it changed the alignment and width of my label.
I found this answer
but I don't know how to convert it to C#.
Can anyone point me to an example? (I already tried Googling it off-course)
This is the method from the link:
// UILabel *myLabel;
CGSize labelSize = [myLabel.text sizeWithFont:myLabel.font
constrainedToSize:myLabel.frame.size
lineBreakMode:UILineBreakModeWordWrap];
CGFloat labelHeight = labelSize.height;
int lines = [myLabel.text sizeWithFont:myLabel.font
constrainedToSize:myLabel.frame.size
lineBreakMode:UILineBreakModeWordWrap].height/16;
// '16' is font size
var size = myLabel.StringSize("Some really long string", myLabel.Font, myLabel.Frame.Size, UILineBreakMode.CharacterWrap);
var lines = size.Height / myLabel.Font.CapHeight;

UILabel auto size error on iOS 7

I'm making my app transition to iOS 7 and have this method (already modified for iOS 7, using boundingRectWithSize...):
+ (CGSize)messageSize:(NSString*)message {
NSDictionary *attributes = #{NSFontAttributeName : [UIFont fontWithName:#"Futura-Medium" size:13]};
CGRect frame = [message boundingRectWithSize:CGSizeMake([PTSMessagingCell maxTextWidth], CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading attributes:attributes context:nil];
return frame.size;
}
I am getting this appearance:
The message UILabel is being cut. It feels like line spacing is too big. It tried many other answers I found but none of them work.
If someone knows how to help me, I appreciate! ;)
Thanks!
Try changing NSStringDrawingUsesFontLeading as your option to NSStringDrawingUsesLineFragmentOrigin.
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. Starting in iOS 6, the NSAttributedString's NSStringDrawing functions were introduced and they're threadsafe unlike the old NSString+UIKit methods we're used to (eg. sizeWithFont:..., etc), which were UIStringDrawing functions (and act unpredictably when you use them from a non-main thread. 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);
I think you are updating your label frame from either viewDidLoad or viewWillAppear, so it is not working.
if you will update frame of label from viewDidAppear method then you will get updated frame of label.
I am not sure why this is happened, I think it is iOS 7 bug.
Try this
+ (CGSize)messageSize:(NSString*)message {
CGSize nameSize = [message sizeWithFont:[UIFont fontWithName:#"Futura-Medium" size:13]
constrainedToSize:CGSizeMake(maxWidth, maxHeight) lineBreakMode:NSLineBreakByWordWrapping];
NSLog(#"width = %f, height = %f", nameSize.width, nameSize.height);
return nameSize;
}

adjusting text based on UITextView frame size

I am trying to adjust the size of the text in my UITextView so it fits. I have the following code:
NSString *storyTitle = [newsfeedToSubject.properties valueForKey:#"title"];
int currentFontSize = 18;
UIFont *currentFont = [UIFont fontWithName:kProximaNovaBold size:currentFontSize];
CGSize storyTitleSize = [storyTitle sizeWithFont:currentFont constrainedToSize:self.newsfeedStoryTitle_.frameSize lineBreakMode:UILineBreakModeWordWrap];
while (storyTitleSize.height >= self.newsfeedStoryTitle_.frameHeight){
currentFontSize--;
currentFont = [UIFont fontWithName:kProximaNovaBold size:currentFontSize];
storyTitleSize = [storyTitle sizeWithFont:currentFont constrainedToSize:self.newsfeedStoryTitle_.frameSize lineBreakMode:UILineBreakModeWordWrap];
}
[self.newsfeedStoryTitle_ setFont:currentFont];
[self.newsfeedStoryTitle_ setText:storyTitle];
[self.newsfeedStoryTitle_ setBackgroundColor:[UIColor redColor]];
However, here's what I am getting:
I tried with different line break mode and also set the autoresize to none, but it didn't help. Any idea?
Your while loop will not do what you want because you are using
storyTitleSize.height
to determine the size of the frame, which will not take into account the wrap around effect of the text. One suggestion would be to truncate the string and only display a certain amount of characters of the title. Otherwise, you will need to calculate how many line breaks you will have, based on the width of the frame and the length of the string, and use
while(storyTitleSize.height * numLineBreaks >= self.newsFeedStoryTitle_.frameHeight)
{
...
}
Hope that helps.
You can also dynamically resize your frame (UITextView) as in this thread
try these code
while (self.textView.contentSize.height >= frameHeight){
currentFontSize--;
currentFont = [UIFont fontWithName:kProximaNovaBold size:currentFontSize];
[self.textView setFont:currentFont];
self.textView.text = #"your string";
}

UILabel numberOfLines

I need to adjust numberOfLines of label in cellForRowAtIndexPath. Label is in word wrap mode.
However, the actual calculation of label's height is taking place in heightForRowAtIndexPath.
How to calculate numberOfLines in cellForRowAtIndexPath? For now I'm just using some very big number.
From the Apple Docs:
To remove any maximum limit, and use
as many lines as needed, set the value
of this property to 0.
Then you may use this technique in the heightForRowAtIndexPath method to determine how tall the cell should be.
Constants:
static const CGFloat kCellFontSize = 14.0;
static const CGFloat kCellWidth = 300.0;
static const CGFloat kCellHeightMax = 999.0;
static const CGFloat kCellPadding = 10.0;
Method:
CGSize maxSize = CGSizeMake( kCellWidth, kCellHeightMax );
CGSize labelSize =
[[self cellTextString] sizeWithFont: [self cellFont]
constrainedToSize: maxSize
lineBreakMode: UILineBreakModeTailTruncation];
return (labelSize.height + (2 * kCellPadding));

String length with given font to fit UITextView 2 - The Return

In this question I asked for a good way to truncate a string to fit a given UITextView. Since there was no way provided by the SDK directly, I've ended up writing the recursive method below (only called by the following public method). However, this doesn't work unless I subtract a fudge factor of 15 (kFudgeFactor) from the field width when calculating the string's height. If I don't do that, the string returned is actually too long for the field, and displays in an extra line below it. Anyone any idea why, and what I should really use instead of this fudge factor?
#pragma mark Size string to fit the new view
#define kFudgeFactor 15.0
#define kMaxFieldHeight 9999.0
// recursive method called by the main API
-(NSString*) sizeStringToFit:(NSString*)aString min:(int)aMin max:(int)aMax
{
if ((aMax-aMin) <= 1)
{
NSString* subString = [aString substringToIndex:aMin];
return subString;
}
int mean = (aMin + aMax)/2;
NSString* subString = [aString substringToIndex:mean];
CGSize tallerSize = CGSizeMake(self.frame.size.width-kFudgeFactor,kMaxFieldHeight);
CGSize stringSize = [subString sizeWithFont:self.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap];
if (stringSize.height <= self.frame.size.height)
return [self sizeStringToFit:aString min:mean max:aMax]; // too small
else
return [self sizeStringToFit:aString min:aMin max:mean];// too big
}
-(NSString*)sizeStringToFit:(NSString*)aString
{
CGSize tallerSize = CGSizeMake(self.frame.size.width-kFudgeFactor,kMaxFieldHeight);
CGSize stringSize = [aString sizeWithFont:self.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap];
// if it fits, just return
if (stringSize.height < self.frame.size.height)
return aString;
// too big - call the recursive method to size it
NSString* smallerString = [self sizeStringToFit:aString min:0 max:[aString length]];
return smallerString;
}
UIScrollView seems to use a fixed 8-pixel inset on both sides. This is independent of alignment or font size (based on testing & observation, not any explicit knowledge of the internals).
So it seems you are right to use your fudge factor, but it should probably be 16.0, not 15.0.
This is probably because the frame of the UIView is not the same size as the content view.
UITextView subclasses from UIScrollView.