关于Custom Segues的初探笔记

开发过程中,我们往往会需要满足各种各样的跳转Segues的需求,光靠自带的那几个默认的转场似乎无法满足成天充满奇思妙想的产品定位人员(哥,我完全没有恶意。哥你说啥效果我就给你啥效果,你是我亲哥)
AppCoda上有一篇文章其实有专门非常详细的讲述了有关于Custom自定义转场Segues的一个小例子,国内也有译文版本,大概预览了下,自己也照着做了个小的Demo。我调重要的部分做为学习的笔记,也希望能给到看到本篇文章的您一点启发
Demo的主要功能其实很简单,建立三个View Controller,以不同的方式(向上滑动屏幕,向下滑动屏幕,点击按钮)进行自定义转场,当中会用到一些动画去呈现,三个View视图控制器都会根据跳转和返回视图(Segue <-> Unwind)呈现相应的数据。让我们了解转场和解除转场并在这之间进行数据呈现的所有过程。Demo的效果如图

1421999721366749.gif

首先在Main.storyboard里面创建好这三个ViewController,并添加三个类对这三个View视图进行绑定。再建立两个完整转场动作的四个自定义类(主视图跳转到第二视图,第二视图回到主视图的segue和unwind处理类。主视图点击按钮跳转到第三视图,第三视图回到主视图的segue和unwind处理类,他们都继承自UIStoryboardSegue),当然也要创建主视图segue连线创建custom转场方式并在转场中写好Identifier、绑定刚刚创建的segue处理类,在主视图的ViewController上创建一个@IBAction方法(解除转场依赖且必须要定义一个这样的方法)同样的segue连线把第二视图对象与Exit连线绑定刚刚创建在主视图里的@IBAction,并设置好Identifier。准备好了这一切,此时你已经设置好了第一视图和第二视图所需要的准备工作,让我们用代码的角度看下,ViewController#1 和 ViewController#2之间互相切换的时候过程是怎么样产生的
1.当app启动的时候,第一个视图viewDidLoad()被执行,向上滑动,创建好的识别器被启动:
var swipeGestureRecognizer:UISwipeGestureRecognizer=UISwipeGestureRecognizer(target:self, action:"showSecondViewController") swipeGestureRecognizer.direction=UISwipeGestureRecognizerDirection.Up self.view.addGestureRecognizer(swipeGestureRecognizer)
2.识别器启动了绑定的方法:
func showSecondViewController() { self.performSegueWithIdentifier("idFirstSegue", sender:self) }
上面的方法体里启动Identifier值为“idFirstSegue”的类(就是我们之前绑定的主视图向第二视图的segue处理类)
segue处理类做的事情就是重载父类UIStoryboardSegue的perform方法,此时此方法被启动:
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) } }
上面做的事情其实很简单,我们一行行解读
首先得到源视图窗口对象
得到目标视图窗口对象
得到屏幕的宽和高
指定应该出现视图的初始位置。把视图放在当前视图的正下方,设置frame让他在视图外
得到窗口的子视图对象
把窗口子视图对象放到目标视图
UIView启用animateWithDuration动画运动,修改两个视图的frame,把视图1移除到屏幕上方边缘,同时把视图2放到视图1原来的位置,在后面设置一个挂尾闭包执行显示目标控制器
以上就是一个完整的主图切换到第二视图的过程

