How to make flip animations from one UIView to another when using Auto Layout?
My best guess will be that animation is performed via animating layer's ~transform, and that transform is applied from the point where anchorPoint position goes. You could try to solve it in two ways:
First: Make sure you position your views via center constraints, ie. align center of the views which are being transformed (not left or top or trailing and stuff). For example, if both views will have, say "horisontaly + vertically" centered in superview this (not sure) could help.
Second: Create a wrapper view, put those views as subviews and disable autolayout for them.
From How do I animate constraint changes?:
You need to call layoutIfNeeded within the animation block. Apple actually recommends you call it once before the animation block to ensure that all pending layout operations have been completed.
From iOS: How does one animate to new autolayout constraint (height):
After updating your constraint:
[UIView animateWithDuration:0.5 animations:^{[self.view layoutIfNeeded];}];
Replace self.view
with a reference to the containing view.
Swift 4 drop in solution.
Do not forget to place animated views to some container view since this is the view which is actually flipping (so placing it inside root view can cause "full-page" flip).
class ViewController: UIViewController {
private enum Side {
case head
case tail
}
private let containerView = UIView(frame: .zero)
private let firstView = UIView(frame: .zero)
private let secondView = UIView(frame: .zero)
private var currentSide: Side = .head
override func viewDidLoad() {
super.viewDidLoad()
// container view
view.addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
containerView.heightAnchor.constraint(equalToConstant: 100),
containerView.widthAnchor.constraint(equalToConstant: 100),
containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapContainer))
containerView.addGestureRecognizer(tapGesture)
// first view
containerView.addSubview(firstView)
firstView.translatesAutoresizingMaskIntoConstraints = false
firstView.backgroundColor = .red
NSLayoutConstraint.activate([
firstView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
firstView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
firstView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
firstView.topAnchor.constraint(equalTo: containerView.topAnchor),
])
// second view
containerView.addSubview(secondView)
secondView.translatesAutoresizingMaskIntoConstraints = false
secondView.backgroundColor = .yellow
secondView.isHidden = true
NSLayoutConstraint.activate([
secondView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
secondView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
secondView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
secondView.topAnchor.constraint(equalTo: containerView.topAnchor),
])
}
@objc
func tapContainer() {
switch currentSide {
case .head:
UIView.transition(from: firstView,
to: secondView,
duration: 1,
options: [.transitionFlipFromRight, .showHideTransitionViews],
completion: nil)
currentSide = .tail
case .tail:
UIView.transition(from: secondView,
to: firstView,
duration: 1,
options: [.transitionFlipFromLeft, .showHideTransitionViews],
completion: nil)
currentSide = .head
}
}
}
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