如何在没有 UINavigationController 的情况下使 Segue 动画水平?
How to make Segue animation Horizontal without UINavigationController?
我正在使用 UISplitViewController
,有 MasterViewController
和 DetailViewController
,没有 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,这将不起作用。
我正在使用 UISplitViewController
,有 MasterViewController
和 DetailViewController
,没有 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,这将不起作用。