Animate a view containing subviews with autolayout - ios

This is the setup:
A UIView created on Interface Builder, linked to an IBOutlet variable (_vAbout)
A constraint for this view that we want to animate, linked to an IBOutlet variable (_ctrBottomAboutView)
I am using this code to animate:
_ctrBottomAboutView.constant = -100;
[UIView animateWithDuration:0.5 animations:^{
[_vAbout layoutIfNeeded];
}
My problem is: whenever the view has any subviews in it, the animation doesn't work. However, if the view has no children, the animation works correctly.
Do you have any idea of a solution? I have tried everything: adding and removing constraints instead of modificating the constant value, adding constraints to the subviews on Interface Builder...

After some experiments starting from the ground with an empty project, this is what I've found:
Given A the view we want to animate and B its superview
It's very important to keep in mind that the view that receives the layoutIfNeeded message is the view that owns the constraint.
In the case of NSLayoutAttributeWidth and NSLayoutAttributeHeight the owner of the constraint is actually A, but in all the other cases, the view that owns the constraint is B
HOWEVER
If A does not have any subviews, we can call [A layoutIfNeeded] at any time on our code and it will work
If A has one or more subviews but we start the animation on viewDidLoad we can call [A layoutIfNeeded] and it will work

Related

Adding ScrollView to ViewController with 2 custom Views

My viewController has one view with images and labels and one textView
Im new in objective c.
My problem is to add ScrollView in my ViewController with 2 custom views(UIView and UITextView).(image in the link) I have tried many things posted here in Stack but nothing works for me.
Thank YOU!
Here is what i have :
self.scrollView.contentSize=self.scrollView.frame.size;
self.scrollView.frame=self.view.frame;
[self.view addSubview:self.scrollView];
Adjusting view's frame was the technology of 5 years ago. You should never set the frame manually, not anymore. Instead start learning Autolayout and Constraints.
These tutorials may help:
https://www.raywenderlich.com/115440/auto-layout-tutorial-in-ios-9-part-1-getting-started-2
https://www.appcoda.com/auto-layout-guide/
You are setting the content size equal to the frame size before you actually set the frame, so it's probably just 0.
You need to just switch the calls around:
self.scrollView.frame=self.view.frame;
self.scrollView.contentSize=self.scrollView.frame.size;
[self.view addSubview:self.scrollView];
The other thing to keep in mind is that because you are setting the frame of a nested view to the frame of its superview, your layout will break (or at least not do what you expect), if the origin of your superview ever changes. If the origin is 0, 0, then you are fine for the moment, but otherwise you may want to set the subview (scrollView) frame to be equal to the superview (self.view) bounds instead of the frame, like this:
self.scrollView.frame=self.view.bounds

Flicker while using layoutIfNeeded on containerview

I have a controller in storyboard that consist of two container views. I set the vertical distance between the two containers to zero.
What I want is to change the height constraint of one of the container at run time.
Here is the code for changing the constraint:
[UIView animateWithDuration:kAnimationDuration animations:^{
self.offeringContainerHeightConstraint.constant = [SJDataManager shared].offeringItems.count * kOfferingCellHeight + kOfferingHeaderHeight;
[self.view layoutIfNeeded];
}];
The issue is, the other container view moves before so there is blank area between the two containers until the animation completes.
I want that two container views should change their constraint value in synchronisation so that this flicker can be removed.
I have finally found the solution for this. Actually it was the difference between the expectedHeightForRowAtIndexPath and heightForRowAtIndexPath. As the height returned by these two methods are different that is why I am getting the flicker.

UIView position does not respect NSLayoutConstraint when altered from viewDidLoad

I have two views, one visible (grey) and one hidden (red). The visible view is constraint to the superview top with a constant of 32, and the other constraint to the superview top with a constant of 16.
The top of the visible view is also constrained to the bottom of the hidden view, with a constant of 8. This constraint has a priority of 749, because it is initially unsatisfiable.
The idea is that the visible (grey) view should be 32 points below the superview normally, but when the hidden (red) view is made visible, the original (grey) view should be 8 points below the other view (red).
I'm achieving this by keeping a strong reference to the 32-point constraint, and making it active/inactive as I'm hiding/unhiding the view (red).
Here's a picture of my layout:
This works very well normally, but I've been trying to set constraint.active = NO and redView.hidden = NO in viewDidLoad, as I need the textfield to display an error if a certain condition has not been met. For some reason, this does not work. The red view is displayed as expected, but the second view (grey) does not move down as it should (it does not respect the 749 constraint, even if the main constraint is no longer active). Here's a picture of the result:
However, I made the code to inactivate the constraint and display the view run after a small delay (using dispatch_after();), and then it suddenly works as expected:
My question is: why doesn't the view respect the constraint and move down when it is run immediately from viewDidLoad? Why does it suddenly work after a small delay? Is this a decent solution of achieving my goal, given I can get it to work properly?
call it in viewDidLayoutSubviews methode not in viewdidload.
autolayout it take some time to load so call it in viewDidLayoutSubviews.
While using autolayout - (void)viewDidLoad will return the frame which you gave in storyboard.
You have to use - (void)viewDidLayoutSubviews to update the frame according to constraints.
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
//Replace self.view by your view whose constraints need to update
[self.view layoutIfNeeded];
}

