博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS9-自定义转场
阅读量:6936 次
发布时间:2019-06-27

本文共 8074 字,大约阅读时间需要 26 分钟。

参考书籍:iOS 9 by Tutorials

其实早在iOS7就推出了两个View之间的自定义过度转变。但是在iOS9中这种自定义转换进一步让你通过自定义segues来使过渡动画和视图控制器完全分离。

通过一个小的demo来了解一下吧。

Getting started

一个简单的项目PamperedPets,宠物照看的应用程序,完成后讲显示宠物的思想和她们的详细列表。

试着探索一下这个初始项目,看他是怎么运行的。

注意:当你打开这个项目的时候在Storyboard中会有些警告,不要惊慌。之后会解决的。

看一下Main.storyboard它有一些预先创建好的scenes,你将开始Animal Detail and Animal Photo scenes的工作。

What are segues?

Segues描述了场景之间的转换。他们显示为视图控制器场景之间的箭头,有几种类型的 Segues

  • Show : Pushes a scene from a navigation controller.

  • Show Detail: Replaces a scene detail when in a UISplitViewController

  • Present Modally: Presents a scene on top of the current scene.

  • Popover: Presents a scene as a popover on the iPad or full screen on the iPhone.

这篇文章我们仅仅自定义modal segues

A simple segue

Main.storyboard里选择Animal Detail View Controller,从Object Library拖拽出一个Tap Gesture Recognizer放在Pet Photo Thumbnail上。

接下来,Ctrl-dragTap Gesture Recognizer TO Tap Gesture Recognizer ,从弹出的菜单中选择present modally

完成上边的步骤就建立好了一个segue

选择 Animal Detail View ControllerAnimal Photo View Controller之间的segue.奖identifier设置为PhotoDetail

AnimalDetailViewController.swift中重写prepareForSegue(_:sender:)

override func prepareForSegue(segue: UIStoryboardSegue,  sender: AnyObject?) {  if segue.identifier == "PhotoDetail" {    let controller = segue.destinationViewController      as! AnimalPhotoViewController    controller.image = imageView.image  }}

现在你运行app并且点击照片,你将会看到一个大的图片出现。

你会发现你回不去了。那么此时你需要创建一个unwind segue。在AnimalDetailViewController.swift:中添加如下代码:

@IBAction func unwindToAnimalDetailViewController(  segue:UIStoryboardSegue) {  // placeholder for unwind segue}

对于一个简单的返场,在这个方法里你不需要添加任何的代码。 任何类似于这样的方法 @IBAction func methodName(segue:UIStoryboardSegue)都会被认为是Storyboard segue 的 unwind

Main.storyboard中选择Animal Photo View Controller scene.。从Object Library拖出来一个Tap Gesture Recognizer放在Pet Photo View上。接下来,Ctrl-drag从你的Tap Gesture Recognizer TO Exit,之后从弹出的菜单中选择unwindToAnimalDetailViewController

重新运行app,就会回发现你从大的图片中返回去了。

我们来探究一下这里发生了什么。当你点击详细视图中的缩略图的时候,手势识别就开始一个segue modalAnimalDetailViewControllerAnimalPhotoViewControllerAnimalDetailViewController在这里被称作为source view controller,而AnimalPhotoViewController责备称作为destination view controller。这个segue持有sourcedestination 的引用。

在这个过程中,目标视图控制器将会调用transition delegate来执行默认的Cover Vertical动画。

Your custom segue library

Main.storyboard中选择 PhotoDetail segue( the Animal Detail and the Animal Photo view controllers.)改变他的segue class DropSegue

再次运行项目,你会发现点击照片之后的动画已经完全改变了。

Creating a custom segue

现在你创建一个自己定义的的segue去更换DropSegue。并且将要创建一个如下图的转场动画.

创建一个自定义的seuge最难的部分就是术语,你将要使用的协议名字相当的长。

  • UIViewControllerTransitioningDelegate : 自定义转场使用该协议来完成动画。

  • UIViewControllerAnimatedTransitioning: 该动画对象通过该协议来描述动画。

  • UIViewControllerContextTransitioning: 这个上下文包含有关呈现,并介绍控制器和视图的详细信息;你把它传递给动画对象,为他们提供在其上执行动画的背景下。

在你开始之前,我们先看看创建一个转场的动画都需要那些步骤:

  1. 继承UIStoryboardSegue的子类,设置segue 为目标控制器的委托.

  2. 创建一个展示和消失的animator

  3. 定义动画效果及其持续时间,在动画中使用。

  4. 指导segue用于演示和解雇动画类。

  5. 最后在故事版中使用这个segue

Subclass UIStoryboardSegue

创建一个Cocoa Touch Class文件命名为ScaleSegue.swift继承UIStoryboardSegue

然后扩展这个类

extension ScaleSegue: UIViewControllerTransitioningDelegate {}

ScaleSegue这个类里重写父类的方法preform()

override func perform() {  destinationViewController.transitioningDelegate = self  super.perform()}

在这里你设置destination view controllertransitioning delegateScaleSegue

Create the animator

ScaleSegue.swift文件下边写如下一个类:

class ScalePresentAnimator : NSObject, UIViewControllerAnimatedTransitioning {}

你将使用ScalePresentAnimator这个类去展现modal view 。你也将建立一个消失时的动画,但是目前来说,一切都还是使用的默认的动画。需要注意的是Xcode中会抱怨这还不符合UIViewControllerAnimatedTransitioning协议;你只是要解决这个问题。

Define the animation

ScalePresentAnimator遵从UIViewControllerAnimatedTransitioning,你需要实现这个协议所必需的一些方法。

func transitionDuration(  transitionContext: UIViewControllerContextTransitioning?)  -> NSTimeInterval {  return 2.0}//规定动画持续的时间(一般时间比较短,这里设置的比较长,是为了明显的看到效果)

实际的动画效果:

func animateTransition(transitionContext: UIViewControllerContextTransitioning){                // 1 获取到目标视图的控制器和View        let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!        let toView = transitionContext.viewForKey(UITransitionContextFromViewKey)        // 2 添加 toView到transiton的context        if let toView = toView{            transitionContext.containerView()?.addSubview(toView)        }                //3 目标视图的初始状态是在屏幕左上角大小为零的一个矩形,当你更改视图的 Frame 时总是要去调用`layoutIfNeeded`来更新视图的约束        toView?.frame = .zero        toView?.layoutIfNeeded()                //4 这个动画必报就是把那个大小为零的矩形View变成最终的大小的一个动画        let duration = transitionDuration(transitionContext)        let finalFrame = transitionContext.finalFrameForViewController(toViewController)                UIView.animateWithDuration(duration, animations: { () -> Void in            toView?.frame = finalFrame            toView?.layoutIfNeeded()            }) { (_) -> Void in              //5 transitionContext要在动画结束时清理,调用completeTransition                transitionContext.completeTransition(true)        }            }

Set the animator in the segue

UIViewControllerTransitioningDelegate下添加下边这个方法。

extension ScaleSegue:UIViewControllerTransitioningDelegate{    func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {        return ScalePresentAnimator()    }}这是简单的在告诉`segue`在展现下一个view的时候使用你的`ScalePresentAnimator`动画

Use the segue in the storyboard

接下来在Main.storyboard中将PhotoDetail segue更换成ScaleSegue,同时呢,改变Presentation成为Form Sheet

接下来运行程序你就会看到下边的动画。

Passing data to animators

通过协议来传递数据。在ScaleSegue.swift里建立一个 protocol

protocol ViewScaleable{  var scaleView:UIView{get}}

通过扩展AniamalDetailViewController继承ViewScaleable协议

AniamalDetailViewController.swift中添加下边的代码

extension AniamalDetailViewController:ViewScaleable{  var scaleView: UIView {return imageView}}

ScaleSegue.swift文件中找到animateTransiton这个函数,在let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!下添加如下代码

//获取源视图的控制器和Viewlet fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)

toView?.frame = .zero替换为

var startFrame = CGRect.zero        if let fromViewController = fromViewController as? ViewScaleable{            startFrame = fromViewController.scaleView.frame        }else{            print("Warning: Controller \(fromViewController) does not"+"conform to ViewScaleable")        }        toView?.frame = startFrame

现在你重新运行你的app你就会发现当你单击图片之后,图片就会从原本的位置满满地放大。是不是这样子看起来更加的舒服呢?

Working with the view hierarchy

接下来我们做点小的改变来让你的app在iPad上运行起来别具一格。

找到animateTransition(_:)这个函数,在` toView?.frame = finalFrame

toView?.layoutIfNeeded()`后边紧跟着写上下边的代码

fromView?.alpha = 0.0

然后在动画完成的闭包里写上:

fromView?.alpha = 1.0 transitionContext.completeTransition(true)

接下来在你的iPad中运行你的app,你会看到下边的样子。

Handling embedded view controllers

接下来呢我们在Main.storyboard中选择 Navigation Controller ,在属性面板中勾选上Is Initial View Controller:这一项。

现在呢你运行程序你会首先看到一个动物的列表,你任意的点击一个,然后点击图片你会发现。奇怪怎么又变成了从左上角出现的动画了。

那是应为我把视图控制器现在嵌入了导航控制器里,使得呈现视图控制器的不是AnimalDetailViewController

那很简单我们来解决一下就好了。

我们在ScaleSegue.swift 这个文件里找到,

let fromViewController = transitionContext  .viewControllerForKey(    UITransitionContextFromViewControllerKey)!

将这句代码改为:

var fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!                if let fromNC = fromViewController as? UINavigationController{            if let controller = fromNC.topViewController{                fromViewController = controller            }        }

此刻你重新运行代码就会恢复原样喽。

当你嵌入了一个UITaBarController处理情况也是类似的。

Completing the scale segue dismissal

你会发现如果再次点击大图,大图消失的时候的动画还是之前的默认情况。我们接下来就完成消失时的动画吧。其实呢既然已经完成了presenting animator那么dismiss animator就简单了许多了吧。道理是一样的,那你就挑战一下自己吧。完成接下来的任务!

修改下边这个段代码

if let toView = toView{            transitionContext.containerView()?.addSubview(toView)        }

修改为

if let toView = toView,fromView = fromView{           transitionContext.containerView()?.insertSubview(toView, belowSubview: fromView)       }

转载地址:http://kxwnl.baihongyu.com/

你可能感兴趣的文章
usermod命令 用户密码管理 mkpasswd命令
查看>>
unit 5
查看>>
Linux常用命令——echo
查看>>
windows命令行下用netsh实现端口转发(端口映射)
查看>>
glusterFS
查看>>
Django 之 分页功能
查看>>
命令行设置NTFS权限
查看>>
装卡巴斯基时显示"360 safety guard"
查看>>
在Centos7上安装Rabbitmq 3.7
查看>>
一般群众也能好懂的云计算!太犀利了
查看>>
Linux服务管理工具
查看>>
生成树讲解——STP
查看>>
Ant开发之,使用Ant生成java api文档(javadoc)和zip压缩包
查看>>
Linux下基本操作笔记
查看>>
「自动化实战」手把手从python安装到setuptools、pip工具安装
查看>>
Thymeleaf 内置对象 之 获取web应用根路径
查看>>
骨干网中ISIS metric更改的几个问题
查看>>
ifconfig命令
查看>>
oracle——将指定用户下所有表的查询权限赋权给查询用户
查看>>
iOS react native 学习资料
查看>>