Animating/Moving Views Under Usage of Autolayout

Animating/Moving views under usage of Autolayout

You must change the constraints if you are using autoLayout. The way that is suggested is to make an outlet in your view controller of the constraint, then you change the constant of the constraint. If you have the time i would definitely recommend going here and watching "Auto Layout by Example" or "Best Practices for Mastering Auto Layout". They helped me out a lot. I guess the point to take away is that with autoLayout, you no longer think in terms of frames. So setting the center just doesnt work with auto layout. It's all about how views are related to each other.

AutoLayout animate sliding view out and sliding other views over to take its place

I'm not an Apple engineer, so I don't know the ins-and-outs of this, but I've seen it often enough.

As a general rule ... when animating constraints we want to allow auto-layout to manage the view hierarchy from a "top down" standpoint.

If you change constraints and tell a subview to layoutIfNeeded, it appears that the superview either isn't made aware of what's going on, or the timing causes issues.

If you change your animation block to [weakSelf.superview layoutIfNeeded]; that should fix the "jumping" problem:

    __weak MyShelf *weakSelf = self;
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionAllowAnimatedContent|UIViewAnimationOptionAllowUserInteraction
animations:^{
view.alpha = 0.0;
view.center = CGPointMake(-weakSelf.itemSize.width, view.center.y);
[weakSelf updateHorizontalPositions];

// tell the superview to initiate auto-layout updates
//[weakSelf layoutIfNeeded];
[weakSelf.superview layoutIfNeeded];

} completion:^(BOOL finished){
[view removeFromSuperview];
//only reorder the views vertically if the one being removed was the top-most view
if (wasInFront) {
[weakSelf updateVerticalPositions];
}
}];

Auto Layout views with animation

When you want to make a dynamic View don't change frame but change constraint's constant as frame doesn't push the parent down , so make the height constraint of the view you want to animate as IBOutlet and control it's constant value

You can try

