I created header for tableview(!not for the section) in story board with auto layout.
I want to have ability to show/hide it.
How can I do it?
I tried this code:
self.tableView.tableHeaderView.hidden = !flag;
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = flag? k_HEIGHT_TableViewHeader : 0;
self.tableView.tableHeaderView.frame = frame;
It works , But it gives me a lot of auto layout errors.
Also I can set it to nil. If so, how can I load header from storyboard?
Have you tried using [tableview reloadData] when you decide to hide/ show?
Because you created it with auto layout, rather than changing the frame, you should link the constraint for the height on your tableHeaderView from the storyboard to your class. Then when you want to collapse it, set the constraint.constant = 0 and expand it again by setting constraint.constant = k_HEIGHT_TableViewHeader. You will need to call layoutIfNeeded on the view afterwards as well.
This change can also be easily animated by wrapping the layoutIfNeeded in a UIView animation block
[UIView animateWithDuration:0.5 animations:^{[self.view layoutIfNeeded];}];
Auto Layout Animation Reference
Related
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 have a UIView called containerView.
I add this as a subview to a controller view's root view. I have programmatically added a few constraints to it (I centered it and made the width a few points from the superview's width).
I have added a few UILabels to the containerView as subviews. The height of the UILabels dictate the height of the containerView.
When the user taps the screen, the containerView is moved up from CGRectOffset() and once the animation is complete, it is moved back to the original position.
CGPoint absolutePoint = self.containterView.frame.origin;
self.containerYConstraint.constant = -absolutePoint;
[UIView
animateWithDuration:0.5
animations:^
{
[viewForUpdate setNeedsUpdateConstraints];
}
completion:^(BOOL finished)
{
self.containerYConstraint.constant = 0;
[viewForUpdate setNeedsUpdateConstraints];
[self.containerView.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)]; // Remove all subviews
}];
I need to remove the UILabels I have put in there as subviews and replace them with different labels. However, the moment I remove one of the UILabel's the entire containerView goes missing (I set the background as red so I can see it). I remove all the subviews in the example but when I try to just remove one the same effect occurs.
Why does this occur? Does this have something to do with auto layout? Also if I want to recenter it after I remove one of the UILabels, how do I re-do the constraints?
Modifying frames when using AutoLayout is a no-no. Once you begin using AutoLayout you're effectively telling the system that you want it to set the frames for you.
Instead of animating the frame directly, create properties pointing to your constraints and animate those constraints.
In a UIViewController on a storyboard, I have a UITableView that is sized specifically to have two rows in one section with no header or footer, i.e. the height is 88.0f. There are some cases when I want to add a third row. So in viewWillAppear:animated: (and other logical places) I set the frame to be 44.0f logical pixels higher:
CGRect f = self.tableView.frame;
self.tableView.frame = CGRectMake(f.origin.x, f.origin.y, f.size.width, f.size.height + 44.0f);
NSLog(#"%#",NSStringFromCGRect(self.tableView.frame));
Nothing controversial; pretty standard resize code, and yet... It doesn't work! The tableView height doesn't change visually. The NSLog statement reports the height I expect (132.0f). Is this because I'm using Storyboards? I'm not sure why this isn't working.
Set an auto layout constraint for the height of the table view in your storyboard. Then connect the constraint to an outlet in your view controller so you can access the constraint in your code. Have the constraint be set to 88. When you want to change the height of the table view, just change the constraint's constant to 132.
You can modify the frame only after the call to layoutSubviews is made, which occurs after viewWillAppear. After layoutSubviews is called on the UIVIew you can change the dimensions.
As Gavin suggests, if you have the autolayout enabled you can add the constrains to the UITableView via storyboard, connect the height constraint and modify its value as follow:
constraint.constant = 132.0f
Otherwise if you have the autolayout disabled you can simply change the frame updating the height, but putting the code in a different method, for example viewDidLoad:.
recently I'm try to do what you've do. And I got same problem, tableview height won't change. Now I got the solution, you need to call layoutSubviews after change the frame. And it work on me.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
tableView.frame = CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y, tableView.frame.size.width, tableView.frame.size.height + 44.);
[tableView layoutSubviews];
}
don't place it in viewDidLoad or viewWillAppear: because even layoutSubviews is called, the frame won't change. place it on viewDidAppear:
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];
I've never worked with autolayout constraints before. I have a small new app I'm working on and noticed that the NIB's views are defaulting to autolayout. So, I figured I'd take the opportunity to work with it and try to figure out where Apple is going with this.
First challenge:
I need to resize an MKMapView and I'd like to animate it to the new position. If I do this the way I'm used to:
[UIView animateWithDuration:1.2f
animations:^{
CGRect theFrame = worldView.frame;
CGRect newFrame = CGRectMake(theFrame.origin.x, theFrame.origin.y, theFrame.size.width, theFrame.size.height - 170);
worldView.frame = newFrame;
}];
...then the MKMapView will 'snap' back to its original height whenever a sibling view gets updated (in my case a UISegmentedControl's title is being updated [myUISegmentedControl setTitle:newTitle forSegmentAtIndex:0]).
So, what I think I want to do is change the constraints of the MKMapView from being equal to the parent view's hight to being relative to the top of the UISegmentedControl that it was covering: V:[MKMapView]-(16)-[UISegmentedControl]
What I want is for the MKMapView height to shorten so that some controls beneath the map view are revealed. To do so I think I need to change the constraint from a fixed full size view to one where the bottom is constrained to the top of a UISegmentedControl...and I'd like it to animate as view shrinks to new size.
How does one go about this?
Edit - this animation is not animating though the bottom of the view does move up 170 instantly:
[UIView animateWithDuration:1.2f
animations:^{
self.nibMapViewConstraint.constant = -170;
}];
and the nibMapViewConstraint is wired up in IB to the bottom Vertical Space constraint.
After updating your constraint:
[UIView animateWithDuration:0.5 animations:^{[self.view layoutIfNeeded];}];
Replace self.view with a reference to the containing view.
This works for me (Both iOS7 and iOS8+). Click on the auto layout constraint you would like to adjust (in interface builder e.g top constraint). Next make this an IBOutlet;
#property (strong, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;
Animate upwards;
self.topConstraint.constant = -100;
[self.viewToAnimate setNeedsUpdateConstraints];
[UIView animateWithDuration:1.5 animations:^{
[self.viewToAnimate layoutIfNeeded];
}];
Animate back to original place
self.topConstraint.constant = 0;
[self.viewToAnimate setNeedsUpdateConstraints];
[UIView animateWithDuration:1.5 animations:^{
[self.viewToAnimate layoutIfNeeded];
}];
There is a very good tutorial from apple itself that explain how to use animation with autolayout.
Follow this link and then find the video named "Auto layout by example"
It gives some interesting stuff about autolayout and the last part is about how to use animation.
I have made this small demo available. It shows how auto-layout constraints can be changed and animated in a very simple example. Simply take a look at the DemoViewController.m.
Most people use autolayout to layout items on their views and modify the layout constrains to create animations.
An easy way to do this without a lot of code is creating the UIView you want to animate in Storyboard and then creating a hidden UIView where you want the UIView to end. You can use the preview in xcode to make sure both UIViews are where you want them to be. After that, hide the ending UIView and swap the layout constraints.
There is a podfile for swapping layout constrains called SBP if you don't want to write it yourself.
Here's a tutorial.
No need to use more IBOutlet reference of the constraint instead of this you can directly access or update already applied constraint either applied by Programmatically or from Interface Builder on any view using the KVConstraintExtensionsMaster library. This library is also managing the Cumulative behavior of NSLayoutConstraint.
To add Height Constraint on containerView
CGFloat height = 200;
[self.containerView applyHeightConstrain:height];
To update Height Constraint of containerView with animation
[self.containerView accessAppliedConstraintByAttribute:NSLayoutAttributeHeight completion:^(NSLayoutConstraint *expectedConstraint){
if (expectedConstraint) {
expectedConstraint.constant = 100;
/* for the animation */
[self.containerView updateModifyConstraintsWithAnimation:NULL];
}
}];