Scaleable buttons in objective-c - iphone

I was wondering if its possible, and how i might do the following,
I would like to have a UIButton, a simple rounded button with one color, but i would like to have it's width scaleable based upon the UILabel that is on the inside of it.
I found somethings that talked about how it's possible to use
UItextfield
and use the .leftView and .rightView properties that it has
Could anyone give me just a quick example of how i might do this?
Thanks!

You can find the size of the NSString in the UILabel by doing something like this:
CGSize size=[uibutton.titlelabel.text sizeWithFont:[UIFont fontWithName:#"Helvetica" size:fontSize] forWidth:uibutton.titlelabel.frame.size.width lineBreakMode:UILineBreakModeCharacterWrap];
Then set the frame of the uilabel and/or uibutton using this size.width and size.height properties.
This is just an example, you will need to adjust the UIFont properties based on your label's settings or preferences.

I assume that the text of the UILabel will by dynamic, so you cannot just figure out the dimensions and add them normally?
try this:
-(void)setLabelText:(NSString*)string
{
int length = string.length;
myLabel.frame = CGRectMake(x, y, 5*length+5, height);
buttonContainingLabel.frame = CGRectMake(x, y, 25+5*length, height);
}
This method assumes a monospaced font, which you are probably not using. Still, discrepancies in character width probably won't amount to any meaningful size differential as you are not writing a novel in the button. I use the value of 5 px here as the width of a letter, but you will have to modify this to fit your text size.

Related

How to estimate the proper height of a UITextField, to hold text of given font size

I want to display a single-line text field using UITextField and I need to know before displaying it, the proper size for its containing UICollectionViewCell. The text can be one of multiple font sizes and I need to get the right height for displaying it comfortably.
Since the text is not known in advance (it can be edited by the user), I can't use NSAttributedString's -size and -boundingRectWithSize:options:context: with anything but dummy text, in which case I can't really trust the resulting size to hold any piece of text, right?
I guess my question is: Is there a rule of thumb about typography in general, or some useful API I'm not aware of, that would allow me to determine that for displaying text at X pt, I need a text field with a height of Y px.
UITextField implements the sizeThatFits: method. So the most reliable way to get the size of your text field is to actually create one, set it up like you would set up your real text fields, and ask it for a suitable size. You don't even have to give it placeholder text, because UITextField will choose the size based on its font, not on its text.
UITextField *dummy = [[UITextField alloc] init];
dummy.font = [UIFont systemFontOfSize:20];
dummy.borderStyle = UITextBorderStyleRoundedRect;
CGFloat requiredHeight = [dummy sizeThatFits:CGSizeMake(HUGE_VALF, HUGE_VALF)].height;
// requiredHeight == 30 in my test
If your deployment target is iOS 6.0 or later, you can instead use the intrinsicContentSize property, like this:
CGFloat requiredHeight = dummy.intrinsicContentSize.height;
// requiredHeight == 30 in my test
Note that sizeThatFits: still works in iOS 6, but intrinsicContentSize is a little easier to understand.
iOS always returns a height of 30 units when the borderStyle is RoundedRect. If you want to compute the height required by a custom font, you have to change the borderStyle to any other value, compute the height, and change the borderStyle back.
textField.borderStyle = UITextBorderStyle.None;
let sizeThatFits = textField.sizeThatFits(CGSize(width:textField.bounds.width, height: CGFloat.max));
textField.bounds.height = sizeThatFits.height;
textField.borderStyle = UITextBorderStyle.RoundedRect;

Calculating the height of a UILabel?

I wanted to know is it possible to get the height of a multi line UILabel? I'm developing a messaging application and wanted to achieve something like the iPhone messaging application.
You can get the property with label.frame.size.height
You probably want the -[UILabel sizeThatFits:] method. Here's what you do. Let's say your UILabel is in the variable myLabel, and you've already set its width to whatever you want.
myLabel.text = #"This is my very long message which will probably need multiple lines to be displayed because it is very long.";
CGRect bounds = myLabel.bounds;
// Create a size that is the label's current width, and very very tall.
CGSize prototypeSize = CGSizeMake(bounds.size.width, MAXFLOAT);
// Ask myLabel how big it would be if it had to fit in prototypeSize.
// It will figure out where it would put line breaks in the text to
// fit prototypeSize.width.
CGSize fittedSize = [myLabel sizeThatFits:prototypeSize];
// Now update myLabel.bounds using the fitted height and its existing width.
myLabel.bounds = (CGRect){ bounds.origin, CGSizeMake(bounds.size.width, fittedSize.height) };
If you call
[label sizeToFit];
it will resize the UILabel to the minimum size needed to hold all the content. Then you can just do label.frame.size.height to get the height of the label with that amount of text in it.

How can I work out the margin size of a UITextView?

I am trying to work out the size of a size of a textView up to the cursor by trimming all the text after the cursor, and then using NSString's sizeWithFont method, like so:
NSString *string = [myTextView.text substringToIndex:myTextView.selectedRange.location];
CGSize size = [string sizeWithFont:myTextView.font constrainedToSize:myTextView.frame.size lineBreakMode:UILineBreakModeWordWrap];
Unfortunately, this never returns quite the right size, probably because the text has margins, so its actual width is less than UITextView's width (thanks to the answerers of this question for working that out).
So I need to work out the size of the margins, and subtract that from the UITextView's size to get the actual size of the text area. Does anyone know how to do that?
Unfortunately it looks like the answer is that there is no margin in UITextView - you just have to simulate one by putting a view behind it, and making the UITextView narrower. If you need the background to scroll with the text, you can listen for scrollViewDidScroll:.
I'd suggest
CGSize tSize = myTextView.frame.size;
tSize.width -= 2 * myTextView.contentInset.left;
tSize.height -= 2 * myTextView.contentInset.top;
CGSize size = [string sizeWithFont:myTextView.font constrainedToSize:tSize lineBreakMode:UILineBreakModeWordWrap];

How make UILabel with autoresizingMask in a custom cell?

I have a custom uitableviewcell with several labels and I would like for some of them to autoresize their frame (width) based on the content (text). I am not sure how to accomplish that. I tried to set fixed frame of the label and after apply autoresizingMask, but that doesn't do the trick. Any *pointer to a sample?
If you want to adjust the frame of a label to fit the labels text, just call
[label sizeToFit];
This will fit in both dimensions.
Use the following method:
CGSize *size = [label.text sizeWithFont:fontOfLabelText];
float widthOfLabel = size.width;
size.width will return the actual width that the text in the label will occupy on the screen. Set the label width equal to width of text.
If you just want to make so that the label fits in multiple lines, you have to say:
cell.textLabel.numberOfLines = 0;
However I'm not sure if this is the one that you want... Usually if the content is text than that'll be what you want.

sizeWithFont doesn't give correct height for UITextView if there is a long string in the text being wrapped

Is there a way to get the correct size of an NSString using:
- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode
that doesnt get thrown off by 2 or 3 hundred character strings. At the moment if I try to use this method on these long strings it incorrectly calculates them and I end up with lots of whitespace at the bottom of the UITextView.
I've tried using UILineBreakModeWordWrap and UILineBreakModeCharacterWrap.
the resizing is being done in
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat result = 44.0f;
NSString* text = nil;
CGFloat width = 0;
CGFloat tableViewWidth;
CGRect bounds = [UIScreen mainScreen].bounds;
tableViewWidth = bounds.size.width;
width = tableViewWidth - 150;
text = stringWithLongWords;
if (text) {
CGSize textSize = { width, 20000.0f };
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:10.0f] constrainedToSize:textSize lineBreakMode:UILineBreakModeWordWrap];
size.height += 50.0f;
result = MAX(size.height, 44.0f+30.0f);
}
return result;
}
UITextView is not exactly like a UILabel wrapped in a UIScrollView. It has line spacing different from the font size and margins that sizeWithFont:constrainedToSize:linkBreakMode: doesn't account for.
Knowing your font size you might be able to calculate the # of lines and take line spacing into account. You can guess at the margins and try to trick sizeWithFont: to give a more useful answer.
The popular solutions seem to be:
just use a UILabel if you don't need any UITextView functionality
if you need hyperlinks, overlay UIButtons that look like hyperlinks over a UILabel
use an off-screen UITextView and its sizeToFit method to get a real answer
I had no luck w/ the 3rd option but it sounds like it should work, so perhaps I did something wrong.
I'm going to try using a UILabel and overlaying buttons for hyperlinks. We'll see how that turns out.
If that fails, there is always the option taken by Loren Brichter (of Tweetie fame): draw everything into a UIView yourself using CoreGraphics.
Good luck!
Check out this post How do I size a UITextView to its content?
It looks like textView.contentSize.height should work (with the caveat that the the correct contentSize is only available after the UITextView has been added to the view with addSubview)
You said that you have a UITableView with differing heights. Have you set the reuse identifier to the same thing for all of the cells? It could be that older cells with their height already set are being reused. If this is the problem, you should resize the cell again when it's being reused.
The best solution I have found so far is to have a separate hidden UITextView with the same font settings, and set its text. After that its contetSize should be accurate.
The width you are using is the width for your UITextView... but you aren't concerned with that width, you are concerned with the width of the actual text area nested inside the text view.
UITextViews, by default, have padding around their borders to produce a space in-between the typed text and the edge of the UITextView a few pixels wide (and long for the top)... To get the correct size you shouldn't use
textView.frame.size.width
but rather,
textView.frame.size.width-(textView.contentInset.left+textView.contentInset.right+textView.textContainerInset.left+textView.textContainerInset.right+textView.textContainer.lineFragmentPadding/*left*/+textView.textContainer.lineFragmentPadding/*right*/)
^Which takes the width of the UITextView and subtracts out all the padding so you are left with the width of just the type-able text area.
Same goes for height except for lineFragmentPadding doesn't have a bottom so you only subtract it out once instead of twice.
The final code is something like this:
CGSize textViewContentSize = CGSizeMake(theTextView.frame.size.width-(theTextView.contentInset.left+theTextView.contentInset.right+theTextView.textContainerInset.left+theTextView.textContainerInset.right+theTextView.textContainer.lineFragmentPadding/*left*/+theTextView.textContainer.lineFragmentPadding/*right*/), theTextView.frame.size.height-(theTextView.contentInset.top+theTextView.contentInset.bottom+theTextView.textContainerInset.top+theTextView.textContainerInset.bottom+theTextView.textContainer.lineFragmentPadding/*top*//*+theTextView.textContainer.lineFragmentPadding*//*there is no bottom padding*/));
CGSize calculatedSize = [theTextView.text sizeWithFont:theTextView.font
constrainedToSize:textViewContentSize
lineBreakMode:NSLineBreakByWordWrapping];
CGSize adjustedSize = CGSizeMake(ceilf(calculatedSize.width), ceilf(calculatedSize.height));
Inspired by #MrNickBarker's answer, here's my solution:
CGFloat width = 280.0f;
UITextView *t = [[UITextView alloc] init];
[t setFont:[UIFont systemFontOfSize:17]];
[label setText:#"some short or long text, works both"];
CGRect frame = CGRectMake(0, 0, width, 0);
[t setFrame:frame];
// Here's the trick: after applying the 0-frame, the content size is calculated and can be used in a second invocation
frame = CGRectMake(0, 0, width, t.contentSize.height);
[t setFrame:frame];
The only issue remaining for me is that this doesn't work with modified insets.
Still can't believe such twists are required, but since -[NSString sizeWithFont:forWidth:lineBreakMode:] does not respect insets, paddings, margins, line spacings and the like, it seems this is the only working solution at the moment (i.e. iOS 6).