I have created a storyboard as in the image. In that I have set the vertical spacing between the red view and tableView is around 20. Its working good.
But when i tried to change the height of red view then the tableview should comes up with the constraints I added but the table view is remained constant at the same place.
I used the following code
redView.frame.size = CGSize(width: 1008, height: 0)
self.view.layoutIfNeeded()
Whenever you are adding a constraint to any object on storyboard then onwards that object's frame will be maintained by autolayout engine. This is the primary design principle I guess. In this case what you can simply do is that just make an outlet of the desired constraint which you want to update and update that constraint within your code. For your understanding I am attaching a screenshot.
Now after making a constraint outlet it must look like as follows.
#IBOutlet weak var containerViewHeight: NSLayoutConstraint!
Now to update the height you just have to do one thing.
containerViewHeight.constant = 0
In my opinion if this update is not working inside viewDidLoad or viewWillAppear then please update inside viewDidLayoutSubviews because when viewDidLoad get called then iOS doesn't apply the layout properly.
In my case what I will do is -
override func viewDidLayoutSubviews() { //This ensures all the layout has been applied properly.
super.viewDidLayoutSubviews()
containerViewHeight.constant = 0
}
Note: - To achieve some nice animation effect during layout changes you can apply this constraint update inside an UIView animation block like as follows. But remember, to see the effect nicely you have to call this after presenting the view properly like after viewDidAppear etc.
UIView.animateWithDuration(1.0, delay: 0.2, usingSpringWithDamping: 8.0, initialSpringVelocity: 1.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
containerViewHeight.constant = 0
self.view.layoutIfNeeded()
}, completion: nil)
Sorry for any mistake.
Update/Edit is most welcome.
Hope this helped. Thanks
You should be using Autolayout to manipulate the redView's height. Modifying its frame's size is not Autolayout. Modify its height constraint (if it exists)
You need to either use constraints or changes to frames! You cannot do both!
What you need to do is to make an outlet reference to your constraint and change its constant-value instead.
If you placed the code in viewdidload or viewwillappear, you will find issues like this. You need to put the code after all the constraints are loaded.
Also, make an object of the height constraint and name it for example redViewHeight.
and change its value by: redViewHeight.constant = 0
It will work!
Replace the constraints for the views with the following.
Redview
Top constraint to ParentView
Leading Constraint to ParentView
Trailing constraint to ParentView
Height Constraint
TableView
Top constraint to RedView
Leading Constraint to ParentView
Trailing constraint to ParentView
Bottom constraint to ParentView
Now wire an IBOutlet for the HeightConstraint of RedView and modify its constant value. The UITableView will adjust its height as desired.
Related
I have a nested collectionView which I would like to get the readableContentGuide for after rotation in order to set the content inset correctly.
This is what it looks like:
I have already tried to subclass the collection view and get the value from layoutMarginsDidChange, traitCollectionDidChange, and layoutSubviews.
However the value I get there is always the previous value (i.e when I'm in portrait I get the landscape value and vice versa)
I have also tried to set the inset in the collectionView's collectionView(_:layout:insetForSectionAt:).
Currently, the only solution that seems to work is observing the bounds of the collection view, but that feels a bit hacky.
Any thoughts on how this can be done?
If you are using Autolayout on storyboard you should activate "Follow Readable Width" option for the superview. First, make sure that the collection view is attached to the superview's margins. Then go to the superview and open Size Inspector, and select the option:
Thanks matt for the answer.
For programmatic Autolayout you don't need margins, just attach the collection view to the readableContentGuide of the superview. Like this:
let cv = collectionView
// Guide of the superview
let readableGuide = view.readableContentGuide
NSLayoutConstraint.activateConstraints([
cv.topAnchor.constraintEqualToAnchor(readableGuide.topAnchor),
cv.bottomAnchor.constraintEqualToAnchor(readableGuide.bottomAnchor),
cv.rightAnchor.constraintEqualToAnchor(readableGuide.rightAnchor),
cv.leftAnchor.constraintEqualToAnchor(readableGuide.leftAnchor)
])
If you prefer frame-based programmatic layout you don't need to use layoutMarginsDidChange, traitCollectionDidChange of the superview, neither observe bounds. The best place for layout code is viewWillLayoutSubviews() func of the controller. This will handle any bounds changes including interface rotations.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
collectionView.frame = view.readableContentGuide.layoutFrame
collectionView.collectionViewLayout.invalidateLayout()
}
Here I explain why we need to invalidateLayout().
I'm trying to animate constraints in my ViewController.
In order to do that, I added this line to my code:
I'm just trying to change the height of a UIView() (from 0 to 100)
barHeight.constant = CGFloat(100)
UIView.animate(withDuration: 2) {self.view.layoutIfNeeded()}
The problem is that, with that line, all of the constraints are animated, and it's not what I would like.
Do you know how I could animate only specific constraints and not others?
Call layoutSubviews before you edit the constraint you want to animate. This will cause any pending layout updates to be applied without any animation and then you can change the next constraint with animation.
Like so:
self.view.layoutSubviews()
barHeight.constant = CGFloat(100)
UIView.animate(withDuration: 2) {self.view.layoutIfNeeded()}
I am making a card app and I need to make an animation so that a card would change its constraints to move to another place. How would I do this for a UIImage.
Hook the leading , trailing , CenterX OR CenterY constraint of the UIImageView and do this
self.imageLeadCon.constant = // value
UIView.animate(withDuration: 3.0 , animations: {
self.view.layoutIfNeeded()
// animation
}, completion: { _ in
// completion
})
I think you're confusing UIImage with UIImageView. The first one is the image's data representation, the second one is the View that displays the UIImage.
So I guess you want to move around the UIImageView. To do that obtain a reference to the constraints (e.g. by ctrl-dragging from the constraint in the storyboard to your UIViewController instance).
After that you can update the constraint in an animation block like here:
// Make sure the view is laid out as Mischanya Schtrigel stated
self.view.layoutIfNeeded()
UIView.animate(withDuration: 0.5) { // duration in seconds
myConstraint.constant = 50 // update your constraint here
self.view.layoutIfNeeded()
}
Contrary to the other answers, I like putting the constraint changes in the animation closure to make it more clear what is going to be animated.
First of all you have to drag and drop your constraint into your view controller, to make an outlet. The same way you are doing it with UIImageView or UITableView, etc.
Actually, for those who have watched WWDC (2014 I guess), Apple have explained how to animate UI in the proper way.
Firstly, you have to call layoutIfNeeded() method to make sure that everything on your view have laid out. After you can change your constraint and right after that in your animation block you call layoutIfNeeded() method to layout your constraint with the new value.
So code should look like that:
view.layoutIfNeeded() // to make sure your view have laid out
self.constraint.constant = //change your constant to any value
UIView.animate(withDuration: 1.0) {
self.view.layoutIfNeeded() //layout your constraint with the new value
}
Background:
I have a normal UIView in Storyboard (called statusView) which has a height of 30, and four constraints pinned to the leading, trailing, top space to the superview, and bottom space to the view below it.
Problem:
My goal is to alter the height of statusView, including animating the frame changes when the user performs an action. As such, statusView could be as short as 0 or as tall as 100.
My expectation is that, given statusView is constrained to the superview and its nearest neighbor below, it should automatically "push" the views below when I alter its height.
Just to test, in viewDidAppear, I call the following:
self.statusView.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 100.0)
self.statusView.setNeedsDisplay()
Issue: However, the height of 30 remains for statusView, despite me setting it to 0.
Edit: posted Storyboard settings
This is the setting for statusView:
If you are using autolayout and want to set an explicit height for a view in your hierarchy, you should use autolayout to do it. I am assuming there is a height constraint set up in interface builder.
Create an IBOutlet for the constraint and link it up in interface builder.
#IBOutlet weak var statusViewHeightConstraint: NSLayoutConstraint!
Then, assuming this is just a size constraint with a constant for the height, just change it in your code:
statusViewHeightConstraint.constant = 100
You can animate this change by wrapping a layout call in an animation block right after modifying your constraint(s). Something like this:
UIView.animateKeyframes(withDuration: 0.2, delay: 0.0, options: [.beginFromCurrentState, .calculationModeCubic], animations: {
self.view.layoutIfNeeded()
}, completion: nil)
I have a view controller with two views (greenView, redView) and a switch:
I have a constraint on the redView top space of 50px from greenView. Here are all the constraints on redView:
I have a switch that when tapped toggles the height of greenView from 50px to 100px.
When I launch the app, the views are laid out as I want, but when the switch is tapped, the greenView frame changes from a height of 50 to 100, the redView doesn't do what I would expect - which is to shift it's y position 50px down to maintain the top space constraint it was assigned. I have an inequality constraint put on the bottom space of redView so there are no conflicts, and I am also calling [self.redView layoutIfNeeded].
Here's the relevant code:
- (IBAction)switchTapped {
if (theSwitch.isOn) {
greenView.frame = CGRectMake(0, 30, 320, 100);
[redView layoutIfNeeded];
} else {
greenView.frame = CGRectMake(0, 30, 320, 50);
[redView layoutIfNeeded];
}
Why isn't redView's y position being updated?
I have watched WWDC videos, but they seem to mention calling layoutIfNeeded in passing. What am I missing?
Here's what it looks like when launched:
And here it is when the switch is tapped:
A couple of pointers:
layoutIfNeeded assumes that you have previously called setNeedsLayout. Use setNeedsLayout instead and your app will update the layout as soon as appropriate.
Setting a frame generally does not work with autolayout. Set or change a height constraint instead. This is something you should do in code, mros had a good suggestion for this.
I find that inequality constraints are usually not the best solution, since they often lead to ambiguous layout. Instead, set an equality constraint, but lower the priority (e.g. to 500). The autolayout system will try to honor the constraint as much as possible, but will prioritize other constraints. (By default, all constraints have 1000 priority.)
There are a couple things that are causing problems. The first thing is that autolayout is supposed to ignore the object's frame. If you want to change the height of something, you change its height constraint, not the frame height. This is because constraints are applied after your layout and would override any frame changes. In addition, the red view looks like it is constrained to your superview "view" not "greenView". As far as editing the constraints, you can set up an IBOutlet and then modify the constant property of the UILayoutConstraint object.