開發(fā)過(guò)程中漫雕,我們往往會(huì)需要滿足各種各樣的跳轉(zhuǎn)Segues的需求秉颗,光靠自帶的那幾個(gè)默認(rèn)的轉(zhuǎn)場(chǎng)似乎無(wú)法滿足成天充滿奇思妙想的產(chǎn)品定位人員(哥稽穆,我完全沒有惡意。哥你說(shuō)啥效果我就給你啥效果米丘,你是我親哥)
AppCoda上有一篇文章其實(shí)有專門非常詳細(xì)的講述了有關(guān)于Custom自定義轉(zhuǎn)場(chǎng)Segues的一個(gè)小例子剑令,國(guó)內(nèi)也有譯文版本,大概預(yù)覽了下拄查,自己也照著做了個(gè)小的Demo吁津。我調(diào)重要的部分做為學(xué)習(xí)的筆記,也希望能給到看到本篇文章的您一點(diǎn)啟發(fā)
Demo的主要功能其實(shí)很簡(jiǎn)單堕扶,建立三個(gè)View Controller碍脏,以不同的方式(向上滑動(dòng)屏幕,向下滑動(dòng)屏幕稍算,點(diǎn)擊按鈕)進(jìn)行自定義轉(zhuǎn)場(chǎng)典尾,當(dāng)中會(huì)用到一些動(dòng)畫去呈現(xiàn),三個(gè)View視圖控制器都會(huì)根據(jù)跳轉(zhuǎn)和返回視圖(Segue <-> Unwind)呈現(xiàn)相應(yīng)的數(shù)據(jù)糊探。讓我們了解轉(zhuǎn)場(chǎng)和解除轉(zhuǎn)場(chǎng)并在這之間進(jìn)行數(shù)據(jù)呈現(xiàn)的所有過(guò)程钾埂。Demo的效果如圖
首先在Main.storyboard里面創(chuàng)建好這三個(gè)ViewController河闰,并添加三個(gè)類對(duì)這三個(gè)View視圖進(jìn)行綁定。再建立兩個(gè)完整轉(zhuǎn)場(chǎng)動(dòng)作的四個(gè)自定義類(主視圖跳轉(zhuǎn)到第二視圖褥紫,第二視圖回到主視圖的segue和unwind處理類姜性。主視圖點(diǎn)擊按鈕跳轉(zhuǎn)到第三視圖,第三視圖回到主視圖的segue和unwind處理類髓考,他們都繼承自UIStoryboardSegue)部念,當(dāng)然也要?jiǎng)?chuàng)建主視圖segue連線創(chuàng)建custom轉(zhuǎn)場(chǎng)方式并在轉(zhuǎn)場(chǎng)中寫好Identifier、綁定剛剛創(chuàng)建的segue處理類氨菇,在主視圖的ViewController上創(chuàng)建一個(gè)@IBAction方法(解除轉(zhuǎn)場(chǎng)依賴且必須要定義一個(gè)這樣的方法)儡炼,同樣的segue連線把第二視圖對(duì)象與Exit連線綁定剛剛創(chuàng)建在主視圖里的@IBAction,并設(shè)置好Identifier查蓉。準(zhǔn)備好了這一切射赛,此時(shí)你已經(jīng)設(shè)置好了第一視圖和第二視圖所需要的準(zhǔn)備工作,讓我們用代碼的角度看下奶是,ViewController#1 和 ViewController#2之間互相切換的時(shí)候過(guò)程是怎么樣產(chǎn)生的
1.當(dāng)app啟動(dòng)的時(shí)候,第一個(gè)視圖viewDidLoad()被執(zhí)行竣灌,向上滑動(dòng)聂沙,創(chuàng)建好的識(shí)別器被啟動(dòng):
var swipeGestureRecognizer:UISwipeGestureRecognizer=UISwipeGestureRecognizer(target:self, action:"showSecondViewController") swipeGestureRecognizer.direction=UISwipeGestureRecognizerDirection.Up self.view.addGestureRecognizer(swipeGestureRecognizer)
2.識(shí)別器啟動(dòng)了綁定的方法:
func showSecondViewController() { self.performSegueWithIdentifier("idFirstSegue", sender:self) }
上面的方法體里啟動(dòng)Identifier值為“idFirstSegue”的類(就是我們之前綁定的主視圖向第二視圖的segue處理類)
segue處理類做的事情就是重載父類UIStoryboardSegue的perform方法,此時(shí)此方法被啟動(dòng):
override func perform() { var firstVCView =self.sourceViewController.view as UIView! var secondVCView =self.destinationViewController.view as UIView! let screenWidth =UIScreen.mainScreen().bounds.size.width let screenHeight =UIScreen.mainScreen().bounds.size.height secondVCView.frame=CGRectMake(0.0, screenHeight, screenWidth, screenHeight) let window =UIApplication.sharedApplication().keyWindow window?.insertSubview(secondVCView, aboveSubview: firstVCView) UIView.animateWithDuration(0.4, animations: { () ->Voidin firstVCView.frame=CGRectOffset(firstVCView.frame,0.0, -screenHeight) secondVCView.frame=CGRectOffset(secondVCView.frame,0.0, -screenHeight) }) { (Finished) ->Voidin self.sourceViewController.presentViewController(self.destinationViewController as! UIViewController, animated:false,completion:nil) } }
上面做的事情其實(shí)很簡(jiǎn)單初嘹,我們一行行解讀
首先得到源視圖窗口對(duì)象
得到目標(biāo)視圖窗口對(duì)象
得到屏幕的寬和高
指定應(yīng)該出現(xiàn)視圖的初始位置及汉。把視圖放在當(dāng)前視圖的正下方,設(shè)置frame讓他在視圖外
得到窗口的子視圖對(duì)象
把窗口子視圖對(duì)象放到目標(biāo)視圖
UIView啟用animateWithDuration動(dòng)畫運(yùn)動(dòng)屯烦,修改兩個(gè)視圖的frame坷随,把視圖1移除到屏幕上方邊緣,同時(shí)把視圖2放到視圖1原來(lái)的位置,在后面設(shè)置一個(gè)掛尾閉包執(zhí)行顯示目標(biāo)控制器
以上就是一個(gè)完整的主圖切換到第二視圖的過(guò)程
我們來(lái)看看解除轉(zhuǎn)場(chǎng)又是怎么樣的:
我們現(xiàn)在要做的就是解除轉(zhuǎn)場(chǎng)的準(zhǔn)備工作:
當(dāng)解除轉(zhuǎn)場(chǎng)發(fā)生的時(shí)候驻龟,會(huì)自動(dòng)調(diào)用我們?cè)赩iewController處理類中寫好的重載segueForUnwindingToViewController方法温眉,需要注意的是,他有三個(gè)參數(shù)分別是:fromViewController翁狐,toViewController還有identifier类溢,字面意識(shí)上就可以解釋:當(dāng)前顯示的視圖控制器(消失的)、目標(biāo)視圖控制器(要它顯示的)露懒、第三個(gè)大家都懂(轉(zhuǎn)場(chǎng)行為標(biāo)示符)闯冷,代碼如下:
override func segueForUnwindingToViewController(toViewController: UIViewController, fromViewController: UIViewController, identifier: String?) -> UIStoryboardSegue { if let id = identifier{ if id == "idFirstSegueUnwind"{ //初始化解除轉(zhuǎn)場(chǎng)自定義類的對(duì)象 let unwindSegue = FirstCustomSegueUnwind(identifier: id, source: fromViewController, destination: toViewController , performHandler: { () -> Void in }) return unwindSegue //返回這個(gè)解除專場(chǎng)自定義類對(duì)象 } }//同樣返回此行為對(duì)象給父類的super方法 return super.segueForUnwindingToViewController(toViewController, fromViewController: fromViewController, identifier: identifier) }
上面代碼上也很好理解,例子是主視圖綁定的兩個(gè)視圖之間的跳轉(zhuǎn)懈词,所以需要通過(guò)判斷identifier來(lái)判斷返回到哪個(gè)解除轉(zhuǎn)場(chǎng)中
完成了解除轉(zhuǎn)場(chǎng)的準(zhǔn)備工作蛇耀,接著就是觸發(fā)他了:
這個(gè)時(shí)候我們?cè)诘诙晥D的ViewController中所以,當(dāng)用戶向下滑動(dòng)屏幕:
var swipeGestureRecognizer:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "showFirstViewController")//定義一個(gè)滑動(dòng)手勢(shì)坎弯,并綁定滑動(dòng)的action方法是: showFirstViewController()方法 swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Down//滑動(dòng)的方向纺涤,向下 self.view.addGestureRecognizer(swipeGestureRecognizer)//讓視圖添加識(shí)別動(dòng)作為 (向下滑動(dòng)) 的識(shí)別器
//當(dāng)向下滑動(dòng)译暂,執(zhí)行方法 func showFirstViewController(){ self.performSegueWithIdentifier("idFirstSegueUnwind", sender: self)//執(zhí)行轉(zhuǎn)場(chǎng)行為,轉(zhuǎn)場(chǎng)行為是identifier里面的id }
(⊙o⊙)… 是不是這個(gè)時(shí)候覺得洒琢,怎么那么眼熟秧秉,是的,歷史又開始重演
這個(gè)時(shí)候監(jiān)聽到用戶滑動(dòng)的識(shí)別器開始執(zhí)行第二視圖倒回第一視圖:Unweid的解除轉(zhuǎn)場(chǎng)處理類
同樣跟上面的segue處理類相同衰抑,也是重寫父類的perform方法
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) } }
相信大家也都能看出來(lái)Unwind唯一和Segue區(qū)別的就是:源視圖和目標(biāo)視圖相反了(很好理解象迎,就是現(xiàn)在需要第二視圖轉(zhuǎn)向第一視圖)
animateWithDuration運(yùn)動(dòng)現(xiàn)在是把兩個(gè)視圖都同時(shí)做底部移動(dòng)的動(dòng)作
** 以上就是解除轉(zhuǎn)場(chǎng)Unwind處理類的寫法 **
轉(zhuǎn)場(chǎng)運(yùn)動(dòng)的時(shí)候數(shù)據(jù)的傳輸也很簡(jiǎn)單,其實(shí)就是通過(guò)重載prepareForSegue這個(gè)方法進(jìn)行的呛踊,這個(gè)方法會(huì)得到一個(gè)segue對(duì)象砾淌,所以我們可以以此segue對(duì)象判斷是哪個(gè)identifier的ID的轉(zhuǎn)場(chǎng)來(lái)進(jìn)行l(wèi)abel的顯示值操作,代碼如下:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "idFirstSegue" { let secondViewController = segue.destinationViewController as! SecondViewController secondViewController.message = "Hello from the 1st View Controller" } }
同樣的解除轉(zhuǎn)場(chǎng)時(shí)候谭网,也可以在第二視圖的處理類中寫相同的代碼汪厨,包括第三個(gè)視圖的segue和Unwind這里就不再重復(fù)了
還有就是第三個(gè)視圖轉(zhuǎn)換到主視圖時(shí)候的縮小動(dòng)畫,其實(shí)也不是什么很難的東西:
主視圖跳轉(zhuǎn)到第三個(gè)視圖的動(dòng)畫實(shí)現(xiàn):
thirdVCView.transform = CGAffineTransformScale(thirdVCView.transform, 0.001, 0.001) UIView.animateWithDuration(0.5, animations: { () -> Void in firstVCView.transform = CGAffineTransformScale(thirdVCView.transform, 0.001, 0.001) }) { (Finished) -> Void in UIView.animateWithDuration(0.5, animations: { () -> Void in thirdVCView.transform = CGAffineTransformIdentity }, completion: { (Finished) -> Void in firstVCView.transform = CGAffineTransformIdentity self.sourceViewController.presentViewController(self.destinationViewController as! UIViewController, animated: false, completion: nil) }) }
第三個(gè)視圖解除轉(zhuǎn)場(chǎng)動(dòng)畫實(shí)現(xiàn):
firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, screenHeight) firstVCView.transform = CGAffineTransformScale(firstVCView.transform, 0.001, 0.001) let window = UIApplication.sharedApplication().keyWindow window?.insertSubview(firstVCView, aboveSubview: thirdVCView) UIView.animateWithDuration(0.5, animations: { () -> Void in thirdVCView.transform = CGAffineTransformScale(thirdVCView.transform, 0.001, 0.001) thirdVCView.frame = CGRectOffset(thirdVCView.frame, 0.0, -screenHeight) firstVCView.transform = CGAffineTransformIdentity firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, -screenHeight) }) { (Finished) -> Void in self.sourceViewController.dismissViewControllerAnimated(false, completion: nil) }
值得一提的是愉择,當(dāng)初我們創(chuàng)建的解除轉(zhuǎn)場(chǎng)綁定@IBAction方法劫乱,其實(shí)她就是實(shí)現(xiàn)第二視圖解除轉(zhuǎn)場(chǎng)回到第一視圖閃了一下紅色背景,并實(shí)現(xiàn)第三視圖回到第一視圖 welcome back那句label的方法:
@IBAction func returnFromSegueActions(sender: UIStoryboardSegue){ if sender.identifier == "idFirstSegueUnwind" { let originalColor = self.view.backgroundColor self.view.backgroundColor = UIColor.redColor() UIView.animateWithDuration(1.0, animations: { () -> Void in self.view.backgroundColor = originalColor }) } else{ self.lblMessage.text = "Welcome back!" } }
在這個(gè)IBAction中我們可以窺探并預(yù)想到轉(zhuǎn)場(chǎng)之間我們需要和能做到的事情
文章已經(jīng)看到這里的同學(xué),應(yīng)該可以完全理解上面的句子的含義
額锥涕,結(jié)尾了衷戈,好長(zhǎng)好長(zhǎng),原文其實(shí)更長(zhǎng),很多都是重復(fù)的层坠,所以我以自己的理解殖妇,把運(yùn)行過(guò)程以分段式的方式去解釋當(dāng)中的代碼(其實(shí)就是偷懶~ ??哈哈)
相信也希望以上我的學(xué)習(xí)總結(jié),能夠幫助到你理解自定義轉(zhuǎn)場(chǎng)的所有過(guò)程破花,同時(shí)在這個(gè)基礎(chǔ)上創(chuàng)建更多的可能性