Move uiview once uilabel text has changed - ios

I have a UIlabel that when a user clicks a button its text changes, this label is then resized using:
CGRect frame = label.frame;
[label sizeToFit];
frame.size.height = label.frame.size.height;
label.frame = frame;
So that the width is kept the same but the height of the label is changed so all text fits in this.
I then however need the uiview below this label to be moved down so that it starts at the bottom of the label, how do I do this?

Worked it out in the end by doing:
CGRect contentframe = _lbl_content.frame; //the uilabel
CGRect keyframe = _view_keystuff.frame; //the uiview
float startlocation = contentframe.origin.y;
startlocation += contentframe.size.height;
keyframe.origin.y = startlocation;
_view_keystuff.frame = keyframe;

You just want to put the UIView below the label?
Try this:
UIView *viewBelowLabel = [[UIView alloc] init];
viewBelowLabel.frame = CGRectMake( "your view origin x", label.frame.origin.y + label.frame.size.height, "your view size width", "your view size height");
You should adjust UIView frame after the resizing of the label

Related

How to programmatically sizeToFit width AND height on UILabel?

I'm programmatically creating multi-line UILabels ([label setNumberOfLines:0];).
The built-in sizeToFit method of UILabel works great for 1 line UILabels, but for multi-line text, it sets the height properly, but the width is set too small, causing longer text lines to wrap.
I don't know the label width until after the user enters their text. I want to resize the labels to fit the width of the longest line of text. And per #DonMag's comment, I also want to restrict the label to not be wider than the screen.
I tried different lineBreakMode settings but there isn't a 'nowrap' option.
I've searched SO and there are many related solutions but none that solve the problem of sizeToFit for both width and height.
Is there a way to programmatically size a multi-line UILabel to fit BOTH the width AND the height of the text?
You can do this with boundingRectWithSize...
Add your label to the view and give it a starting width constraint (doesn't really matter what value, as it will be changed).
Keep a reference to that width constraint (IBOutlet works fine if you're using IB).
Don't give it a height constraint.
When you set the text of the label, you can use this to change its width:
// get the font of the label
UIFont *theFont = _theLabel.font;
// get the text of the label
NSString *theString = _theLabel.text;
// calculate the bounding rect, limiting the width to the width of the view
CGRect r = [theString boundingRectWithSize:CGSizeMake(self.view.frame.size.width, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:#{NSFontAttributeName: theFont}
context:nil];
// change the constant of the constraint to the calculated width
_theWidthConstraint.constant = ceil(r.size.width);
// NOTE: If you are *not* using auto-layout,
// this same calculation can be used to explicitly set
// the .frame of the label.
Edit:
As per the OP's requirement, a complete, runnable example -- using code only, no storyboards / IB -- can be found here: https://github.com/DonMag/MultilineLabelFitWidth
Edit 2:
GitHub project updated... now includes examples for both manual frame setting and auto layout / constraints.
With some more experimentation, I found something that does the trick that I have not seen in SO (yet...). In general it works like this:
Find the longest text line
Set numberOfLines to 1 (temporarily)
Set label text to longest text line
Call label.sizeToFit (sets label width for longest line)
Set numberOfLines to 0 (multi-line)
Set label text to full multi-line text
Call label.sizeToFit (sets label height for all lines)
Voila! Now your UILabel is sized to fit your multi-line text.
Here is an example (demo project on GitHub: UILabelSizeToFitDemo):
- (UILabel *)label = nil;
- (void)updateLabel:(NSString *)notes {
// close to the "sticky" notes color
UIColor *bananaColor = [ViewController colorWithHexString:#"#FFFC79"];
if (_label == nil) {
_label = [[UILabel alloc] init];
_label.numberOfLines = 0;
_label.textColor = UIColor.blackColor;
[_label setBackgroundColor:[bananaColor colorWithAlphaComponent:0.9f]];
_label.textAlignment = NSTextAlignmentLeft;
[self.view addSubview:_label];
}
// make font size based on screen size
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat fontSize = MIN(screenWidth,screenHeight) / 12;
[_label setFont:[UIFont systemFontOfSize:fontSize]];
// split lines
NSArray *lines = [notes componentsSeparatedByString:#"\n"];
NSString *longestLine = lines[0]; // prime it with 1st line
// fill a temp UILabel with each line to find the longest line
for (int i = 0; i < lines.count; i++) {
NSString *line = (NSString *)lines[i];
if (longestLine == nil || line.length > longestLine.length) {
longestLine = line;
}
}
// force UILabel to fit the largest line
[_label setNumberOfLines:1];
[_label setText:longestLine];
[_label sizeToFit];
// make sure it doesn't go off the screen
if (_label.frame.size.width > screenWidth) {
CGRect frame = _label.frame;
frame.size.width = screenWidth - 20;
_label.frame = frame;
}
// now fill with the actual notes (this saves the previous width)
[_label setNumberOfLines:0];
[_label setText:notes];
[_label sizeToFit];
// center the label in my view
CGPoint center = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.size.height / 2);
[_label setCenter:center];
}
UPDATE: Here is an alternate complete solution, using the boundinRectWithSize from the code snippet by #DonMag:
-(void)updateLabel:(NSString *)notes {
// close to the "sticky" notes color
UIColor *bananaColor = [ViewController colorWithHexString:#"#FFFC79"];
if (_label == nil) {
_label = [[UILabel alloc] init];
_label.numberOfLines = 0;
_label.textColor = UIColor.blackColor;
_label.backgroundColor = [bananaColor colorWithAlphaComponent:0.9f];
_label.textAlignment = NSTextAlignmentLeft;
[self.view addSubview:_label];
}
// set new text
_label.text = notes;
// make font size based on screen size
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat fontSize = MIN(screenWidth,screenHeight) / 12;
[_label setFont:[UIFont systemFontOfSize:fontSize]];
// calculate the bounding rect, limiting the width to the width of the view
CGRect frame = [notes boundingRectWithSize:CGSizeMake(self.view.frame.size.width, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:#{NSFontAttributeName: _label.font}
context:nil];
// set frame and then use sizeToFit
[_label setFrame:frame];
[_label sizeToFit];
// center the label in my view
CGPoint center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
[_label setCenter:center];
}

How to calculate TextView height base on text

I am using the code below for calculate the height of text, then set this height for UILabel and UITextView
CGSize targetSize = CGSizeMake(300, CGFLOAT_MAX);
NSString *message = #"The Internet connection appears to be offline.";
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize boundingBox = [message boundingRectWithSize:targetSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:FontOpenSanWithSize(14)}
context:context].size;
CGSize size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
// it will return size:width = 299 and size height 20
// => if I use this height and set for UILabel, it can display full content
// => if I use this height and set for UITextView, it can not display full content
It's work perfect for UILabel but for UITextView sometime it calculate wrong.
I think the problem happened because the padding (left, right) of UITextView is bigger than UILabel.
So how can I calculate the correct size of text for display in UITextView. Any help or suggestion would be great appreciated.
Like the description image below
With the same size (300), same font, same text but the UITextView display in 2 lines, but UILabel in 1 lines.
And my code for calculate height return 20, it not enough for display in 2 lines, so the UITextViewcan not display full content
The reason why I need to calculate the height of UITextView base on text because my UITextView is in a popup.
And the popup height will depend on the TextView height
There are two things you can try:
Set textView.textContainerInset = UIEdgeInsetsZero
Set textView.textContainer.lineFragmentPadding = 0
With these operations you can get rid of all the padding in the textView and when its width matches with the label's one the heights are also the same.
Here's a sample code you can place in an empty viewController and test it yourself:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSString *text = #"The internet connection appears to be offline.";
CGFloat width = 100.f;
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(20, 20, width, 300)];
textView.font = [UIFont fontWithName:#"AvenirNext-Regular" size:12.f];
textView.text = text;
[self.view addSubview:textView];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20 + width, 20, width, 300)];
label.numberOfLines = 0;
label.font = [UIFont fontWithName:#"AvenirNext-Regular" size:12.f];
label.text = text;
[self.view addSubview:label];
// Getting rid of textView's padding
textView.textContainerInset = UIEdgeInsetsZero;
textView.textContainer.lineFragmentPadding = 0;
// Setting height of textView to its contentSize.height
CGRect textViewFrame = textView.frame;
textViewFrame.size = textView.contentSize;
textView.frame = textViewFrame;
// Setting height of label accorting to it contents and width
CGRect labelFrame = label.frame;
labelFrame.size = [label sizeThatFits:CGSizeMake(width, HUGE_VALF)];
labelFrame.size.width = width;
label.frame = labelFrame;
NSLog(#"Label bounds: %#", NSStringFromCGRect(label.bounds));
NSLog(#"TextView bounds: %#", NSStringFromCGRect(textView.bounds));
// Visualizing final effect with borders
textView.layer.borderColor = [UIColor redColor].CGColor;
textView.layer.borderWidth = 1.f;
label.layer.borderColor = [UIColor greenColor].CGColor;
label.layer.borderWidth = 1.f;
}
Console output:
2016-09-01 14:29:06.118 stack39268477[943:243243] Label bounds: {{0, 0}, {100, 66}}
2016-09-01 14:29:06.119 stack39268477[943:243243] TextView bounds: {{0, 0}, {100, 66}}
You don't need to calculate Height of UITextview based on text.
Just change frame and set height like this:
textview.size.height = textview.contentSize.height;
This is easy solution. I hope this helps you.
This calculates the size of any string, whether or not you put them in a text view.
let frame = NSString(string: yourText).boundingRect(
with: CGSize(width: yourDesiredWidth, height: .infinity),
options: [.usesFontLeading, .usesLineFragmentOrigin],
attributes: [.font : yourFont],
context: nil)
let height = frame.size.height
Most of the answers here are hints into the right direction :-)
So, just to sum it all up...
UITextView uses a NSTextContainer (inside a private API _UITextContainerView) to do the real layout work.
This NSTextContainer(View) may have insets to the surrounding UITextView, which are set by UITextView's textContainerInset property.
The defaults for this insets seem to be:
top: 8
left: 0
bottom: 8
right: 0
The NSTextContainer itself may have additional left and right insets for the text itself. These insets are set in NSTextContainer's lineFragmentPadding property.
The default for this is 5.0.
As a result, when calculating the optimum frame size for a UITextView based on the boundingRect for some text inside that UITextView, we have to take all these insets into account:
CGSize reservedSpace = CGSizeMake((textView.textContainerInset.left + (2.0 * textView.textContainer.lineFragmentPadding) + textView.textContainerInset.right),
(textView.textContainerInset.top + textView.textContainerInset.bottom));
CGSize targetSize = CGSizeMake((300.0 - reservedSpace.width), CGFLOAT_MAX);
NSString* message = #"The Internet connection appears to be offline.";
NSStringDrawingContext* context = [[NSStringDrawingContext alloc] init];
CGSize boundingBox = [message boundingRectWithSize:targetSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:FontOpenSanWithSize(14)}
context:context].size;
CGSize size = CGSizeMake(ceil(boundingBox.width),
(ceil(boundingBox.height) + reservedSpace.height));
Good luck :-)
Swift 5
A classic hack I've used to do this is create a function that takes the String as a parameter. Within the function it generates a label with the width required, infinite number of lines, and a height of "too much". Then apply sizeToFit() on the label and return the height of the frame.
func calculatedHeight(for text: String, width: CGFloat) -> CGFloat {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: width,
height: .greatestFiniteMagnitude))
label.numberOfLines = 0
label.text = text
label.sizeToFit()
return label.frame.height
}
The returned height can be applied to a UITextField height anchor. Though I do recommend calculating the width dynamically based on screen size, but that's up to you.
self.textView.textContainerInset = UIEdgeInsets.zero
self.textView.textContainer.lineFragmentPadding = 0
In storyboard or xib mark textview height >=0.
If you are using text view with table view.
Calculate cell height according to content, textview will adjust it's space.

