Swift 中的自定义翻转 Segue [英] Custom Flip Segue in Swift

查看:27
本文介绍了Swift 中的自定义翻转 Segue的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的自定义转场代码

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:

  1. 如果您只是展示另一个视图控制器的视图,将动画更改为翻转的简单解决方案是在目标视图控制器中设置 modalTransitionStyle.您可以在界面生成器中的 segue 属性下完全执行此操作.

  1. 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/showViewControllerpresent/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 及更高版本中,您可以使用自定义动画过渡,在其中指定 .custommodalPresentationStyle.例如,在 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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