How to make flip animation from one UIView to another when using automatic layout?

I always used the following code to flip an animation between one view and another:

[UIView transitionFromView:firstView toView:secondView duration:0.6 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) { // finish code here }]; 

This worked perfectly, giving a natural flip.

But when I used it in the view that was defined on the storyboard using Auto Layout, everything became messy - the dimensions were resized and moved after this animation.

Is there any way to revive this kind of flip by revitalizing the constraints?

+8
source share
5 answers

Auto layout can only be used for positioning and resizing. It cannot be used to create the kind of Core Animation transform needed to create a flip transition effect. Thus, the short exact answer is no, there is no way to animate this kind of flip by revitalizing restrictions.

Using the show / hide transition option

However, there is an easy way to change the code that you are already using so that it matches Auto Layout. The way to do this is: (1) add your firstView and secondView to the hierarchy of your view before ordering the animation, (2) to make sure you add auto-layout constraints that define the layout of both of these views and (3) add a parameter to the animation so that you only show / hide two views, instead of 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 needed? The reason is that without the UIViewAnimationOptionShowHideTransitionViews option UIViewAnimationOptionShowHideTransitionViews the transitionFromView:toView:duration:options:completion: method will actually manage the view hierarchy and add a new destination view. If automatic layout is enabled, then it will not be selected correctly, since it will not have restrictions.

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

Using view hierarchy manipulation

Alternatively, you can also use your existing transitionFromView:toView:duration:options:completion: call transitionFromView:toView:duration:options:completion: But if you do not just show a representation of a destination that already has restrictions, then you need to use the completion block to add these restrictions, as shown below:

  [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 given here: https://github.com/algal/AutoLayoutFlipDemo2

+31
source

My best guess would be that the animation is done by animating the transform layer ~, and this transform is applied from the point where the anchorPoint position is. You can try to solve it in two ways:

First: Make sure you position your views with center constraints, i.e. align the center of the views being viewed (not left, top or end, etc.). For example, if both views have, say, “horisontaly + vertically” in the center of observation, this (not sure) could help.

Second: Create a shell view, place these views as subzones, and turn off auto-detection for them.

+2
source

From How to change animation constraint changes? :

You need to call layoutIfNeeded in the animation block. Apple actually recommends that you call it once in front of the animation block to ensure that all pending layout operations are complete.

From iOS: how can you revitalize a new autodetection restriction (height) :

After updating your restriction:

 [UIView animateWithDuration:0.5 animations:^{[self.view layoutIfNeeded];}]; 

Replace self.view reference to the containing view.

0
source

if someone helps, as you do in Swift, in Swift these are static variables, you just need to pass the ones you need in the array in the parameter parameter.

  UIView.transition(from: firstView, to: secondView, duration: 0.5, options: [.transitionFlipFromLeft, .showHideTransitionViews]) { (_) in } 

Here are the options https://developer.apple.com/documentation/uikit/uiviewanimationoptions

0
source

Swift 4 drops in solution.

Do not forget to place animated views in any container, as this view is actually flipped (therefore, placing it inside the root view can cause a flip to the whole page).

 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 } } } 
0
source

Source: https://habr.com/ru/post/976648/


All Articles