IOS uiview frame didn't update

Hi I tried to assign new frame size to the separator uiview, but it doesn't change, i dunno why, I tried to debug and the newFrame have a new value but after assign to separator, the separator seem like never receive the new value
UIView *separator = (UIView *)[cell viewWithTag: 10];
CGRect newFrame;
if(lblBuyPrice.frame.origin.x > lblBuyQuantity.frame.origin.x){
newFrame = CGRectMake( lblBuyQuantity.frame.origin.x, separatorBuy.frame.origin.y, lblBuyQuantity.frame.size.width, separatorBuy.frame.size.height);
}else{
newFrame = CGRectMake( lblBuyPrice.frame.origin.x, separatorBuy.frame.origin.y, lblBuyPrice.frame.size.width, separatorBuy.frame.size.height);
}
separatorBuy.frame = newFrame;
the uiview element is inside a cell

UIButton Title Label Not Filling When Text Alignment Is Centered

I have a UIButton and am trying to center the text. The below works, but it cuts off my button's titleLabel way before the end of the button. I want it to grow until the edge of the subview, i.e. a CGRect frame of (8+9,button height ,button width - 8 - 9, button height)
self.setDestinationButton.titleLabel.textAlignment = NSTextAlignmentCenter;
self.setDestinationButton.layer.masksToBounds = NO;
self.setDestinationButton.titleLabel.adjustsFontSizeToFitWidth = YES;
self.setDestinationButton.titleLabel.minimumScaleFactor = .75;
self.setDestinationButton.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
UIImageView *destinationIcon = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"grey_dot"]];
destinationIcon.frame = CGRectMake(8, 18 ,9, 9);//choose values that fit properly inside the frame of your baseButton
//or grab the width and height of yourBaseButton and change accordingly
destinationIcon.contentMode=UIViewContentModeScaleAspectFill;//or whichever mode works best for you
[self.setDestinationButton addSubview:destinationIcon];