我们来看看解除转场又是怎么样的:
我们现在要做的就是解除转场的准备工作:
当解除转场发生的时候,会自动调用我们在ViewController处理类中写好的重载segueForUnwindingToViewController方法,需要注意的是,他有三个参数分别是:fromViewController,toViewController还有identifier,字面意识上就可以解释:当前显示的视图控制器(消失的)、目标视图控制器(要它显示的)、第三个大家都懂(转场行为标示符),代码如下:
override func segueForUnwindingToViewController(toViewController: UIViewController, fromViewController: UIViewController, identifier: String?) -> UIStoryboardSegue { if let id = identifier{ if id == "idFirstSegueUnwind"{ //初始化解除转场自定义类的对象 let unwindSegue = FirstCustomSegueUnwind(identifier: id, source: fromViewController, destination: toViewController , performHandler: { () -> Void in }) return unwindSegue //返回这个解除专场自定义类对象 } }//同样返回此行为对象给父类的super方法 return super.segueForUnwindingToViewController(toViewController, fromViewController: fromViewController, identifier: identifier) }
上面代码上也很好理解,例子是主视图绑定的两个视图之间的跳转,所以需要通过判断identifier来判断返回到哪个解除转场中
完成了解除转场的准备工作,接着就是触发他了:

这个时候我们在第二视图的ViewController中所以,当用户向下滑动屏幕:

var swipeGestureRecognizer:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "showFirstViewController")//定义一个滑动手势,并绑定滑动的action方法是: showFirstViewController()方法 swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Down//滑动的方向,向下 self.view.addGestureRecognizer(swipeGestureRecognizer)//让视图添加识别动作为 (向下滑动) 的识别器
//当向下滑动,执行方法 func showFirstViewController(){ self.performSegueWithIdentifier("idFirstSegueUnwind", sender: self)//执行转场行为,转场行为是identifier里面的id }
(⊙o⊙)… 是不是这个时候觉得,怎么那么眼熟,是的,历史又开始重演
这个时候监听到用户滑动的识别器开始执行第二视图倒回第一视图:Unweid的解除转场处理类
同样跟上面的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) } }
相信大家也都能看出来Unwind唯一和Segue区别的就是:源视图和目标视图相反了(很好理解,就是现在需要第二视图转向第一视图)
animateWithDuration运动现在是把两个视图都同时做底部移动的动作

** 以上就是解除转场Unwind处理类的写法 **

转场运动的时候数据的传输也很简单,其实就是通过重载prepareForSegue这个方法进行的,这个方法会得到一个segue对象,所以我们可以以此segue对象判断是哪个identifier的ID的转场来进行label的显示值操作,代码如下:
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" } }
同样的解除转场时候,也可以在第二视图的处理类中写相同的代码,包括第三个视图的segue和Unwind这里就不再重复了
还有就是第三个视图转换到主视图时候的缩小动画,其实也不是什么很难的东西:
主视图跳转到第三个视图的动画实现:
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) }) }
第三个视图解除转场动画实现:
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) }
值得一提的是,当初我们创建的解除转场绑定@IBAction方法,其实她就是实现第二视图解除转场回到第一视图闪了一下红色背景,并实现第三视图回到第一视图 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!" } }
在这个IBAction中我们可以窥探并预想到转场之间我们需要和能做到的事情
文章已经看到这里的同学,应该可以完全理解上面的句子的含义

额,结尾了,好长好长,原文其实更长,很多都是重复的,所以我以自己的理解,把运行过程以分段式的方式去解释当中的代码(其实就是偷懒~ 😊哈哈)
相信也希望以上我的学习总结,能够帮助到你理解自定义转场的所有过程,同时在这个基础上创建更多的可能性

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容

  • 概述 这篇文章,我将讲述几种转场动画的自定义方式,并且每种方式附上一个示例,毕竟代码才是我们的语言,这样比较容易上...
    伯恩的遗产阅读 53,854评论 37 381
  • *7月8日上午 N:Block :跟一个函数块差不多,会对里面所有的内容的引用计数+1,想要解决就用__block...
    炙冰阅读 2,486评论 1 14
  • OC开发我们主要有以下三种自定义方法,供大家参考:Push & PopModalSegue 前两种大家都很熟悉,第...
    ScaryMonsterLyn阅读 1,646评论 1 3
  • 概述 这篇文章,我将讲述几种转场动画的自定义方式,并且每种方式附上一个示例,毕竟代码才是我们的语言,这样比较容易上...
    iOS_Developer阅读 1,026评论 0 4
  • 武志红老师的《巨婴国》就像一把手术刀,让人边读边反思反思自身,不时一拍大腿恍然大悟:说得太对了!原来有时感到憋屈是...
    古米莱阅读 505评论 3 9