class CustomLayout: UIView {

var heightCon:NSLayoutConstraint!

override init(frame: CGRect) {
super.init(frame: frame)
setup()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}

private func setup() {
onLayout()
}

public func onLayout() {
print("\(frame.size.height)")
let MARGIN: CGFloat = 10
for i in 0 ..< subviews.count {
let child = subviews[i]
if i == 0 { // slide layout
child.translatesAutoresizingMaskIntoConstraints = false
child.topAnchor.constraint(equalTo: topAnchor, constant: MARGIN).isActive = true
child.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
child.widthAnchor.constraint(equalToConstant: frame.size.width - (MARGIN * 2)).isActive = true
heightCon = child.heightAnchor.constraint(equalToConstant: child.frame.size.height)
heightCon.isActive = true
}

//

    public func slideAnimation() {
print("\(HEIGHT)")

isClosed = !isClosed

let ss = self.superview as! CustomLayout

ss.heightCon.constant = self.isClosed ? 0 : self.HEIGHT

UIView.animate(withDuration: 1, delay: 0, options: .curveEaseInOut, animations: {
ss.layoutIfNeeded()
}, completion: nil)
}

UIView animateWithDuration Animating Auto-Layout Views Outside of Animation Block

This is happening because calling layoutIfNeeded in the animation block is causing all pending layout changes to have their new values calculated for use by the animation. In fact, this is the way to animate changes with constraints, to set them outside the animation block and then call layoutIfNeeded in the block; the new values are calculated and used for the animation.

Fortunately the solution seems to be as simple as could be hoped, to call layoutIfNeeded before doing the animation, before calling it again in the block. According to this answer and others this is in fact Apple's official recommendation, though the link they provide to apple.com is broken.

(By the way, about your hack, there are some cases where you need to stagger changes to after UIKit has completed its layout pass, for example to do something on a UITableView after reloadData has completed. The "correct hack" is to do a UIViewAnimation, then in its completion block do the second animation, keeping the sequencing it in UIKit land.)

What is the proper way of animate views when constraints applied?

The problem here, is that NSLayoutConstraint toggling works like properties in the sense that they are nothing but values which can be switched on/off, and alternated by playing with this toggling and other references to other possible values they can have. There's no real way of going around this that I know of unfortunately, and in fact i myself have built a small library similar to Anima, and it works rather well if you respect the NSLayoutConstraints' nature.

The proof of this is that under the hood of this Anima library, it's simply storing the animation points declared inside of the chain (inside Enum values in fact), and applying them as the animation moves along. Regardless, you should never re-set translatesAutoResizingMaskIntoConstraints to true when working with NSLayoutConstraints.
The second reason for this is that Constraints are the basis for all iOS frame operations, including .frame, and animations (which is why Anima works so well from the looks of it).
I wrote a post on this recently, but as I explain by referencing Apple:

Your problem is that when translatesAutoresizingMaskIntoConstraints is
called, methods like .frame or .frame.size are ignored/overriden
(depending on when you use them, before or after
translatesAutoresizingMaskIntoConstraints). As described by Apple:

Note that the autoresizing mask constraints fully specify the view’s
size and position; therefore, you cannot add additional constraints to
modify this size or position without introducing conflicts. If you
want to use Auto Layout to dynamically calculate the size and position
of your view, you must set this property to false, and then provide a
non ambiguous, nonconflicting set of constraints for the view.

UPDATED
Otherwise, try not to set translatesAutoResizingMaskIntoConstraints to true with these views, by doing that you basically tell your controller to ignore your constraints, and to try to apply constraints based on the .frame or .frame.size or position values set on the UIView. Thus, making your custom constraints obsolete. If by stopping this, you still get the issue, it's probably a constraint value issue, of which i can't give you much more advice without any code unfortunately.

how to animate views change views hierarchy using autolayout?

The problem was in the constraints of view 3 which doesn't change after animation and when we wants to manipulate the view hierarchy, animation is stopped work properly. Matt said about that in his answer, also he provide a solution from his great book: http://www.apeth.com/iOSBook/ch17.html#_animation_and_autolayout
thank you for that Matt.

I change this solution a little and decide to post it here for anyone who will need this. But if you want to understand this better I highly recommend you to read chapter about animation from book, link is above.

First, we need to animate our movement of view 3, then change our constraints and only after this change the view hierarchy. In the book a found several ways to done this, but this one is seemed to me the best:

layout = -35.0; //  new constraint constant of self.view3

NSArray *constraintsForAnimation = self.myMainView.constraints; // all constraints of superview

NSUInteger indexOfconstraintsAttribute = [constraintsForAnimation
indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){ // get index of constraint of view 3

NSLayoutConstraint *constraint = obj;

return (([constraint.firstItem isEqual: self.view3])) ? idx : NO; // return this index if we found constraint of view 3

}];

constraint.constant = layout; // changing the constraint of view3

[UIView animateWithDuration:0.3 animations:^{ // animation
[self.view3 layoutIfNeeded]; // apply new constraints to view3
}];

[self.myMainView exchangeSubviewAtIndex:3 withSubviewAtIndex:4];// toggle view 1 with view 2

We move our view 3 only with help of constraints, we don't set frame or centre view property.
Okay, we done with and this working like as I want. Hope this help you too.

How to make flip animations from one UIView to another when using Auto Layout?

Auto Layout can only be used to position and resize views. It cannot be used to produce the kind of Core Animation transform needed to produce the flip transition effect. So the short exact answer is no, there is no way to animate this kind of flip by animating constraints.

Using the show/hide transition option

However, there is a simple way to modify the code you are already using so that it is consistent with Auto Layout. The way to do it is (1) to add both your firstView and secondView to your view hierarchy before you order the animation, (2) to ensure that you've added Auto Layout constraints that define the layout of both those views, and (3) to add an option to the animation so that you are only showing/hiding the two views, rather than tearing down and setting up a new view hierarchy.

In other words you want something like:

        // assert: secondView added to view hierarchy
// assert: secondView.hidden == true
// assert: secondView has correct constraints
[UIView transitionFromView:firstView
toView:secondView
duration:0.6
options:UIViewAnimationOptionTransitionFlipFromLeft | UIViewAnimationOptionShowHideTransitionViews
completion:nil];

Why is this what's needed? The reason is that, without the UIViewAnimationOptionShowHideTransitionViews option, the method transitionFromView:toView:duration:options:completion: will actually manipulate the view hierarchy and add the new destination view. If Auto Layout is engaged, then it won't be laid out correctly since it won't have constraints.

You can see an example project showing this approach working here: https://github.com/algal/AutoLayoutFlipDemo

Using view hierarchy manipulation

Alternatively, you can also use your existing call to transitionFromView:toView:duration:options:completion:. But if you're not going to just show a destination view that already had constraints in place, then you need to use the completion block to add those constraints, as follows:

    [UIView transitionFromView:firstView
toView:secondView
duration:0.6
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:^(BOOL finished) {
[self.view addConstraint:[NSLayoutConstraint
constraintWithItem:secondView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1 constant:0]];

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:secondView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1 constant:0]];
}];

A working example of this approach is here: https://github.com/algal/AutoLayoutFlipDemo2



Related Topics



Leave a reply



Submit