Autolayout issues when adding a new view into a hierarchy

I'm struggling big time with a couple of scenarios when i have a UIView with constraints applied via IB and adding a new UIView into it's view hierarchy. Here's how it goes:
I have a full screen UITableView with the following constraints applied so it scales nicely through all the resolutions of iOS devices:
Now i'd like to add a UIView above the UITableView acting as a sort-of toolbar.
Right now the only possible way i succeeded in adding this view is adding it on top of the TableView, so that it covers the top of tableview. What i'd like to achieve is the newly added UIView pushing table down so both are visible. I tried several things including adding a container view in IB just above the tableview however that didn't work at all.
So my question is: is there a way to dynamically edit, remove and add new constraints to the view hierarchy, ideally supporting animation?
You can create IBOutlets for a constraint, just like any view.
Each NSLayoutConstraint object has a constant property that you can set anytime in code (it's the value of the constraint).
So you would create both views on Interface Builder, and constraint the top of the table view to the bottom of the new view.
The new view will have a set height constraint, and you create an outlet to that height constraint to make that view appear or disappear in code.
There are other possible solutions but I think this one is the easiest.
The code would be something like this:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *topHeightConstraint;
To set the value:
self.topHeightConstraint.constant = 60;
and to animate it:
[UIView animateWithDuration:0.6 animations:^{
self.topHeightConstraint.constant = 60;
[self.view layoutIfNeeded];
}];

What happens with constraints when a view is removed

The question I have is simple but I couldn't find any information in the documentation.
What happens with layout constraints when a view is removed from the view hierarchy (or moved to another view)?
For example, let's have container C with subviews A and B. Container C holds some constraints. Then we call [A removeFromSuperview]. What happens with the constraints for A?
What then happens if we add A to C again?
The constraints are removed. If you add A again, you will have to make new constraints for it, or if you save the constraints before you remove A, you can add them back. When I do something like this, I save the constraints like this for a view called view1:
self.portraitConstraints = [NSMutableArray new];
for (NSLayoutConstraint *con in self.view.constraints) {
if (con.firstItem == self.view1 || con.secondItem == self.view1) {
[self.portraitConstraints addObject:con];
}
}
Since I had this question too, I checked the Apple Docs just for kicks, and it turns out that it is documented that the constraints are removed.
The documentation for the UIView removeFromSuperview method states:
Calling this method removes any constraints that refer to the view you
are removing, or that refer to any view in the subtree of the view you
are removing.
I'm not sure if this was documented last year when the original question was posted, but I just thought I'd share this information in case anyone needed it...
Be aware though, that if you have two independent parent views A and B, and a subview C, where C is currently a subview of A, with appropriate constraints, that calling [B addSubview:C] will NOT clear any constraints relating to A and C, and auto layout will start throwing exceptions, because those constraints no longer relate to views in the same hierarchy.
You will need to call [C removeFromSuperview] explicitly to remove the constraints, before adding C to B.
This is true on Mac OS X - I haven't checked iOS
The constraints are also removed when you [A removeFromSuperview]
They are forgotten and adding A to C again adds no constraints.
They are removed too, you can do a simple test. Pick up a view SUBVIEW and create costraints that constraint SUBVIEW to follow its superview resizing (like attched to to superview edges). To do that you add SUBVIEW as a subview to this CONTAINERVIEW and add as constraints something like that:
V:|-[SUBVIEW]-|
H:|-[SUBVIEW]-|
These constraints should be added to SUBVIEW superview, thus CONTAINERVIEW.
If you remove SUBVIEW by simply checking all the CONTAINERVIEW constraints you could see that two aren't around anymore.
This question also can be proved by interface builder. When drag and drop a UIView on the ViewController add constraints then remove the UIView, you can see the blue constraints disappear.

Resources