Swift: 是用Custom Segue還是用Transition動畫

用一個很簡單的場景做為例子:在storyboard上,你有用UINavigationController串起來兩個UIViewController。這兩個controller之間要互相跳轉(zhuǎn),A->B, B->A。跳轉(zhuǎn)的時(shí)候默認(rèn)的那個push來push去的效果你覺得很傻X,所以想換一個效果。比如,不那么二的fade in/out效果。

很多的例子會說寫一個cusom的UIStoryboardSegue,然后在這個里面寫一個UIView.animationWithDuration來實(shí)現(xiàn)這個效果。千萬別這么干!從iOS7開始就有一個更加方便簡潔的方法可以實(shí)現(xiàn)這個效果。

下面就開始介紹這個很牛X的方法。首先創(chuàng)建一個single view的項(xiàng)目。名稱可以叫做transitionDemo。各個controller之間的關(guān)系是這樣的:


一個UINavigationController作為啟動controller,串起來一個UITableViewController(root controller)和一個UIViewController。在Prototype Cellsctrl+drag到view controller上,并選擇show。

下面分別為table view controller創(chuàng)建類TDTableViewController為view controller創(chuàng)建類TDViewController之后分別在storyboard里關(guān)聯(lián)起來。把從table view controller到view controller的segue的identitifer設(shè)置為TDViewController。

segue

接下來給TDTableViewController添加數(shù)據(jù)源:

class TDTableViewController: UITableViewController {

var tableViewDataSource: [String]?
    
    override func viewDidLoad() {
        super.viewDidLoad()

        createDataSource()
    }
    
    func createDataSource() {
        if let _ = self.tableViewDataSource {
            return
        }
        
        self.tableViewDataSource = [String]()
        for i in 0..<100 {
            self.tableViewDataSource!.append("item :- [\(i)]")
        }
    }
    //............
}

打開storyboard,在TDViewController里添加一個label,給這個label添加約束,隨便是什么約束都可以只要是正確的。然后在controller里添加這個label的outlet并關(guān)聯(lián)。

TDViewController代碼中添加數(shù)據(jù)屬性,并在viewDidLoad方法里給這label的text賦值:

class TDViewController: UIViewController {

    @IBOutlet weak var dataLabel: UILabel!
    
    var singleData: String!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        self.dataLabel.text = self.singleData
    }
}

使用默認(rèn)的segue,這里現(xiàn)在是show模式,跳轉(zhuǎn)。并從table view controller出傳遞數(shù)據(jù)給view controller:

// 使用segue跳轉(zhuǎn)
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        self.dataItem = self.tableViewDataSource![indexPath.row]
        self.performSegueWithIdentifier("TDViewController", sender: nil)
    }

// 傳遞數(shù)據(jù)
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier != "TDViewController" {
            return
        }
        
        let destinationController = segue.destinationViewController as! TDViewController
        destinationController.singleData = self.dataItem!
    }

run一把來看看:

Segue和Transition

custom segue

直接用代碼把兩種方式都實(shí)現(xiàn)出來,分別代替上面使用的默認(rèn)的實(shí)現(xiàn)方式。

首先弄一個custom segue。要開發(fā)一個custom segue,首先需要創(chuàng)建一個類并繼承自UIStoryboardSegue,我們?yōu)檫@個類命名為DetailStoryboardSegue。在這個類中關(guān)鍵就是實(shí)現(xiàn)方法perform()。

import UIKit

class DetailStoryboardSegue: UIStoryboardSegue {
    override func perform() {
        let sourceView = self.sourceViewController.view // 1
        let destView = self.destinationViewController.view
        
        let window = (UIApplication.sharedApplication().delegate as! AppDelegate).window
        window?.insertSubview(destView, aboveSubview: sourceView) // 2
        
        destView.alpha = 0.0
        
        UIView.animateWithDuration(0.3, animations: { // 3
            destView.alpha = 1.0
        })
    }
}
  1. self.sourceViewControllerself.destinationViewController都是UIStoryboardSegue類本身就有的屬性。從A controller跳轉(zhuǎn)到B controller,那么source就是A,destination就是B。我們的fade in/out效果就是通過source和destination controller的view的切換和alpha實(shí)現(xiàn)的。
  2. 在window上做source和destination view的切換。把destination view覆蓋到source view上。
  3. destination view的alpha在上一步設(shè)置為了0,也就是完全透明的。在動畫中把destination view的alpha設(shè)置回完全不透明,把view呈現(xiàn)在用戶面前達(dá)到fade in的效果。

實(shí)現(xiàn)完成后,在storyboard中把segue的Kind 設(shè)置為custom,然后給Class設(shè)置為類DetailStoryboardSegue。

其實(shí)很簡單,運(yùn)行起來看看。


你會看到,運(yùn)行的結(jié)果出了一點(diǎn)問題。之前在正確位置顯示的label,在這里居然出現(xiàn)在了屏幕的頂端。這是因?yàn)榍懊?code>TDViewController是用navigation controller的push出來的,屏幕的最頂端有一個navigation bar,所以label的top約束是有效的。而我們的custom segue的切換中并不存在navigation controller的push。而是簡單的view的覆蓋替換,沒有navigation bar。所以labe的top約束失效了,直接被顯示在了頂端。

這個錯誤其實(shí)引出了一個問題,但是這里我們暫時(shí)不做深入討論。先看看Transitioning animtion動畫是怎么運(yùn)行的,然后我們討論這個嚴(yán)肅的問題。

custom transitioning animation

實(shí)現(xiàn)自定義的切換動畫就需要實(shí)現(xiàn)UIViewControllerAnimatedTransitioning這個protocol了。我們自定義一個類DetailTransitioningAnimator來實(shí)現(xiàn)這個protocol。

