I had this structure on my storyboard (i have added some example name to easly understand):
UIView (the main view) "BlackView"
-UIScrollView (inside the main view) "OrangeView"
UIView "YelloView" (ContentView inside main scroll)
UITableView "BluView" (Inside and on the top of the contentView)
UIView "RedView" (Inside of the ContentView and on the same level of UITableview)
What i wanna achive? I want a table that grow proportionally to his rows number (the row height is fixed to 60). I also want an UIScrollview that grow in content_size and scroll ability accordingly to the heights sum of the BlueView and the RedView.
The autolayout constraints are:
UiScrollView "OrangeView": (top 0,trailing 0,bottom 0,leading 0)
UIView "YelloView": (top 0,trailing 0,bottom 0,leading 0) (equal width to main ed equal height to main "BlackView" , priority “low” for bottom)
UITabelView "BluView": (top 0,trailing 0,leading 0)
UIView "RedView": (Vertical space 2 to "BluView",trailing 0,leading 0,bottom 0 to YelloView)
I have a complete chain of constraints from top to bottom of the view. UITableView is free to grow because i haven't set directly his "height" constraint and uitableview setscrollingenabled is FALSE. The result is a tableview that don't grow.
I can produce the growing by code:
Table.frame=CGRectMake(BluView.frame.origin.x, BluView.frame.origin.y, BluView.frame.size.width, BluView.contentSize.height);
I works, the table frame and the "OrangeView" scroll content_size grow correctly but "RedView" doesn't respond to this frame change and it doesn't move to the new "table bottom" ad 2 of distance.
What i have already tried to refresh the constraints system:
[BlackView layoutIfNeeded];
[BlackView setNeedsUpdateConstraints];
[RedView layoutIfNeeded];
[ScrollContainer layoutIfNeeded];
[RedView layoutIfNeeded];
[RedView setNeedsUpdateConstraints];
[OrangeView setNeedsUpdateConstraints];
[RedView updateConstraints];
[OrangeView updateConstraints];
[BlackView updateConstraints];
[BluView setTranslatesAutoresizingMaskIntoConstraints:NO];
[RedView setTranslatesAutoresizingMaskIntoConstraints:NO];
I have called those methods before and after the uitable frame change, they doesn't refresh the RedView's y position. How i can obtain the desired behaviour? There's a way to create anothere type of "costraints tree" or maybe there's another way to refresh the position of the RedView after i have programmatically change the Tableview frame?
UITableView is free to grow because i haven't set directly his "height" constraint and uitableview setscrollingenabled is FALSE.
Setting setscrollingenabled to false doesn't tell the tableView to have the height of its content, it just disables the scroll behavior. So if your tableView is 200pt tall and you have a content of 500pt, you'll never see the 300pt at the bottom.
Add a height constraint to your tableView and update its constant with the tableView's contentSize.height every time the contentSize changes.
This way it will always have the size of its content, and because it's in a scrollView you'll still be able to scroll to see all the content.
PS: The yellow view is useless here if it's only a container for the blue and red views.
Related
I have the following view structure with its constraints:
UIView "parent"
UIScrollView (leading, trailing, top and bottom to superview)
- UIView "container" (leading, trailing, top and bottom to UIScrollView,
equal width and height so parent UIView)
- UIView "A" (leading, trailing, top to UIScrollView, height of 200)
- UIView "B" (top, leading and trailing to UIView A, height of 140)
- UIView "C" (top, leading and trailing to UIView B, height >= 88 "rest
of the screen until bottom", bottom to UIView "container")
"A" and "B" UIView's do not change its size but "C" does. Inside it, I am adding programmatically n "labels containers" UIView that have different heights depending on the content of m UILabel that they host.
Right now, I am calculating the size of the n UILabel with boundingRectWithSize: and I am sizing the height of their parent "labels containers" UIView that it is being added inside "C" UIView setting its height constraint to the sum of all UILabel.
Then, I resize "C" UIView height constraint so that it is equal to the sum of all added UIView.
This is working perfectly on all different screen sizes, portrait and landscape. The UIScrollView is showing all three "A", "B" and "C" subviews, having "C" n UIView that host m UILabel.
But now I am having troubles when rotating the device. I face the problem that I have to recalculate the size of all UILabel to change the height constraint of all the "labels container" UIView and change the "C" UIView height constraint so that it can fit everything without large blank spaces between all views.
So my question is: how can I achieve the same behaviour using exclusively Auto Layout?
Now I have to recalculate sizes and change height constraints so that everything adapts, but I would love that all UILabel resize themselves automatically and fit their content, then the "labels container" UIView resize to fit all the UILabel and then "C" UIView resizes automatically to fit the content.
Thank you in advance!
As per my knowledge you need to add TableView for your dynamic labels and to achieve this you have to follow below steps:
1step : Add ScrollView in your ViewController.
2step : Add ContainerView in ScrollView to manage scroll constrains.
3step : Add your "A" view with fixed size lets say 150px.
4step : Add your "B" view with fixed size lets say 150px.
5step : Add TableView for dynamic labels with fixed size 0px. At this step make Outlet of your tableView height Constrains we will use it in next step.
6step : Call your service and add labels in your tableView.
in this 6th step while you are adding labels in your table view means while you reload your tableView do below code for dynamic heigh constrains.
7step : Now as per your Row height adjust height of table view as mention below.
arrListArray = #[#"One",#"Two",#"Three",#"Four",#"Five",#"Six",#"Seven",#"Eight",#"Nine",#"Ten"];
int rowHeight = 44;
self.tblHeightConstraint.constant = (rowHeight * arrListArray.count);
now reload your constrains with below method:
[UIView animateWithDuration:0.1
animations:^{
// Called on parent view
[self.view layoutIfNeeded];
}];
it will increase your scrollView contain size automatically.
Show sample images :
View Constrains Stack :
Now as per your comment you have to add CollectionView in place of tableView and add TableView in CollectionViewCell with your specific design.
Hope this will helps!
Ok, I was doing three things wrong:
1st: I was calculating the height of the labels to set then the size of the "labels container" UIView using a height constraint. Height constraints not needed as Auto Layout manages everything.
2nd: I was modifying the "C" UIView constraint height manually by adding the height value of each "labels container" UIView added.
3rd: After fixing the first two steps, I forgot to set the last added UIView constraint with its parent view...
Now everything works as expected.
In my UITableViewCell, I have two UIViews stacked on top of each other. Let's call them Top and Bottom.
The Top view has leading, trailing, and top constraints to superview. It has a height constraint of 20.
The Bottom view has leading, trailing, and bottom constraints to superview. It has a height constraint of 20.
Top and Bottom have a vertical constraint.
What is the easiest way to programatically "hide" the Bottom View (and have the Top View touch the bottom of the superview)? I prefer not to create any more constraints, since I did design this in storyboard, and I prefer not to activate/disable constraints.
If you don’t need to target iOS 8 and below, the easiest way is to embed the two views in a UIStackView. You can then simply hide a view by setting its hidden property and the stack view will automatically update the layout:
The stack view automatically updates its layout whenever views are added, removed or inserted into the arrangedSubviews array, or whenever one of the arranged subviews’s hidden property changes.
Since your parent view is a table view cell, you may have to tell the table view to recalculate the cell heights (unless you’re using autosizing cells, then this may work automatically, I’m not sure). You can force a recalculation by sending the table view an empty beginUpdates/endUpdates pair:
tableView.beginUpdates()
tableView.endUpdates()
The right way:
The Top view has leading, trailing, and top constraints to superview. It has a height constraint of 20.
The Bottom view has leading, trailing, bottom constraints to superview and top constraint to Top view.
Than just make a property for height Constratint inside your cell:
#property (nonatomic, weak) IBOutlet NSLayoutConstraint *heightConstraint;
Than when you need to change the size, call this code:
self.heightConstraint.constant = 40;
[self.view layoutIfNeeded];
or with animation:
self.heightConstraint.constant = 40;
[UIView animateWithDuration:0.3 animations:^{
[self.contentView layoutIfNeeded];
}];
You can increase the height constraint of the top view to 40 and reduce the height constraint of the bottom view to 0. Personally I prefer to have the bottom view height constraint to 20 and add a constraint to the topView bottom equal to bottomView top. And if i want to hide the bottomView I just change the height constraint of the bottomView to 0.
Hope it helps. If you need I can post some pictures in Xcode.
I have a view (let's call it "contentView") that its height is determined by its subviews, which is mostly UILabels.
The UILabels content is dynamic and may grow or shrink at runtime.
The UILabels have numberOfLines = 0, and no height constraint, which allow their height to grow with the content.
The UILabels have 0 leading/trailing/top/bottom constraints to their neighbors and/or superview, as follows:
The result is having contentView's height equal the total height of all its sub UILabels (with their height being determined by the height of their text).
I now need to hide contentView on a press of a button (and show it again when the button is pressed again).
Also note, that while animating, if there are other views below "contentView" they need to move in to take the empty space (and move back out when the button is pressed again).
It seems the most natural animation is to change the height of contentView to 0, while animating the views under contentView up at the same time (with the reverse animation changing the height back to its original height).
Any suggestions on how to animate contentView's height?
Unfortunately I was not able to create a smooth enough animation for this layout, but I found a workaround.
Constraints change the layout while animating, and I needed the opposite effect - the layout had to remain fixed while the animation is in progress.
So I disabled the constraints for the duration of the animation, and that seemed to work well.
The full recipe is:
I disabled the constraints on contentView:
contentView.translatesAutoresizingMaskIntoConstraints = YES
I then did a simple frame animation. I made sure to calle layoutIfNeeded on superview to make sure the view that's under contentView moves up during the animation:
CGRect frame = view.frame;
frame.size.height = 0;
[[view superview] layoutIfNeeded];
[UIView animateWithDuration:0.5 animations:^{
view.frame = frame;
[[view superview] layoutIfNeeded];
}];
I also had to change some properties for the subviews to have them animate correctly (this step might not be needed. It depends to the base layout of the subviews):
for UITextView: scrollEnabled = YES
for UIButton: clipsToBounds = YES
At the end of the animation, on the completion block, I set contentView.translatesAutoresizingMaskIntoConstraints back to NO and restored the subviews properties:
for UITextView: scrollEnabled = NO
for UIButton: clipsToBounds = NO
(I ended up using UITextViews instead of UILabels because I needed the text to be selectable. I then set scrollEnabled to NO to size the textviews based on their content).
Yes: add a height constraint with a constant of 0.
You can use deactivateConstraints() to temporarily disable other constraints that might conflict with this, and activateConstraints() to re-enable them.
You could put the relevant constraints in an array programmatically, or use an outlet collection if you're using a storyboard.
I'm using Auto Layout in my iOS 7 project with the following view hierarchy
Main View
-Container View
---Button
---Button
---ImageView
-Banner View (iAd Banner View)
The Main View and Container View are full width and height of screen. I have Horizontal and Vertical Space Constraints on the Container View sticking to the main view (screen's height and width). And also the subviews of Container View are constrained to the button of the view with a 20px space.
My issue occurs when the Banner View is finally filled and placed at the bottom of the screen, which then I have the Container View subtract the Banner View's Height from its frame height to allow space for the Banner View to show. (code used below) The ideal outcome is the Container View to subtract the height and its subviews constraint update based on this new height ,but what end up happening is the iAD Banner View just overlays the view as shown in the picture.
Code for BannerViewDidLoadAd:
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
CGRect contentFrame = self.containerView.bounds;
CGRect bannerFrame = self.bannerView.bounds;
if (self.bannerView.bannerLoaded) {
contentFrame.size.height = self.containerView.frame.size.height - self.bannerView.frame.size.height;
bannerFrame.origin.y = contentFrame.size.height;;
} else {
bannerFrame.origin.y = contentFrame.size.height;
}
[UIView animateWithDuration:animated ? 0.25 : 0.0 animations:^{
[self.containerView setFrame:contentFrame];
[self.containerView layoutIfNeeded];
self.bannerView.frame = bannerFrame;
self.bannerView.hidden = NO;
}];
[self.containerView updateConstraints];
}
Image of iAd overlaying Container View and it's SubViews
After you create the banner view in code (and add it as a subview of main view), you should add a 0 length spacing constraint between the bottom of the container view, and the top of the banner view (the banner view would need constraints to the two sides of the main view and a height constraint as well). The container view should have 0 length constraints to all four edges of the main view. You should make an IBOutlet to that bottom constraint, and animate that constraint's constant value by an amount equal to the height of the banner view (so it will shrink, and the banner view will move up with it due to its 0 length vertical spacing constraint). So, if the outlet to the bottom constraint was called bottomCon, and the height of the banner view was 100 points, you would animate like this:
[UIView animateWithDuration:animated ? 0.25 : 0.0 animations:^{
self.bottomCon.constant = 100;
[self.mainView layoutIfNeeded];
}];
There's no need to hide and unhide the view, since you will initially place it off the bottom of the screen anyway. Also make sure that you call [bannerView setTranslatesAutoresizingMaskIntoConstraints:NO] right after you create the banner view, or you'll get auto layout errors when you run the app.
The response from rdelmar was enough for me to get this working, but I'll add a few things. With auto layout on, there is no need to set the banner's size with setAutoresizingMask:UIViewAutoresizingFlexibleWidth (and currentContentSizeIdentifier is deprecated in iOS 6). Just create the banner object and then pin it into position using the procedure outlined by rdelmar and auto layout takes care of the horizontal sizing.
Here are the constraints I used:
// pin sides to superview
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[_bannerView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_bannerView)]];
// set height to a constant
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[_bannerView(==66)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_bannerView)]];
// pin contentView to bannerView with 0 length constraint
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[_contentView]-0-[_bannerView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_contentView,_bannerView)]];
I was concerned about setting a height constraint because the height of the banner will change depending on platform and/or orientation. But it doesn't seem to make any difference what value I set for the height constraint - the banner is always shown with the correct height, so I don't even bother setting it. I am assuming this because there is an intrinsic sizing to the height of the ad banners.
I have a UIViewController with a UICollectionView and a UIView at the bottom. The way I put it together is displayed in the image below
The yellow square is the UICollectionView and the red is the UIView. This works out just fine. But now I want to resize the UIView because it sometimes contains more info and needs to be bigger. So I tried this:
[self setFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height + 10)];
But this expands the UIView at the bottom and it is not visible. I guess this is because I do not have the correct constraints? I also tried to subtract the origin.y with the same amount and this works only the UICollectionView doesn't get resized with the new height. So how do I tackle this problem?
If you are using autolayout, you should not be setting the frame from your code. Instead you should modify the constant of a constraint that is causing your view to be the incorrect size. You can have an IBOutlet to that constraint and you can change it's constant property. Then call setNeedsLayout on your view controller's view
When setting constraints on your storyboard or in a xib file, animations perform animations on the constraints instead of the sizes and positions.
First create a outlet reference of the constraint which will change (in your case the top space of your UIView to the top layout guide) in the header file of your view controller.
When you want to animate a view, you now have to update its constraints and ask to layout the views.
For example :
[UIView animateWithDuration:0.2
animations:^{
viewYConstraint.constant -= 44;
[self.view layoutIfNeeded];
}
]
//Now don't forget to update constraints
[self.view updateConstraints];