如何在没有 UINavigationController 的情况下使 Segue 动画水平?

How to make Segue animation Horizontal without UINavigationController?

我正在使用 UISplitViewController,有 MasterViewControllerDetailViewController,没有 UINavigationController

目前master->detail的segue动画,由

触发
performSegueWithIdentifier("showDetail", sender: self)

DetailViewController从底部向上显示组成。

如何制作向左显示的动画?

将您的视图控制器嵌入 UINavigationControllers。

根据 SplitViewController 模板:

在较小的设备上,它将不得不使用 Master 的 navigationController。

另外这个问题已经得到回答here and here and here

来自 View Controller Programming Guide 的更多内容:

There are two ways to display a view controller onscreen: embed it in a container view controller or present it. Container view controllers provide an app’s primary navigation….

在情节提要中,将某些内容嵌入导航控制器并不难。点击你要嵌入的view controller,然后Editor->embed in->navigation controller.

听起来好像新的视图控制器是以模态方式呈现的。如果将 detailViewController 嵌入到 UINavigationController 中并按下新控制器,它将从右向左设置动画,默认情况下也应显示后退按钮。

当你有一个紧凑宽度的屏幕时,"Show Detail" segue 回落到 modal segue automatically.So 您的 DetailViewController 将作为默认模式转场垂直显示。

您可以使用 UIViewControllerTransitioningDelegate 来自定义模态转场的动画。

下面是一个实现横向动画的例子:
1. 设置 transitioningDelegate 及其委托方法

class MasterViewController: UITableViewController,UIViewControllerTransitioningDelegate {

//prepare for segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "showDetail" {
        let detailVC = segue.destinationViewController as! DetailViewController
        detailVC.transitioningDelegate = self
 //     detailVC.detailItem = object//Configure detailVC
    } 
}

//UIViewControllerTransitioningDelegate
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return LeftTransition()
}

func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    let leftTransiton = LeftTransition()
    leftTransiton.dismiss = true
    return leftTransiton
}

}    

2: 自定义 UIViewControllerAnimatedTransitioning : LeftTransition

import UIKit

class LeftTransition: NSObject ,UIViewControllerAnimatedTransitioning {
var dismiss = false

func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
    return 2.0
}

    func animateTransition(transitionContext: UIViewControllerContextTransitioning){
    // Get the two view controllers
    let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
    let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
    let containerView = transitionContext.containerView()!

    var originRect = containerView.bounds
    originRect.origin = CGPointMake(CGRectGetWidth(originRect), 0)

    containerView.addSubview(fromVC.view)
    containerView.addSubview(toVC.view)

    if dismiss{
        containerView.bringSubviewToFront(fromVC.view)
        UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
                fromVC.view.frame = originRect
            }, completion: { (_ ) -> Void in
                fromVC.view.removeFromSuperview()
                transitionContext.completeTransition(true )
        })
    }else{
        toVC.view.frame = originRect
        UIView.animateWithDuration(transitionDuration(transitionContext),
            animations: { () -> Void in
                toVC.view.center = containerView.center
            }) { (_) -> Void in
                fromVC.view.removeFromSuperview()
                transitionContext.completeTransition(true )
        }
    }
}
}

我最近需要更好地控制 segue 的执行方式,因此我制作了自定义 segue classes,它们都在不同的方向上执行转换。这是实现之一:

Swift 2.x

override func perform() {

    //credits to http://www.appcoda.com/custom-segue-animations/

    let firstClassView = self.sourceViewController.view
    let secondClassView = self.destinationViewController.view

    let screenWidth = UIScreen.mainScreen().bounds.size.width
    let screenHeight = UIScreen.mainScreen().bounds.size.height

    secondClassView.frame = CGRectMake(screenWidth, 0, screenWidth, screenHeight)

    if let window = UIApplication.sharedApplication().keyWindow {

        window.insertSubview(secondClassView, aboveSubview: firstClassView)

        UIView.animateWithDuration(0.4, animations: { () -> Void in

            firstClassView.frame = CGRectOffset(firstClassView.frame, -screenWidth, 0)
            secondClassView.frame = CGRectOffset(secondClassView.frame, -screenWidth, 0)

            }) {(Finished) -> Void in

                self.sourceViewController.navigationController?.pushViewController(self.destinationViewController, animated: false)

        }

    }

}

这个将有一个 "right to left" 过渡。您可以根据需要修改此功能,只需更改源视图控制器和目标视图控制器的初始和结束位置。

另外不要忘记,您需要将您的 segue 标记为 "custom segue",并将新的 class 分配给它。

更新: 添加了 Swift 3 版本

Swift 3

override func perform() {

    //credits to http://www.appcoda.com/custom-segue-animations/

    let firstClassView = self.source.view
    let secondClassView = self.destination.view

    let screenWidth = UIScreen.main.bounds.size.width
    let screenHeight = UIScreen.main.bounds.size.height

    secondClassView?.frame = CGRect(x: screenWidth, y: 0, width: screenWidth, height: screenHeight)

    if let window = UIApplication.shared.keyWindow {

        window.insertSubview(secondClassView!, aboveSubview: firstClassView!)

        UIView.animate(withDuration: 0.4, animations: { () -> Void in

            firstClassView?.frame = (firstClassView?.frame.offsetBy(dx: -screenWidth, dy: 0))!
            secondClassView?.frame = (secondClassView?.frame.offsetBy(dx: -screenWidth, dy: 0))!

        }, completion: {(Finished) -> Void in

            self.source.navigationController?.pushViewController(self.destination, animated: false)

        }) 

    }

}

--Swift 3.0--

Armin 的解决方案适用于 swift 3。 新建 -> 文件 -> Cocoa 触摸 Class -> Class: ... (Subclass: UIStoryboardSegue).

import UIKit

class SlideHorSegue: UIStoryboardSegue {
    override func perform() {

        //credits to http://www.appcoda.com/custom-segue-animations/

        let firstClassView = self.source.view
        let secondClassView = self.destination.view

        let screenWidth = UIScreen.main.bounds.size.width
        let screenHeight = UIScreen.main.bounds.size.height

        secondClassView?.frame = CGRect(x: screenWidth, y: 0, width: screenWidth, height: screenHeight)

        if let window = UIApplication.shared.keyWindow {

            window.insertSubview(secondClassView!, aboveSubview: firstClassView!)

            UIView.animate(withDuration: 0.4, animations: { () -> Void in

                firstClassView?.frame = (firstClassView?.frame)!.offsetBy(dx: -screenWidth, dy: 0)
                secondClassView?.frame = (secondClassView?.frame)!.offsetBy(dx: -screenWidth, dy: 0)

            }) {(Finished) -> Void in

                self.source.navigationController?.pushViewController(self.destination, animated: false)

            }

        }

    }

}

在故事板中:将您的转场标记为 "custom segue",并将新的 class 分配给它。

注意:如果您的 detailesVC 中有 UIScrollView,这将不起作用。