使用自动布局时如何将动画从一个 UIView 翻转到另一个?
Posted
技术标签:
【中文标题】使用自动布局时如何将动画从一个 UIView 翻转到另一个?【英文标题】:How to make flip animations from one UIView to another when using Auto Layout? 【发布时间】:2014-10-12 20:55:35 【问题描述】:我一直习惯用下面的代码在一个视图和另一个视图之间制作翻转动画:
[UIView transitionFromView:firstView
toView:secondView
duration:0.6
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:^(BOOL finished)
// finish code here
];
效果很好,翻转看起来很自然。
但是当我在使用 Auto Layout 在情节提要上定义的视图上使用它时,事情开始变得混乱 - 视图在动画之后被调整大小和移动。
有没有办法通过动画约束来动画这种翻转?
【问题讨论】:
【参考方案1】:自动布局只能用于定位和调整视图大小。它不能用于产生产生翻转过渡效果所需的那种核心动画变换。所以简短的确切答案是否定的,没有办法通过动画约束来动画这种翻转。
使用显示/隐藏转换选项
但是,有一种简单的方法可以修改您已在使用的代码,使其与自动布局保持一致。这样做的方法是 (1) 在您订购动画之前将 firstView
和 secondView
添加到视图层次结构中,(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:
方法实际上将操纵视图层次结构并添加新的目标视图。如果启用了 Auto Layout,则不会正确布局,因为它没有约束。
您可以在此处查看展示此方法的示例项目: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
【讨论】:
非常感谢!您的回答是救命稻草。【参考方案2】:我最好的猜测是动画是通过动画层的 ~transform 执行的,并且该变换是从 anchorPoint 位置所在的点开始应用的。您可以尝试通过两种方式解决它:
第一: 确保通过中心约束定位视图,即。对齐正在转换的视图的中心(不是左、上或尾随等)。例如,如果两个视图都有,说“horisontaly + vertical”在超级视图中居中,这(不确定)可能会有所帮助。
第二: 创建一个包装视图,将这些视图作为子视图并禁用它们的自动布局。
【讨论】:
【参考方案3】:来自How do I animate constraint changes?:
您需要在动画块中调用 layoutIfNeeded。 Apple actually recommends你在动画块之前调用一次,确保所有待处理的布局操作都已经完成。
来自ios: How does one animate to new autolayout constraint (height):
更新约束后:
[UIView animateWithDuration:0.5 animations:^[self.view layoutIfNeeded];];
将self.view
替换为对包含视图的引用。
【讨论】:
我在同一个控制器上有两个视图。将其视为需要翻转的卡片的 2 个面。 哦,我明白了,你能在你的问题中澄清一下吗?【参考方案4】: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
【讨论】:
【参考方案5】:如果对任何人有帮助,这就是你在 Swift 上的做法,在 Swift 上,选项是静态变量,你只需要在 options 参数中的数组上传递你需要的变量。
UIView.transition(from: firstView, to: secondView, duration: 0.5, options: [.transitionFlipFromLeft, .showHideTransitionViews]) (_) in
这里是选项 https://developer.apple.com/documentation/uikit/uiviewanimationoptions
【讨论】:
以上是关于使用自动布局时如何将动画从一个 UIView 翻转到另一个?的主要内容,如果未能解决你的问题,请参考以下文章
在 Swift 中,如何使用自动布局为 UIView 设置动画,就像页面滑入一样?
如何使用自动布局在 UIScrollView 中为视图高度设置动画?