How to create TextView with increasing frame size rather than increasing the scroll size when user types

I need to create two text entry field one beneath the other. When user types in the upper text field, I need to increase the frame size dynamically (scrolling inside the textview is not recommended.) and the lower text entry frame should go downwards. Will this can be possible in ios? Any help will be appreciated.
You can update the frame of your textView via its contentSize like below:
-(void)updateTextViewSize
{
CGRect rect = textView.frame;
rect.size.width = textView.contentSize.width;
rect.size.height = textView.contentSize.height;
textView.frame = rect;
//lowering the textView2 below this textView
//assuming that the lower textview touches the
//bottom of the upper textView.
CGRect rect2 = textViewLower.frame;
rect2.origin.y = textView.frame.origin.y + textView.frame.size.height;
textViewLower.frame = rect2;
//update your scrollView here
//assuming textView and textViewLower are already added to the scrollView
scrollView.contentSize = CGSizeMake(scrollView.contentSize.width, textViewLower.frame.origin.y + textViewLower.frame.size.height);
}
//implement this delegate method as shown below
-(void)textViewDidChange:(UITextView*)textView
{
[self updateTextViewSize];
}
It is easy to do resizing of the UITextView to its correct height according to its content using the UITextView contentSize.
CGRect frame = _textView.frame;
frame.size.height = _textView.contentSize.height;
_textView.frame = frame;

Resources