Presentation ViewController
基礎(chǔ)知識
在沒有UINavigationController
的時候嗜价,我們通常用present modally
(彈出模態(tài)控制器)的方式切換視圖艇抠。默認(rèn)情況下,目標(biāo)視圖從屏幕的下方彈出久锥。具體方法是:
通過presentViewController(_: animated:completion:)
來彈出視圖练链,
通過viewController
的modalTransitionStyle
的屬性設(shè)置彈出ViewController
時的動畫:
viewController.modalTransitionStyle = UIModalTransitionStyle.CrossDissolve
presentViewController(secondViewController, animated: true, completion: nil)
系統(tǒng)自帶的四種動畫有:
enum UIModalTransitionStyle: Int {
case CoverVertical // 底部滑入,默認(rèn)
case FlipHorizontal // 水平翻轉(zhuǎn)
case CrossDissolve // 隱出隱現(xiàn)
case PartialCurl // 翻頁
}
自定義轉(zhuǎn)場動畫
在轉(zhuǎn)場的過程中系統(tǒng)會提供一個視圖容器containerView
奴拦,當(dāng)前的視圖(fromView)會自動加入到這個容器中媒鼓,我們所要做的就是將目標(biāo)視圖(toView)加入到這個容器中,然后為目標(biāo)視圖的展現(xiàn)增加動畫错妖。
需要注意的是:如果是從 A 視圖控制器 present 到 B绿鸣,則A是fromView,B是toView暂氯。從 B 視圖控制器dismiss到 A 時潮模,B 變成了fromView,A是toView痴施。
創(chuàng)建動畫管理器類擎厢,該類需繼承NSObject并遵循UIViewControllerAnimatedTransitioning協(xié)議:
import UIKit
class FadeAnimator: NSObject, UIViewControllerAnimatedTransitioning {
let duration = 1.0
// 指定轉(zhuǎn)場動畫持續(xù)的時間
func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
return duration
}
// 實現(xiàn)轉(zhuǎn)場動畫的具體內(nèi)容
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
// 得到容器視圖
let containerView = transitionContext.containerView()
// 目標(biāo)視圖
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
containerView.addSubview(toView)
// 為目標(biāo)視圖的展現(xiàn)添加動畫
toView.alpha = 0.0
UIView.animateWithDuration(duration,
animations: {
toView.alpha = 1.0
}, completion: { _ in
transitionContext.completeTransition(true)
})
}
}
然后在ViewController(第一個VC)中加入如下代碼:
// 聲明一個動畫實例
let transition = FadeAnimator()
// 遵循 UIViewControllerTransitioningDelegate 協(xié)議,并實現(xiàn)其中的兩個方法:
extension ViewController: UIViewControllerTransitioningDelegate {
// 提供彈出時的動畫
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return transition
}
// 提供消失時的動畫
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return transition
}
}
@IBAction func register(sender: AnyObject) {
var viewController = storyboard!.instantiateViewControllerWithIdentifier("SecondViewController") as SecondViewController
viewController.transitioningDelegate = self
presentViewController(viewController, animated: true, completion: nil)
}
最終效果如下:
Segue
通過Segue轉(zhuǎn)場辣吃,即通過在Storyboard中拖線的方式進行轉(zhuǎn)場动遭。自定義轉(zhuǎn)場動畫的關(guān)鍵就是:
- 創(chuàng)建一個UIStoryboardSegue的子類,并重載其中perform方法神得。
- 在perform方法中實現(xiàn)自定義動畫的邏輯脖母。
- 將這個UIStoryboardSegue類與拖的segue線進行綁定坝疼。
下面我們一步步來實現(xiàn):
創(chuàng)建兩個UIViewController類——FirstViewController、SecondViewController,分別對應(yīng)Storyboard中的兩個ViewController場景鲤脏,從第一個VC向第二個VC拖一個Segue览祖,并選擇custom:
創(chuàng)建一個UIStoryboardSegue子類膳殷,并與上一部的Segue進行綁定膨疏,并指定它的Identifier:
打開FirstCustomSegue.swift,在perform方法中實現(xiàn)具體動畫:
import UIKit
class FirstCustomSegue: UIStoryboardSegue {
// 重載perform方法宗苍,在這里我們添加想要的自定義邏輯
override func perform() {
// 得到源控制器和目標(biāo)控制器的視圖
var sourceView = self.sourceViewController.view as UIView!
var destinationView = self.destinationViewController.view as UIView!
// 得到屏幕的寬和高
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
// 把destinationView放在sourceView的下面
destinationView.frame = CGRectMake(0.0, screenHeight, screenWidth, screenHeight)
let window = UIApplication.sharedApplication().keyWindow
// 把目標(biāo)視圖添加到當(dāng)前視圖中
window?.insertSubview(destinationView, aboveSubview: sourceView)
UIView.animateWithDuration(0.4, animations: { () -> Void in
sourceView.frame = CGRectOffset(sourceView.frame, 0.0, -screenHeight)
destinationView.frame = CGRectOffset(destinationView.frame, 0.0, -screenHeight)
}, completion: { _ in
// 展示新的視圖控制器
self.sourceViewController.presentViewController(self.destinationViewController as UIViewController, animated: false, completion: nil)
})
}
}
在FirstViewController中執(zhí)行這個自定義轉(zhuǎn)場稼稿,這里我們采用滑動手勢:
override func viewDidLoad() {
super.viewDidLoad()
var swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "showSecondViewController")
swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Up
self.view.addGestureRecognizer(swipeGestureRecognizer)
}
func showSecondViewController() {
self.performSegueWithIdentifier("FirstCustomSegue", sender: self)
}
解除轉(zhuǎn)場
與轉(zhuǎn)場對應(yīng)的是解除轉(zhuǎn)場(unwind segue)亿遂,和正常轉(zhuǎn)場一樣,也需要創(chuàng)建一個UIStoryboardSegue的子類渺杉,并重載其中perform方法蛇数。
不同的是,解除轉(zhuǎn)場需要在第一個ViewController中創(chuàng)建一個帶UIStoryboardSegue類參數(shù)的IBAction方法是越,這個方法的內(nèi)容可以為空耳舅,但必須存在。
需要注意的是倚评,F(xiàn)irstViewController沒有UINavigationController的時候解除轉(zhuǎn)場的動畫才會出現(xiàn)浦徊,但是正常轉(zhuǎn)場效果是都有的,不知道這是不是個漏洞天梧,知道的老師可以告訴下盔性,謝謝。
下面我們來實現(xiàn)這個自定義解除轉(zhuǎn)場:
打開FirstViewController.swift呢岗,添加代碼:
@IBAction func backFromSegueAction(sender: UIStoryboardSegue) {
}
打開Storyboard冕香,在SecondViewController場景中進行如下操作:
然后在Scene中會出現(xiàn)Unwind segue,選中后在屬性中設(shè)置Identifier:backFromSegue
創(chuàng)建UIStoryboardSegue子類-FirstCustomSegueUnwind后豫,重寫其中的perform方法:
import UIKit
class FirstCustomSegueUnwind: UIStoryboardSegue {
override func perform() {
var secondVCView = self.sourceViewController.view as UIView!
var firstVCView = self.destinationViewController.view as UIView!
let screenHeight = UIScreen.mainScreen().bounds.size.height
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(firstVCView, aboveSubview: secondVCView)
UIView.animateWithDuration(0.4, animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, screenHeight)
secondVCView.frame = CGRectOffset(secondVCView.frame, 0.0, screenHeight)
}) { (Finished) -> Void in
self.sourceViewController.dismissViewControllerAnimated(false, completion: nil)
}
}
}
接下來在FirstViewController中來調(diào)用這個子類悉尾,打開FirstViewController.swift,重寫以下方法:
override func segueForUnwindingToViewController(toViewController: UIViewController, fromViewController: UIViewController, identifier: String?) -> UIStoryboardSegue {
if let id = identifier {
if id == "backFromSegue" {
let unwindSegue = FirstCustomSegueUnwind(identifier: id, source: fromViewController, destination: toViewController, performHandler: { () -> Void in
})
return unwindSegue
}
}
return super.segueForUnwindingToViewController(toViewController, fromViewController: fromViewController, identifier: identifier)
}
最后挫酿,在SecondViewController中執(zhí)行這個解除轉(zhuǎn)場构眯,還是采用滑動手勢:
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "showFirstViewController")
swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Down
self.view.addGestureRecognizer(swipeGestureRecognizer)
}
func showFirstViewController() {
self.performSegueWithIdentifier("backFromSegue", sender: self)
}
}
到這里,整個轉(zhuǎn)場就全部寫完了早龟,最后看下效果: