Swift: 是用Custom Segue还是用Transition动画

简介:

用一个很简单的场景做为例子:在storyboard上,你有用UINavigationController串起来两个UIViewController。这两个controller之间要互相跳转,A->B, B->A。跳转的时候默认的那个push来push去的效果你觉得很傻X,所以想换一个效果。比如,不那么二的fade in/out效果。

很多的例子会说写一个cusom的UIStoryboardSegue,然后在这个里面写一个UIView.animationWithDuration来实现这个效果。千万别这么干!从iOS7开始就有一个更加方便简洁的方法可以实现这个效果。

下面就开始介绍这个很牛X的方法。首先创建一个single view的项目。名称可以叫做transitionDemo。各个controller之间的关系是这样的:


一个UINavigationController作为启动controller,串起来一个UITableViewController(root controller)和一个UIViewController。在Prototype Cellsctrl+drag到view controller上,并选择show

下面分别为table view controller创建类TDTableViewController为view controller创建类TDViewController之后分别在storyboard里关联起来。把从table view controller到view controller的segue的identitifer设置为TDViewController


segue

接下来给TDTableViewController添加数据源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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并关联。

TDViewController代码中添加数据属性,并在viewDidLoad方法里给这label的text赋值:

1
2
3
4
5
6
7
8
9
10
11
12
class  TDViewController UIViewController  {
 
     @IBOutlet  weak  var  dataLabel UILabel !
 
     var  singleData String !
 
     override  func  viewDidLoad () {
         super . viewDidLoad ()
 
         self . dataLabel . text  self . singleData
     }
}

使用默认的segue,这里现在是show模式,跳转。并从table view controller出传递数据给view controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 使用segue跳转
     override  func  tableView ( tableView UITableView didSelectRowAtIndexPath  indexPath NSIndexPath ) {
         self . dataItem  self . tableViewDataSource ![ indexPath . row ]
         self . performSegueWithIdentifier ( "TDViewController" sender nil )
     }
 
// 传递数据
     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

直接用代码把两种方式都实现出来,分别代替上面使用的默认的实现方式。

首先弄一个custom segue。要开发一个custom segue,首先需要创建一个类并继承自UIStoryboardSegue,我们为这个类命名为DetailStoryboardSegue。在这个类中关键就是实现方法perform()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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跳转到B controller,那么source就是A,destination就是B。我们的fade in/out效果就是通过source和destination controller的view的切换和alpha实现的。
  2. 在window上做source和destination view的切换。把destination view覆盖到source view上。
  3. destination view的alpha在上一步设置为了0,也就是完全透明的。在动画中把destination view的alpha设置回完全不透明,把view呈现在用户面前达到fade in的效果。

实现完成后,在storyboard中把segue的Kind 设置为custom,然后给Class设置为类DetailStoryboardSegue

其实很简单,运行起来看看。


你会看到,运行的结果出了一点问题。之前在正确位置显示的label,在这里居然出现在了屏幕的顶端。这是因为前面TDViewController是用navigation controller的push出来的,屏幕的最顶端有一个navigation bar,所以label的top约束是有效的。而我们的custom segue的切换中并不存在navigation controller的push。而是简单的view的覆盖替换,没有navigation bar。所以labe的top约束失效了,直接被显示在了顶端。

这个错误其实引出了一个问题,但是这里我们暂时不做深入讨论。先看看Transitioning animtion动画是怎么运行的,然后我们讨论这个严肃的问题。

custom transitioning animation

实现自定义的切换动画就需要实现UIViewControllerAnimatedTransitioning这个protocol了。我们自定义一个类DetailTransitioningAnimator来实现这个protocol

这个protocol有两个方法是必须实现的,func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeIntervalfunc animateTransition(transitionContext: UIViewControllerContextTransitioning)。来看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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方法不是必需的,但是为了可以自定义动画的执行时间添加这个构造方法。
  2. transitioning动画的执行方法。在这个方法里实现我们在之前的custom segue里实现的效果。
  3. 注意这里,用的是let containerView = transitionContext.containerView()得到的container view。而不是之前用到的window。

下面把transitioning动画应用到代码中。在使用的时候需要实现UINavigationControllerDelegate。创建一个类NavigationControllerDelegate来实现这个protocol。

1
2
3
4
5
6
7
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应用在UINavigationController中。

  1. 把object这个东西拖动到项目中唯一存在的navigation controller上。
  2. 给刚刚的object设置custom class为类NavigationControllerDelegate

    只要关注右侧的Class的设置就可以。
  3. 给navigation controller的代理设置为刚刚拖动上去的delegate。

对上面的custom segue代码去掉,并稍作修改之后,运行起来。


木有问题,上面出现的错误也没有了。

这个问题不重要,但是还是要讨论一下:为什么custom segue会出现问题,而transitoning动画就没有问题呢?这里是因为实际上transitioning动画是在navigation controller的基础上改变的。Transitioning动画只是修改了navigation controller的push动画,改成了fade in的效果,而navigation controller的其他机制没有改动。custom segue则完全是两个view之间的动画。那么,这里就留下一个问题由读者去修改上面的custom segue代码来让这个navigation bar 出现出来。

但是什么情况下用custom segue,什么情况下用transition动画呢?Transitioning动画更加的灵活,不像custom segue是在storyboard里定死的。你可以根据不同的情况设定你自己想要的动画。

custom segue就是用来调用一些如:presentViewControllerdismissViewController之类的方法的。这些方法调用的时候两个view controller之间的转化动画则使用上面的方法来做。他们的职责是不同的。

最后补充一点。也是一个发挥custom segue的作用的地方。在多个storyboard的情况下可以使用custom segue。步骤:(假设你已经创建了另外一个storyboard,叫做Another.storyboard

  1. 拖一个storyboard reference到你现在的storyboard中。
  2. 点选这个storyboard reference,并在右侧工具栏的Storyboard下填写另外一个storyboard的名字,这里是Another.storyboard
  3. ctrl+drag,从一个view controller中的按钮等view连接到刚刚添加的storyboard reference上。(确保你的另外一个storyboard的view controller已经设置为默认启动)
  4. 如果你要启动的是另外一个storyboard的某一个特定的view controller,那么就可以写一个custom segue了。在这个custom segue中初始化出另外一个storyboard,从中获取到你要启动的view controller,然后在custom segue的perform方法中完成controller跳转的最后一步。

all code here

to be continued。。。

 

 
欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处!

















本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/5180225.html ,如需转载请自行联系原作者

相关文章
|
Swift Perl
Swift:Lottie实现tabBar切换动画
Swift:Lottie实现tabBar切换动画
626 0
Swift:Lottie实现tabBar切换动画
|
iOS开发
swift4.0 CAKeyframeAnimation,抖动动画
demo地址: https://github.com/weiman152/CAKeyframeAnimationDemo 继上一篇之后,这一篇我们来介绍使用CAKeyframeAnimation来实现简单的抖动动画。
1451 0
|
iOS开发
swift4.0 CAKeyframeAnimation动画使用
简单的平移、缩放和旋转以及渐隐渐显,都可以使用 UIView.animate(withDuration: 0.25, animations: { // 最终的结果显示 }) { (_) in // 动画完成之后要进行的操作 } 如果是稍微复杂的动画,比如沿着曲线运动,就可以考虑使用关键帧动画CAKeyframeAnimation实现。
1189 0
太阳升起并下落的小动画-SWIFT
一个小小的动画,太阳公公上山又下山。先上效果图。   用 lipecap 录的gif效果有点卡顿。好吧,说下如何实现的。 首先在一个大圆内先计算出内切九边形各个顶点的位置,接着连接相应的顶点变成一个九角星太阳的九条光芒,然后在九角星的中心画一个圈形的Layer,这样就大致画好了大阳的形状。
2718 0
|
iOS开发 Swift
iOS开发点滴 - 如何通过Segue写代码传递数据从一个ViewController到另一个ViewController(Swift代码)
1. 拖线 按住Control键,用鼠标从源控制器的某个控件开始,拖动到目的控制器   2. 选择弹出类型并设置Segue Identifier 在弹出的对话框中,选择“Selection Segue->Show” 见下图   设置Segue Identifier   3.
914 0
|
Swift
SWIFT中调用Segue的几个方法
场景1: 如图所示,在视图的第一个按钮处拉出一条Segue到另外一个视图,并给这个Segue命名,如SegueOne 此时可以用代码调用这个Segue切换视图: self.performSegueWithIdentifier("SegueOne", sender: nil)   场景2...
838 0