I'm trying to use autolayout to automatically resize my popover to fit it's contents. I have fixed popover width but to compute height i rely on systemLayoutSizeFittingSize: passing my predefined width and zero height e.g. CGSizeMake(190, 0).
ContentController* controller = [ContentController new];
CGSize preferredSize = [controller.view systemLayoutSizeFittingSize:CGSizeMake(190, 0)];
controller.preferredContentSize = preferredSize;
UIPopoverController* popover = [[UIPopoverController alloc] initWithContentViewController:controller];
//popover presentation.
So far, so well, my current ContentController view hierarchy is something like (setup in the Interface Builder):
UILabel - multiline header (dynamically resized)
|
UIImage with fixed width / height (static size)
|
UILabel - multiline body (dynamically resized)
Thus, i just plug in my header / body text, call systemLayoutSizeFittingSize and it returns valid size that fits all the content of the view.
The problem arises when i try to put my body label inside UIScrollView. (Both in the IB and in code).
From now on, systemLayoutSizeFittingSize will not take body label height into account and will return height for header + image.
I've setup all the constraints in the IB to support Pure Autolayout approach.
I've checked scrollview's content size - it is indeed equals to body label's intrinsic size, but scroll view's frame is squashed into 0.
I've checked and tried to reset label's maxPreferredLayoutWidth to the width of the content view, but it doesn't seems to affect anything.
I've set translatesAutoresizingMaskIntoConstraints on every view to NO, but it has no effect.
I've set hugging / compression resistance priorities of both label and it's scrollview to 1000, but no luck. Setting them on the container view doesn't work either.
Here are screenshots of my IB view setup:
My guess that is is somehow related to popover hosting views and their autolayout constraints, but i'm not sure.
I'm updating my labels via simple
_textContainerHeaderLabel.text = headerText;
_textContainerBodyTextLabel.text = bodyText;
[self.view setNeedsUpdateConstraints];
[self.view layoutSubviews];
So, the main question - how do i compute view's optimal fitting size via autolayout when it has UIScrollViews in it?
Finally found a solution for this case. Don't know how correct it is, but i did the following - I've added height inequality constraint (>=0) from label to it's scrollview.
The trick is to make this constraint's priority lower than label's compression resistance (vertical, in my case). This seems to to solve this problem.
Related
I have a screen layout where there are two resizable labels , which will contain multiline text. These labels are placed inside their parent views which intern are added to main contentView, main contentView is then added to scrollView ( thats what most of the solutions suggests). For both the labels (below About and Time and location labels in first image attached) I have set height constraints as "greater than or equal to" and setting the numberOfLines to 0 as well as calling SizetoFit, but actual output is not as expected (see second image attached). There are no constraints warnings. All constraints are provided for all the elements.
The code in viewDidLoad is as follows for one of the label.
self.lblAbout.text = #"this is a long two three lines about string which will have two lines this is a long two three lines about string which will have two lines";
self.lblAbout.numberOfLines = 0;
[self.lblAbout sizeToFit];
[self.lblAbout setPreferredMaxLayoutWidth:244.0];
Also
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
scrollView.contentSize = contentView.frame.size;
}
Not if any additional constraints are needed, I have added all leading , trailing , top , bottom constrains along with height wherever needed, plus the spacing between all the views is in place.
What i want is the labels should get adjusted to number of lines and the contentView (parent view) should scroll inside ScrollView as the total height will be larger than the screen available.
*** problem I think is the outer view of the labels aren't getting resized as per the label because of which all the views below it aren't getting repositioned ****
Please try this Solution,
1. add Height Constraint to superView of your Label.
2. add IBOutlet of that Constraint
3. add this Method to find out Height of your Text
you need to give width of super view of your Label so width will be same as your super view
4. Now get Height form returned CGRect and assign it to your Constraints's constant. it should be like
heightConstraint.constant = youObject.size.height;
please Make sure you have added other Constraints accordingly this. if not than you need to also increase Height of other superviews accordingly.
(CGRect)sizeOfDetailLabelFromString:(NSString*)string maxWidth:(CGFloat)maxWidth{
NSDictionary *attributes = #{NSFontAttributeName:FONT_LIGHT};
CGRect rect = [string boundingRectWithSize:CGSizeMake(maxWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:attributes context:nil];
return rect;
}
Here is what worked finally.
Added ScrollView in main View
Added View as a contentView ( be sure to rename it in designer to something than just view )
pinned scrollview to main view using leading trailing top and bottom space constraints.
Pinned contentView to scrollview same as above
Added all the components with their respective constraints
Set the height of UILabel which i want to resize using "Greater than or equal to constraint (this is necessary) and set number of lines to 0 in code
The parent view of label shouldn't have any fixed height but enough constraints to calculate it at runtime.
Make sure ScrollView has no ambiguity in calculating contentSize.
imp - Add constraints to width of Main view , scrollview , contentView ( that was in my case , you may not need equal width constraint between contentView and scrollview , but between contentView and main view its necessary)
Now scrollview scrolls exactly the way it's needed.
To my belief most of the above things i already did but somehow it wasn't working ,deleted everything and did all the things again few times and it worked.
I am pretty new to iOS dev. I am trying to create a view with a UILabel and a UITableView. The UILabel is used to display some header text & the tableview is positioned below the header & it covers the remaining portion of the screen.
I am creating this view using code & not the storyboard approach. So I need to specify the height & width of my tableview as constraints. For the width I have set a constraint where it matches the width of the container view. However for the height I wish to use the following equation:
tableview's height = container view's height - UILabel's height
For this I need to know how I can get the container's & the UILabel's height via code. I tried the following approach-
CGFloat header_height=lblHeader.bounds.size.height;
But this gives the same height as the container view's height .i.e. 460. Can someone please explain how I can get the height of the UIlabel view?
To get the height of the UILabel do this instead:
CGFloat header_height = lblHeader.frame.size.height;
Frames and bounds are not the same.
Cocoa: What's the difference between the frame and the bounds?
I'm trying to set up a simple UITableViewCell in Interface Builder without auto layout. I'm using the old springs and struts.
When I give a label a flexible width, it seems to lay out as if the UITableViewCell has a much larger width, even though the UITableViewCell tells me it has a width of 375 pixels in layoutSubviews:
- (void)layoutSubviews
{
[super layoutSubviews];
NSLog(#"Label width: %f", self.nameLabel.frame.size.width); // 695.0 for a label that stretches the whole width
NSLog(#"Superview (UITableViewCell) width: %f", self.nameLabel.superview.frame.size.width); // 375.0
}
On a simulated iPhone 5S (iOS 7 or 8), the superview is 320 but the UILabel spreads to 640.
On a simulated iPhone 6, the superview is 375 but the UILabel spreads to 695.
On a simulated iPhone 6 Plus, the superview is 414 but the UILabel speads to 734.
I don't have this problem with other views. For example, I'm able to add a UILabel to a UIViewController and have it stretch the width correctly. What is going on? And how do I fix this?
EDIT:
Interestingly, if I add the constraints programmatically during cellForRowAtIndexPath: then it seems to work as expected, so long as I use the older dequeueReusableCellWithIdentifier instead of dequeueReusableCellWithIdentifier:forIndexPath:. I want to keep all my constraints in Interface Builder though. Any ideas?
I played around a bit with this today. Looks like the frame of the label when it's instantiated relative to the frame of the content view is wrong. If I make the label the same size as the cell in the storyboard, then in awakeFromNib the contentView has a size of CGRectZero but the label has the size I set in the storyboard. Therefore, when you get to layoutSubviews and the contentView is resized to the right size (0,0,320,44), because of the flexible width mask, the label itself is resized along with the content view (width increases by 320 as well). That's why its appearing larger than intended.
The only way I could get around this (albeit, it feels like a gross hack and you should probably stick to auto-layout), was to set the label's frame relative to the content view in awakeFromNib.
- (void)awakeFromNib
{
[super awakeFromNib];
self.label.frame = self.bounds;
}
I have a UIView that is a footerview of a uitableview. At run time, the user enters text into a uitextview within the footerview that should adjust to the size of the text content with a height constraint in autolayout.
All other objects in the view (labels, imageviews) have appropriate constraints to accommodate the expansion of the textview.
HOWEVER the height of the overall footerview will not change size, and it is impossible to use autolayout on the tableview footerview height.
Does anyone have a solution? Thanks
Haven't found an actual, elegant, solution yet, but I've postponed fixing this by using a workaround:
Setting the frame of the view used as a footer to be as large as you might possible need. In my case this meant giving it about 60px of spare vertical room. Since it's the footer and there's nothing below it to reposition the user won't be affected by the workaround.
The contents of the footer view are pinned to the top and have enough space to expand when needed.
For the record: my view is loaded from a nib file.
Although in theory the size one gives to the top level view in interface builder is just for design-time and the runtime size should be calculated based on constraints and the resulting intrinsic size, for this specific case I found the height stays the same as it was in IB.
We can change the height of the footer view run time by the following code:
func methodToChangeTableViewFooterHeight()
{
var footerView:UIView = self._tableView.tableFooterView! as UIView
var frame:CGRect = footerView.frame
frame.size.height = self.heightCollectionCS.constant + 10
footerView.frame = frame
self._tableView.tableFooterView! = footerView
}
Here , self.heightCollectionCS.constant is the height constraint for our Collection View.
We can use text content height on that place.
You may try to set again the footer view each time you footer height changes, to inform the table it should change the footer height. Or use inset. From within the footer view:
SetNeedsLayout()
LayoutIfNeeded()
ownertable.TableFooterView = this
Sorry about that, misread that question long ago. You can access the footer directly through the tableview's property tableFooterView.
What you could do is create your default footer in a xib or in your viewDidLoad:. Once you need to increase the size of the footer, you can pull out the UIView from that property and edit its frame if necessary to make it larger.
So make sure the tableFooterView gets assigned a UIView because it is nil by default. To just make the height taller, you can use self.tableView.tableFooterView.frame = CGRectMake(whatever rect you need);
I am trying to create a custom container view that has a UIImageView and a multiline UILabel as subviews. To make the view work nicely with autolayout, I am overriding intrinsicContentSize as below:
- (CGSize)intrinsicContentSize
{
return [self sizeThatFits:self.bounds.size];
}
The size calculated in sizeThatFits has the same width, and adjusts the height so that the label and image are not clipped. This works well, but I was surprised to see in the docs the following comment:
This intrinsic size must be independent of the content frame, because there’s no way to dynamically communicate a changed width to the layout system based on a changed height, for example.
If that is the case, what is the autolayout way to adjust the views current height based on its width and content? Should I be approaching this in a different way?
To answer my own question, it appears that there is not an autolayout suitable solution to this situation. Looking to UILabel for inspiration, the problem here has been solved with the addition of a property preferredMaxLayoutWidth, which can then be used as a constraining width during the intrinsic content size calculation. Any custom view would need to use something similar.
I think the doc means that, your containerView might have a placeHolderFrame as content frame.
intrinsic size should not be related to the content frame, but only to it's own subContent.
Your image and UILabel for example.
You should calculate both height and the width from the label and the image.
Which should be easy, since they all have intrinsic size.
Just my opinion...
I guess you could use UILabel's new preferredMaxLayoutWidth property to layout label correctly and use other approaches to layout other stuff.
Something like this:
- (void)layoutSubviews
{
...
[super layoutSubviews]; // get width from solved constraints
label.preferredMaxLayoutWidth = label.frame.size.width; // use it
[super layoutSubviews]; // update height of a label (probably intrinsicContentSize)
...
}
Align the bottom edge of the containing view with both the image and the label.
[self alignBottomEdgeWithView:labelView predicate:#"10"];
Details in http://code.dblock.org/ios-uiview-with-an-image-and-text-with-dynamic-height.