I will use a UIPopoverController to display a UITableViewController. The table view will show a list of names with unknown lenghts (could be Bob or could be Alexander).
I know how to vary the height of the popover, but i can't seem to figure how to vary the width of it so a name does not get truncated.
Need to figure out the width of a name so i can set the popover to that width so names don't bleed out of the popover:
any ideas?
Get the width of the string and then set the UITable to be that width. Here is a method you could add to get the width. You could add it to a category or to your class. I took the code from here.
- (CGFloat)widthOfString:(NSString *)string withFont:(NSFont *)font {
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil];
return [[[NSAttributedString alloc] initWithString:string attributes:attributes] size].width;
}
This way you can specify the font you are using in case you end up changing it in the future. (Different fonts obviously will result in different widths).
Presumably you've got any array of names. What you'll do is enumerate the list of names, determine the lengths required for each one, and then store the greatest one and apply it as the desired width. You will need to define a "maximum size" that should not be exceeded. A method along these lines should return something suitable:
- (CGFloat)suitableWidthForLabel:(UILabel *)nameLabel withNames:(NSArray *)arrayOfNames
{
CGFloat suitableWidth = 100.0f; // A reasonable starting point
CGSize maximumSize = CGSizeMake(600.0f, nameLabel.frame.size.height);
UIFont *labelFont = [nameLabel font];
for (NSString *aName in arrayOfNames)
{
CGSize expectedLabelSize = [aName sizeWithFont:labelFont
constrainedToSize:maximumSize
lineBreakMode:nameLabel.lineBreakMode];
suitableWidth = MAX(suitableWidth, expectedLabelSize.width);
}
return suitableWidth; // This will be the largest size you may need to accommodate
}
Related
I have a UITableView that displays a UILabel in each cell. The text length varies for each item.
[#"Test1",#"Test 245", #Test 568974"]
Now I want to identify the font size that can correctly fit the largest text. In this case I need to find the font size of UILabel that can correctly display the text #Test 568974" with out any truncation.
I have tried using the
adjustsFontSizeToFitWidth
property and also set
Automatically Adjust Font
in story board with minimum font size. But that makes each text to have a different font size.
How to do it?
I finally found the solution,
-(CGFloat)findFontSizeforLabel:(UILabel*)label{
CGFloat minFontSize = 22;
for(Product *product in products){
CGFloat fontSize = 22.0;
while(fontSize > 0){
NSDictionary *attributes = #{NSFontAttributeName: [UIFont fontWithName:#"fontname" size:fontSize]};
CGSize size = [product.title sizeWithAttributes:attributes];
if(size.width < label.frame.size.width - 15){
if(fontSize < minFontSize){
minFontSize = fontSize;
}
break;
}
fontSize--;
}
}
return minFontSize;
}
NSString has a method sizeWithAttributes:
Create a dictionary with the desired text attributes, e.g.:
attrs = [NSDictionary dictionaryWithObject:yourFont forKey: NSFontAttributeName]
and use the method above to get the bounding rectangle. Note that you need to round the returned fractional values to integral values.
With that do a binary search between reasonable font sizes to fit your largest label.
To make label resizable add to interface builder constraints like this:
and set for your table rowHeight to Automatic and estimatedRowheight more than 45:
tableView.estimatedRowHeight = 50
tableView.rowHeight = UITableViewAutomaticDimension
This makes your cell adjust label height.
If you want to get label height programmatically, use:
NSDictionary *attributes = #{NSFontAttributeName: [UIFont fontWithName:#"HelveticaNeue" size:14]};
CGRect rect = [textToMeasure boundingRectWithSize:label.frame.size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributes
context:nil];
This gives you size text for given label size and attributes
I have 4 label views of which one is supposed to show large numerical values and is set for autoshrink.
My requirement would be to set the same font-scaling or size this label has, after its been auto-adjusted to fit its content, to the other labels too so that the text content looks uniform throughout.
Setting the minimum scale factor didn't help for the other labels because they have content within the frame limits.
There's no way to do this directly, since querying the font of the label where the text has been shrunk to fit, still shows the original font size. You have to do it by iterating over smaller and smaller font sizes until you find the size that fits in your label, and then use that font size to adjust your other labels. In my example below, labelLong is the one whose text can shrink, and labelShort is the one whose text doesn't need to shrink.
-(void)updateFont {
NSStringDrawingContext *ctx = [NSStringDrawingContext new];
ctx.minimumScaleFactor = 1.0;
UIFont *startingFont = self.labelLong.font;
NSString *fontName = startingFont.fontName;
CGFloat startingSize = startingFont.pointSize;
for (float i=startingSize*10; i>1; i--) { // multiply by 10 so we can adjust font by tenths of a point with each iteration
UIFont *font = [UIFont fontWithName:fontName size:i/10];
CGRect textRect = [self.labelLong.text boundingRectWithSize:self.labelLong.frame.size options:NSStringDrawingTruncatesLastVisibleLine attributes:#{NSFontAttributeName:font} context:ctx];
if (textRect.size.width <= [self.labelLong textRectForBounds:self.labelLong.bounds limitedToNumberOfLines:1].size.width) {
NSLog(#"Font size is: %f", i/10);
self.labelShort.font = [UIFont fontWithName:fontName size:i/10];
break;
}
}
}
In my project there is a specific requirement, When UILabel's text gets truncated I need to give view more functionality. Initially there will be a CGRect given. Accordingly we need to show the label if text truncated we need to at the end of label ...View more text should be shown. Upon tapping on ...View more view more I need to make my label bigger. So I m doing
NSMutableString *truncatedString = [text mutableCopy];
[truncatedString appendString:ellipsis];
NSRange range = NSMakeRange(truncatedString.length - (ellipsis.length + 1), 1);
do {
[truncatedString deleteCharactersInRange:range];
range.location--;
[self setText:truncatedString];
} while ([self isTextTruncated]);
it works fine for smaller text since I m using it for UITableViewCell. It is lagging for bigger texts since above operation happens for every time. So I want to know the text that is adopted in UILabel so that I can do any operation with new text. Any help would be greatly appreciated.
EDIT:
I have a label and bigger text. say my text is
"Apple Inc. is an American multinational corporation headquartered in Cupertino, California, that designs, develops, and sells consumer electronics, computer software, online services, and personal computers." if my label would adopt only "Apple Inc. is an American multinational corporation.." I need this text alone
Use this method to calculate height that would be required for the text to get fit into the provided UILabel:
- (CGFloat)getLabelHeight:(UILabel*)label
{
CGSize constraint = CGSizeMake(label.frame.size.width, 20000.0f);
CGSize size;
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize boundingBox = [yourString boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:label.font}
context:context].size;
size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
return size.height;
}
Compare the returned height with height of your label:
CGFloat heightRequired = [self getLabelHeight:myLabel];
if(myLabel.frame.size.height < heightRequired) {
//you need to show more because the text is more than the label width and height.
}
else {
//you don't need to show more because the text is not more than the label width and height.
}
EDIT: The purpose of comparing height is to check whether frame is enough to show text or not. So, even if you want to increase the width of label to show more text, it will give you desired result.
You need to calculate size of text as bellow and if returned size is bigger than text field size than you need to show ...View more
CGSize requiredSize = [text sizeWithFont:withFont constrainedToSize:textViewSize lineBreakMode:lineBreakMode];
If you didn't find an answer, i recommend this workaround:
By trying using a text with known length, get the max number of characters that the label fits,and use that value to do the following:
int maxNumOfChar = 15; //For example
if (text.length > maxNumOfChar){
NSString* viewMore = #"...View More";
text = [[text substringToIndex:maxNumOfChar - [viewMore length]] stringByAppendingString: viewMore];
}
I have a problem to implement the text vertical alignment inside a table cell.
What I want to do is based on the length of the text I want to display a message top aligned in side one UILabel inside a cell.
For example if the message is only one line
The text should align top:
And if there are two rows then it should look like this:
At the beginning what I can see is like this
So I have searched the web and what I found is to
use the
[label1 sizeToFit];
But the problem with that is within the table view cell it is not always necessarily called especially when I switched to and from another tab view.
Then I tried to generate the label on the fly by code, but the problem is that let alone the complicated process of setting up the font format I want. I have to manage whether the label has been inserted or not then reuse it every time cellForRowAtIndexpath is called.
And more weirdly, once I select the row. The alignment is switched from the one you see in the first picture to the third one. It also happens when I switched to a different tab view and switch back to the view.
I was wondering if anybody has encountered such issue and have a solution to the problem.
Thank you for your reply in advance.
Edit:
#βḧäṙℊặṿῗ, what you said I have tried. It successfully align the label text if there is only one line. My situation is that, since I have multiple tab views. Once I switch back and forth between tabs view. The alignment just restored to centre-vertical alignment again. It also happens when I selected the row. Any idea?
Try this
// label will use the number of lines as per content
[myLabel setNumberOfLines:0]; // VERY IMP
[myLabel sizeToFit];
EDIT:
As you have one extra condition that maximumly display two lines then you need to set setNumberOfLines: to 2
[myLabel setNumberOfLines:2];
Create UILabel+Extras and add following methods to this class.
- (void)alignTop{
CGSize fontSize = [self.text sizeWithAttributes:#{NSFontAttributeName:self.font}];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGRect rect = [self.text boundingRectWithSize:CGSizeMake(finalWidth, finalHeight) options:NSStringDrawingTruncatesLastVisibleLine attributes:#{NSFontAttributeName:self.font} context:nil];
CGSize theStringSize = rect.size;
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i< newLinesToPad; i++)
self.text = [self.text stringByAppendingString:#" \n"];
}
Call this method like this..
[YOUR_LABEL alignTop];
Either You set no of Lines to be 0 like
[yourLabelObject setNumberOfLines:0];
[yourLabelObject sizeToFit];
Or
You can find the height of label at run time depending upon textString length.
Following method will return you the size(height & width) of label for length of text string.
here width is fixed and only height will change :
- (CGSize) calculateLabelHeightWith:(CGFloat)width text:(NSString*)textString andFont:(UIFont *)txtFont
{
CGSize maximumSize = CGSizeMake(width, 9999);
CGSize size = [textString sizeWithFont:txtFont
constrainedToSize:maximumSize
lineBreakMode:UILineBreakModeWordWrap];
return size;
}
Yo need to calculate frame of label each time when u r doing to set text and set frame of label
I hope this will helps you.
Might sound a bit silly but this is my approach: For any field that needs to be top aligned I fill the text up with multiple "\n"s. This causes the text to be automatically top aligned. Pretty much the same as Mehul's method above.
http://i.stack.imgur.com/8u5q4.png
I have an app where I load a large plist file in a tableview. This plist file is as a book. It contains chapters and lines. Each row has different length depending on the line length and therefore I need to resize the cell automatically.
I am using storyboard and standard tableview and cell. Cell style=basic and the cell label is set to text=plain lines=0 linebreaks=wordwrap
Up to there, no problem resizing the cell height to the proper size. As the cell height is defined before the text is inserted in the label we have to do it by the well known method of using CGsize and I do it like that (it's working fine)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:
(NSIndexPath*)indexPath
{
NSUInteger section = [indexPath section];
NSString *key = [chapters objectAtIndex:section];
NSArray *linesSection = [lines objectForKey:key];
NSString* theText = [linesSection objectAtIndex:indexPath.row];
int labelWidth = LW_CHAPTERINDEX_10;
if (chapters.count < 100){
if (chapters.count < NB_MIN_CHAP){
labelWidth = LABELWIDTH;
} else {
labelWidth = LW_CHAPTERINDEX_10;
}
}
CGSize textSize = [theText sizeWithFont:[UIFont systemFontOfSize:14]
constrainedToSize:CGSizeMake(labelWidth, MAXFLOAT)
lineBreakMode:UILineBreakModeWordWrap];
return textSize.height;
}
The problem is the hardcoding depending on the index. For now I have 3 possibilites.
No index
Index with numbers below 10
Index with numbers below 100
and in the code this part
int labelWidth = LW_CHAPTERINDEX_10;
if (chapters.count < 100){
if (chapters.count < NB_MIN_CHAP){
labelWidth = LABELWIDTH;
} else {
labelWidth = LW_CHAPTERINDEX_10;
}
}
is the hardcoding depending on the 3 possibilities.
I find this was of doing weird, especially if apple will start to deliver more different screen sizes.
QUESTION
How can I get the index width at runtime to determine my label width ?
For example i would like to program something like screen.width - index-width to get the label width.
Or any other that should allow it to be dynamical and no more statically hardcoded.
Unfortunately there is no (standard) way you can directly access the section index subview. However, you can use the methods for calculating the CGSize of text to determine dynamically the width of the section index.
You could determine all possible strings to be returned by sectionIndexTitlesForTableView: beforehand and calculate the necessary size, perhaps padding with some extra pixels left and right. Maybe it is necessary to make some experiments to determine the correct font and size.
Now your approach with something like screenSize.width - textSize.width - 2*PADDING should be viable. The only hardcoded thing is now the padding, which should not break things when new screen sizes are introduced.
Ok, to save other people a few hours of work....I laboriously tested various fonts and sizes and margins, comparing them to a UIView hierarchy dump of an actual table view with an index, to arrive at this method which will return the width of a table index, so that the bounds of the table view cell content can be calculated as table_width - index_width. I will point out that there is often another 20 pixel right-side amount reserved for an accessory view.
I also discovered that the cell.contentView.bounds is NOT correctly set until AFTER cellForRowAtIndexPath and willDisplayCell methods are called, so trying to grab the value during those calls is doomed to fail. It is set correctly by the time viewDidAppear is called.
I have no idea how to calculate the index width if the device language is set for a non-English alphabet.
- (CGFloat) getMaxIndexViewWidth
{
CGFloat indexMargin = 21.0;
NSArray *sectionTitles = [self sectionIndexTitlesForTableView:nil]; //NOTE -- if multiple tables, pass real one in
CGFloat maxWidth11 = CGFLOAT_MIN;
for(NSString *title in sectionTitles)
{
CGFloat titleWidth11 = [title sizeWithFont:[UIFont fontWithName:#"Helvetica-Bold" size:11.0f]].width;
maxWidth11 = MAX(maxWidth11, titleWidth11);
}
return maxWidth11+indexMargin;
}