问题描述
这是我的自定义转场代码
Here's my Code for my Custom Segue
class FlipFromRightSegue: UIStoryboardSegue {
override func perform() {
let source:UIViewController = self.sourceViewController as UIViewController
let destination:UIViewController = self.destinationViewController as UIViewController
UIView.transitionWithView(source.view, duration: 1.0, options: .CurveEaseInOut | .TransitionFlipFromRight, animations: { () -> Void in
source.view.addSubview(destination.view)
}) { (finished) -> Void in
destination.view.removeFromSuperview()
source.presentViewController(destination, animated: false, completion: nil)
}
}
}
我认为这可行,但实际上只有在已经执行转场时视图才会改变.我该怎么做才能让翻转"在中间时视图发生变化?
I thought this works but actually the view changes only when the segue is already performed. What should I do so that the view changes when the "Flip" is in the middle?
提前致谢.
推荐答案
从 iOS 7 开始,我们通常不会使用自定义转场动画过渡.我们要么使用标准的模态演示,指定一个 modalTransitionStyle
(即我们可以为模态转换选择的一些动画的固定列表),或者您将实现自定义动画转换.两者的描述如下:
As of iOS 7, we generally don't animate transitions using a custom segue. We'd either use a standard modal presentation, specifying a modalTransitionStyle
(i.e. a fixed list of a few animations we can pick for our modal transitions), or you'd implement custom animation transitions. Both of those are described below:
如果您只是展示另一个视图控制器的视图,将动画更改为翻转的简单解决方案是在目标视图控制器中设置
modalTransitionStyle
.您可以在界面生成器中的 segue 属性下完全执行此操作.
If you are simply presenting another view controller's view, the simple solution for changing the animation to a flip is by setting the
modalTransitionStyle
in the destination view controller. You can do this entirely in Interface Builder under the segue's properties.
如果您想以编程方式执行此操作,您可以在 Swift 3 中的目标控制器中执行以下操作:
If you want to do it programmatically, in the destination controller you could do the following in Swift 3:
override func viewDidLoad() {
super.viewDidLoad()
modalTransitionStyle = .flipHorizontal // use `.FlipHorizontal` in Swift 2
}
然后,当您调用 show
/showViewController
或 present
/presentViewController
时,将执行您的演示与水平翻转.而且,当您关闭视图控制器时,动画会自动为您反转.
Then, when you call show
/showViewController
or present
/presentViewController
, and your presentation will be performed with horizontal flip. And, when you dismiss the view controller, the animation is reversed automatically for you.
如果您需要更多控制,在 iOS 7 及更高版本中,您可以使用自定义动画过渡,在其中指定 .custom
的 modalPresentationStyle
.例如,在 Swift 3 中:
If you need more control, in iOS 7 and later, you would use custom animation transitions, in which you'd specify a modalPresentationStyle
of .custom
. For example, in Swift 3:
class SecondViewController: UIViewController {
let customTransitionDelegate = TransitioningDelegate()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
modalPresentationStyle = .custom // use `.Custom` in Swift 2
transitioningDelegate = customTransitionDelegate
}
...
}
指定将实例化动画控制器的 UIViewControllerTransitioningDelegate
.例如,在 Swift 3 中:
That specifies the UIViewControllerTransitioningDelegate
that would instantiate the animation controller. For example, in Swift 3:
class TransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return AnimationController(transitionType: .presenting)
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return AnimationController(transitionType: .dismissing)
}
}
动画控制器只会做 .transitionFlipFromRight
是一个演示文稿,或者 .transitionFlipFromLeft
如果在 Swift 3 中关闭:
And the animation controller would simply do .transitionFlipFromRight
is a presentation, or .transitionFlipFromLeft
if dismissing in Swift 3:
class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
enum TransitionType {
case presenting
case dismissing
}
var animationTransitionOptions: UIViewAnimationOptions
init(transitionType: TransitionType) {
switch transitionType {
case .presenting:
animationTransitionOptions = .transitionFlipFromRight
case .dismissing:
animationTransitionOptions = .transitionFlipFromLeft
}
super.init()
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
//let inView = transitionContext.containerView
let toView = transitionContext.viewController(forKey: .to)?.view
let fromView = transitionContext.viewController(forKey: .from)?.view
UIView.transition(from: fromView!, to: toView!, duration: transitionDuration(using: transitionContext), options: animationTransitionOptions.union(.curveEaseInOut)) { finished in
transitionContext.completeTransition(true)
}
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 1.0
}
}
有关 iOS 7 中引入的自定义过渡的更多信息,请参阅 WWDC 2013 视频 使用视图控制器的自定义转换.
For more information on the custom transitions introduced in iOS 7, see WWDC 2013 video Custom Transitions Using View Controllers.
如果应该承认上面的AnimationController
实际上是一种过度简化,因为我们使用的是transform(from:to:...)
.这会导致动画不可取消(以防您使用交互式过渡).它还删除了from"视图本身,从 iOS 8 开始,这实际上是演示控制器的工作.
If should be acknowledged that the above AnimationController
is actually a over-simplification, because we're using transform(from:to:...)
. That results in an animation that isn't cancelable (in case you're using interactive transition). It's also removing the "from" view itself, and as of iOS 8, that's now really the job of the presentation controller.
因此,您确实想使用 UIView.animate
API 来制作翻转动画.我很抱歉,因为以下涉及在关键帧动画中使用一些不直观的 CATransform3D
,但它会导致翻转动画,然后可以进行可取消的交互式转换.
So, you really want to do the flip animation using UIView.animate
API. I apologize because the following involves using some unintuitive CATransform3D
in key frame animations, but it results in a flip animation that can then be subjected to cancelable interactive transitions.
因此,在 Swift 3 中:
So, in Swift 3:
class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
enum TransitionType {
case presenting
case dismissing
}
let transitionType: TransitionType
init(transitionType: TransitionType) {
self.transitionType = transitionType
super.init()
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let inView = transitionContext.containerView
let toView = transitionContext.view(forKey: .to)!
let fromView = transitionContext.view(forKey: .from)!
var frame = inView.bounds
func flipTransform(angle: CGFloat, offset: CGFloat = 0) -> CATransform3D {
var transform = CATransform3DMakeTranslation(offset, 0, 0)
transform.m34 = -1.0 / 1600
transform = CATransform3DRotate(transform, angle, 0, 1, 0)
return transform
}
toView.frame = inView.bounds
toView.alpha = 0
let transformFromStart: CATransform3D
let transformFromEnd: CATransform3D
let transformFromMiddle: CATransform3D
let transformToStart: CATransform3D
let transformToMiddle: CATransform3D
let transformToEnd: CATransform3D
switch transitionType {
case .presenting:
transformFromStart = flipTransform(angle: 0, offset: inView.bounds.size.width / 2)
transformFromEnd = flipTransform(angle: -.pi, offset: inView.bounds.size.width / 2)
transformFromMiddle = flipTransform(angle: -.pi / 2)
transformToStart = flipTransform(angle: .pi, offset: -inView.bounds.size.width / 2)
transformToMiddle = flipTransform(angle: .pi / 2)
transformToEnd = flipTransform(angle: 0, offset: -inView.bounds.size.width / 2)
toView.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
fromView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
case .dismissing:
transformFromStart = flipTransform(angle: 0, offset: -inView.bounds.size.width / 2)
transformFromEnd = flipTransform(angle: .pi, offset: -inView.bounds.size.width / 2)
transformFromMiddle = flipTransform(angle: .pi / 2)
transformToStart = flipTransform(angle: -.pi, offset: inView.bounds.size.width / 2)
transformToMiddle = flipTransform(angle: -.pi / 2)
transformToEnd = flipTransform(angle: 0, offset: inView.bounds.size.width / 2)
toView.layer.anchorPoint = CGPoint(x: 1, y: 0.5)
fromView.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
}
toView.layer.transform = transformToStart
fromView.layer.transform = transformFromStart
inView.addSubview(toView)
UIView.animateKeyframes(withDuration: self.transitionDuration(using: transitionContext), delay: 0, options: [], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.0) {
toView.alpha = 0
fromView.alpha = 1
}
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
toView.layer.transform = transformToMiddle
fromView.layer.transform = transformFromMiddle
}
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.0) {
toView.alpha = 1
fromView.alpha = 0
}
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
toView.layer.transform = transformToEnd
fromView.layer.transform = transformFromEnd
}
}, completion: { finished in
toView.layer.transform = CATransform3DIdentity
fromView.layer.transform = CATransform3DIdentity
toView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
fromView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 1.0
}
}
仅供参考,iOS 8 使用演示控制器扩展了自定义转换模型.有关详细信息,请参阅 WWDC 2014 视频深入了解演示控制器.
无论如何,如果在转换结束时,from"视图不再可见,您可以指示您的表示控制器将其从视图层次结构中删除,例如:
Anyway, if, at the end of the transition, the "from" view is no longer visible, you'd instruct your presentation controller to remove it from the view hierarchy, e.g.:
class PresentationController: UIPresentationController {
override var shouldRemovePresentersView: Bool { return true }
}
而且,很明显,你必须通知你的 TransitioningDelegate
这个展示控制器:
And, obviously, you have to inform your TransitioningDelegate
of this presentation controller:
class TransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
...
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return PresentationController(presentedViewController: presented, presenting: presenting)
}
}
此答案已针对 Swift 3 更新.如果您想查看此答案,请参阅此答案的先前修订版Swift 2 实现.
This answer has been updated for Swift 3. Please refer to the previous revision of this answer if you want to see the Swift 2 implementation.
这篇关于Swift 中的自定义翻转 Segue的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!