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添加数据源:

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赋值:

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:

// 使用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()

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)。来看代码:

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。

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跳转的最后一步。

to be continued。。。

时间: 2024-08-11 07:33:54

Swift: 是用Custom Segue还是用Transition动画的相关文章

css3 transition 动画基础

一.transition语法: transition:[<transition-property>||<transition-duration>|| <transition-timing-function>||<transition-delay>|| <transition-property>||<transition-duration>|| <transition-timing-function>||<transi

transition动画初级介绍

Transition:(过渡转变)让瞬间改变到目标值的事情,按照我们规定的过渡方式改变到目标值. 第一点:transition适合用于哪些属性? 适合所有的元素,包括::before和::after伪元素. Ps(::before和::after伪元素是在不改变html结构的情况下,适用于样式改变,所以在js中是对::before和::after进行不了事件绑定的.) var before=document.querySelector("::before");console.log(b

Android自定义Transition动画

本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发. 曾经(或者现在)很多人说起Android和iOS都会拿Android的UI设计来开黑, "你看看人家iOS的设计, 再来看看Android的, 差距怎么就这么大呢?", 对于这种说辞, 可以一句话来总结一下"他们还停留在4.X之前的时代". 自从Android5.0推出Material Design设计规范后, Android在设计上早已甩那个万年不变的iOS好几十条街! 以上纯

css3 Transition动画执行时有可能会出现闪烁的bug

css3 Transition动画执行时有可能会出现闪烁的bug,一般出现在开始的时候. 解决方法: 1.-webkit-backface-visibility: hidden; 2.-webkit-transform-style: preserve-3d; 需要应用在动画元素上. 第二种方法在低端android上会出现性能问题.所以推荐第一种方法

Swift游戏实战-跑酷熊猫 03 熊猫跑动动画

这节内容,我们一起来利用SKAction的来为熊猫创建动画,我们将学会通过纹理组产生动画,使用永远循环的SKAction让熊猫不停的跑动. 要点: 枚举的使用:用来记录熊猫的动作状态 enum Status:Int{ case run=1,jump,jump2,roll //当run被赋值为1的时候,后面的jump,jump2,roll会被自动赋值为2,3,4 } 通过序列帧纹理产生动画: SKAction.animatWithTextures(texture数组,播放的间隔时间) 永远循环动画

解决transition动画与display冲突的几种方法

如demo(如果没有显示,请查看源地址http://jsfiddle.net/ihardcoder/HNduT/2/)所示,基本的效果是在点击“Translate”按钮后,蓝色区域透明度变为0,然后隐藏display:none:点击Reset按钮后,首先显示蓝色区域display:block,然后透明度逐渐恢复至1,代码如下: 1 var btn1 = $("#testbtn1"); 2 var btn2 = $("#testbtn2"); 3 var contai

iOS涂色涂鸦效果、Swift仿喜马拉雅FM、抽屉转场动画、拖拽头像、标签选择器等源码

iOS精选源码 LeeTagView 标签选择控件 为您的用户显示界面添加美观的加载视图 Swift4: 可拖动头像,增加物理属性 Swift版抽屉效果,自定义转场动画管理器 Swift 仿写喜马拉雅FM 可能是最好用的引导页 涂色涂鸦实现 iOS优质博客 iOS WKWebView的使用--API篇 WKWebView是苹果在iOS 8之后推出的框架WebKit中的浏览器控件, 其加载速度比UIWebView快了许多, 但内存占用率却下降很多, 也解决了加载网页时的内存泄露问题. 现在的项目大

CSS3 transition动画、transform变换、animation动画

一.CSS3 transition动画 transition可以实现动态效果,实际上是一定时间之内,一组css属性变换到另一组属性的动画展示过程. 属性参数: 1.transition-property 设置过渡的属性,比如:width height background-color2.transition-duration 设置过渡的时间,比如:1s 500ms3.transition-timing-function 设置过渡的运动方式 linear 匀速 ease 开始和结束慢速 ease-

transition动画

1.transition-property设置过度的属性,比如:width height background-color 2.transition-duration 设置 过度的时间  比如1S500MS 3.transition-timing-function设置 过度运动的方式,常用的有:linear(匀速)   ease(缓冲运动) 4.transition-delay 设置动画的延迟 5.transition :property duration timing-function del