這個protocol有兩個方法是必須實(shí)現(xiàn)的,func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeIntervalfunc animateTransition(transitionContext: UIViewControllerContextTransitioning)。來看代碼:

import UIKit

class DetailTransitioningAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    let durationTimeInterval: NSTimeInterval
    // 1
    init(duration: NSTimeInterval){
        durationTimeInterval = duration
    }
    
    func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
        return self.durationTimeInterval
    }
    //2
    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        let containerView = transitionContext.containerView()
        let sourceController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
        let destController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
        
        
        let sourceView = sourceController!.view
        let destView = destController!.view
        
        containerView?.addSubview(destView) // 3
        
        destView.alpha = 0.0
        
        UIView.animateWithDuration(0.3, animations: {
            destView.alpha = 1.0
        }, completion: {completed in
            let cancelled = transitionContext.transitionWasCancelled()
            transitionContext.completeTransition(!cancelled)
        })
    }
    
    func animationEnded(transitionCompleted: Bool) {
        
    }
}
  1. 這個init方法不是必需的,但是為了可以自定義動畫的執(zhí)行時(shí)間添加這個構(gòu)造方法。
  2. transitioning動畫的執(zhí)行方法。在這個方法里實(shí)現(xiàn)我們在之前的custom segue里實(shí)現(xiàn)的效果。
  3. 注意這里,用的是let containerView = transitionContext.containerView()得到的container view。而不是之前用到的window。

下面把transitioning動畫應(yīng)用到代碼中。在使用的時(shí)候需要實(shí)現(xiàn)UINavigationControllerDelegate。創(chuàng)建一個類NavigationControllerDelegate來實(shí)現(xiàn)這個protocol。

import UIKit

class NavigationControllerDelegate: NSObject, UINavigationControllerDelegate {
    func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return DetailTransitioningAnimator(duration: 0.3)
    }
}

這里沒有太多需要講的。只要知道是這個方法就可以了。

下面在storyboard中把類NavigationControllerDelegate應(yīng)用在UINavigationController中。

  1. 把object這個東西拖動到項(xiàng)目中唯一存在的navigation controller上。
  2. 給剛剛的object設(shè)置custom class為類NavigationControllerDelegate

    只要關(guān)注右側(cè)的Class的設(shè)置就可以。
  3. 給navigation controller的代理設(shè)置為剛剛拖動上去的delegate。


對上面的custom segue代碼去掉,并稍作修改之后,運(yùn)行起來。



木有問題,上面出現(xiàn)的錯誤也沒有了。

這個問題不重要,但是還是要討論一下:為什么custom segue會出現(xiàn)問題,而transitoning動畫就沒有問題呢?這里是因?yàn)閷?shí)際上transitioning動畫是在navigation controller的基礎(chǔ)上改變的。Transitioning動畫只是修改了navigation controller的push動畫,改成了fade in的效果,而navigation controller的其他機(jī)制沒有改動。custom segue則完全是兩個view之間的動畫。那么,這里就留下一個問題由讀者去修改上面的custom segue代碼來讓這個navigation bar 出現(xiàn)出來。

但是什么情況下用custom segue,什么情況下用transition動畫呢?Transitioning動畫更加的靈活,不像custom segue是在storyboard里定死的。你可以根據(jù)不同的情況設(shè)定你自己想要的動畫。

custom segue就是用來調(diào)用一些如:presentViewControllerdismissViewController之類的方法的。這些方法調(diào)用的時(shí)候兩個view controller之間的轉(zhuǎn)化動畫則使用上面的方法來做。他們的職責(zé)是不同的。

最后補(bǔ)充一點(diǎn)。也是一個發(fā)揮custom segue的作用的地方。在多個storyboard的情況下可以使用custom segue。步驟:(假設(shè)你已經(jīng)創(chuàng)建了另外一個storyboard,叫做Another.storyboard

  1. 拖一個storyboard reference到你現(xiàn)在的storyboard中。
  2. 點(diǎn)選這個storyboard reference,并在右側(cè)工具欄的Storyboard下填寫另外一個storyboard的名字,這里是Another.storyboard。
  3. ctrl+drag,從一個view controller中的按鈕等view連接到剛剛添加的storyboard reference上。(確保你的另外一個storyboard的view controller已經(jīng)設(shè)置為默認(rèn)啟動)
  4. 如果你要啟動的是另外一個storyboard的某一個特定的view controller,那么就可以寫一個custom segue了。在這個custom segue中初始化出另外一個storyboard,從中獲取到你要啟動的view controller,然后在custom segue的perform方法中完成controller跳轉(zhuǎn)的最后一步。

all code here

to be continued。。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • /* UIViewController is a generic controller base class th...
    DanDanC閱讀 2,061評論 0 2
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,692評論 4 61
  • 不管任何App,對于跳轉(zhuǎn)這個最基礎(chǔ)的動畫都是必須的,而iOS的轉(zhuǎn)場動畫不僅只是動畫,還涉及層級、跳轉(zhuǎn)限制、攜帶參數(shù)...
    HuberyQing閱讀 2,191評論 0 0
  • 概述 這篇文章,我將講述幾種轉(zhuǎn)場動畫的自定義方式,并且每種方式附上一個示例,畢竟代碼才是我們的語言,這樣比較容易上...
    伯恩的遺產(chǎn)閱讀 54,506評論 37 381
  • 我叫簡辭,遇見千花是在江蘇的錦溪。 錦溪是我一直很想去的小城,夾岸桃李紛披,滿溪躍金,燦爛若錦。 ...
    霓裳衣畫閱讀 308評論 0 0

友情鏈接更多精彩內(nèi)容