如何在使用自动布局时从一个UIView制作翻转动画到另一个UIView?

9

我曾经一直使用以下代码来实现两个视图之间的翻转动画:

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

这个方法是有效的,翻转效果很自然。

但当我在使用自动布局定义的视图上使用它时,情况变得混乱 - 这个动画后,视图会被重新调整大小和移动。

有没有一种方法可以通过动画约束来实现这种翻转效果呢?

5个回答

33

自动布局仅用于定位和调整视图的大小。它不能用于生成产生翻转转换效果所需的核心动画变换。因此,简短而确切的答案是,不,没有办法通过动画约束来实现这种翻转。

使用显示/隐藏转换选项

然而,有一种简单的方法可以修改您已经在使用的代码,以使其与自动布局一致。做法是:(1) 在执行动画之前将firstViewsecondView都添加到您的视图层次结构中,(2) 确保您已经添加了自动布局约束来定义这两个视图的布局,(3) 添加一个选项到动画中,以便你只显示/隐藏这两个视图,而不是拆除和设置一个新的视图层次结构。

换句话说,您希望像这样:

        // 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];
为什么需要这样做呢?原因是,如果不使用UIViewAnimationOptionShowHideTransitionViews选项,则transitionFromView:toView:duration:options:completion:方法实际上会操纵视图层次结构并添加新的目标视图。如果启用了自动布局,则由于缺少约束条件,它将无法正确布局。
您可以在此处查看演示此方法的示例项目:https://github.com/algal/AutoLayoutFlipDemo

使用视图层次结构操作

或者,您也可以使用现有的transitionFromView:toView:duration:options:completion:调用。但是,如果您不只是要显示已经有约束条件的目标视图,则需要使用完成块来添加这些约束条件,如下所示:
    [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]];
                }];

这种方法的一个可行示例在此处: https://github.com/algal/AutoLayoutFlipDemo2


非常感谢!你的回答真是救了我一命。 - kender

2

我最好的猜测是,动画是通过动画层的~transform执行的,并且该变换从anchorPoint位置处应用。您可以尝试以两种方式解决它:

第一种方法: 确保您通过中心约束对视图进行定位,即将正在转换的视图的中心对齐(而不是左侧、顶部或尾随等)。例如,如果两个视图都在父视图中“水平+垂直”居中,这可能会有所帮助。

第二种方法: 创建一个包装器视图,将这些视图作为子视图放置,并为它们禁用自动布局。


1

我在同一个控制器上有两个视图。可以将其想象成需要翻转的卡片的两面。 - kender
噢,我明白了,你能否在你的问题中澄清一下,方便未来的回答者? - Wyetro

1

Swift 4 的即插即用解决方案。

不要忘记将动画视图放置在某个容器视图中,因为这是实际翻转的视图(因此将其放置在根视图中可能会导致“全页”翻转)。

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

如果需要帮助,这就是在Swift上如何实现的方法。在Swift中,这些选项为静态变量,您只需在选项参数中传递所需的变量数组即可。

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

以下是选项 https://developer.apple.com/documentation/uikit/uiviewanimationoptions


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接