swift自定义转场动画(比较有难度)

一 转场效果图和采用转场方式

1 转场效果图 :

2 采用方式 (方法):

—-> 2.1 自定义转场动画
—-> 2.2 协议

二 转场实现需要获取的东西

1 获取转场前图片的frame

2 设置一张临时imageView作为转场图片(图片并不是真实存在的)

3 获取图片放大展示的frame

三 转场图解

四 转场动画思想

1 通过在实现转场动画的类中定义协议方法,定义代理属性,明确谁可以提供需要的frame和imageView,将对方设置为代理,让代理提供需求,达到转场目的.

2 明确代码的书写位置,在哪里可以实现转场动画.

五 协议(弹出动画)

1 协议书写位置

2 协议方法 : (可以让外界提供需要的东西)

 //MARK: - 定义协议用来拿到图片起始位置;最终位置和图片
protocol PersentDelegate : class {
    //起始位置
    func homeRect(indexPath : NSIndexPath) ->CGRect
    //展示图片的位置
    func photoBrowserRect(indexPath : NSIndexPath) ->CGRect
    //获取imageView(用这张临时的图片来实现动画效果)
    func imageView(indexPath : NSIndexPath) ->UIImageView
}

3 在该该类中设置代理

//设置代理(代理一般都是用weak修饰)
    weak var presentDelegate : PersentDelegate?

4 实现转场动画的代码书写位置 : (上一篇送已经说明转场动画的书写位置)

—-> 4.1 得到一张图片
—-> 4.2 得到home中图片的起始frame
—-> 4.3 展示图片的frame
—-> 4.4 完成动画
—-> 4.5 图片由透明到清晰,通过alpha来实现
//获取转场的上下文:可以通过上下文获取到执行动画的view
    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        isPresented ? animateForPresentView(transitionContext) : animateForDismissed(transitionContext)
    }
    //弹出
    func animateForPresentView(transitionContext: UIViewControllerContextTransitioning) {
        //1 取出弹出的view
        let presentView = transitionContext.viewForKey(UITransitionContextToViewKey)!
        //2 将弹出的view加入到contentView中
        transitionContext.containerView()?.addSubview(presentView)
        //3 执行动画
        //3.1 获取需要执行的imageView
        guard let persentDelegate = presentDelegate else {
            return
        }
        guard let indexPath = indexPath else {
            return
        }
        //调用方法,得到一张图片
        let imageView = persentDelegate.imageView(indexPath)
        //将图片添加到父控件中
        transitionContext.containerView()?.addSubview(imageView)
        //设置imageView的起始位置
        imageView.frame = persentDelegate.homeRect(indexPath)
        presentView.alpha = 0
        //设置弹出动画的时候背景颜色为黑色
        transitionContext.containerView()?.backgroundColor = UIColor.blackColor()
        UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
            //设置展示图片的位置
            imageView.frame = persentDelegate.photoBrowserRect(indexPath)
            }) { (_) -> Void in
            //修改图片透明度
            presentView.alpha = 1.0
            //移除图片
            imageView.removeFromSuperview()
            transitionContext.containerView()?.backgroundColor = UIColor.clearColor()
            //完成动画
            transitionContext.completeTransition(true)
        }
    }

六 传值

1 如何通过协议获取home中某张图片的frame值?

—-> 1.1 通过在扎UN擦汗那个动画实现的方法中设置一个indexPath(用于外界传入角标)来确定frame
class PhotoBrowserAnimator: NSObject {
    //设置角标
    var indexPath : NSIndexPath?
}

2 设置代理对象

//Mark: - 点击图片,弹出控制器
extension HomeViewController {
        //定义为私有的
        private func showPhotoBrowser( indexPath : NSIndexPath) {
        //将图片的角标传入
        photoBrowserAnimator.indexPath = indexPath
        //设置代理
        photoBrowserAnimator.presentDelegate = self
}

3 实现协议方法(该部分代码需要写的比较多,所以扩充一个方法)

—-> 3.1 获取home中图片具体的frame值(重点掌握 : 如何将cell的frame值转为cell在window中的坐标值)
//MARK: - 实现协议中的方法
extension HomeViewController : PersentDelegate {
    func homeRect(indexPath: NSIndexPath) -> CGRect {
        //取出cell
        let cell = (collectionView?.cellForItemAtIndexPath(indexPath))!
        //将cell的frame值转成cell在window中的坐标
        let homeFrame = collectionView!.convertRect(cell.frame, toCoordinateSpace: UIApplication.sharedApplication().keyWindow!)
        //返回具体的尺寸
        return homeFrame
    }
}
—-> 3.2 返回一张作为转场动画的临时图片(在同一个方法中书写)
func imageView(indexPath: NSIndexPath) -> UIImageView {
        //创建imageView对象
        let imageView = UIImageView()
        //取出cell
        let cell = (collectionView?.cellForItemAtIndexPath(indexPath))! as! HomeViewCell
        //取出cell中显示的图片
        let image = cell.imageView.image
        //设置图片
        imageView.image = image
        //设置imageView相关属性(拉伸模式)
        imageView.contentMode = .ScaleAspectFill
        //将多余的部分裁剪
        imageView.clipsToBounds = true
        //返回图片
        return imageView
    }
—-> 3.3 返回最终展示图片的frame值
func photoBrowserRect(indexPath: NSIndexPath) -> CGRect {
        //取出cell
        let cell = (collectionView?.cellForItemAtIndexPath(indexPath))! as! HomeViewCell
        //取出cell中显示的图片
        let image = cell.imageView.image
        //计算方法后的图片的frame
        return calculate(image!)
    }
—-> 3.4 如何计算根据传入的图片值,得出最终展示的图片值? 解答如下

4 创建一个类,内部只是提供一个计算展示图片的frame(外界也是可以调用的)

—-> 4.1 类中代码 :
import UIKit

//MARK: - 设置图片的frame
    func calculate (image : UIImage) ->CGRect {
    let imageViewX : CGFloat = 0.0
    let imageViewW : CGFloat = UIScreen.mainScreen().bounds.width
    let imageViewH : CGFloat = imageViewW / image.size.width * image.size.height
    let imageViewY : CGFloat = (UIScreen.mainScreen().bounds.height - imageViewH) * 0.5

    return CGRect(x: imageViewX, y: imageViewY, width: imageViewW, height: imageViewH)
}

七 协议(消失动画)

1 和弹出动画一样的道理,但是该部分只需要获取当前展示图片的indexPath和imageView

2 明确谁可以提供这部分需求;将代理设置给谁?代码书写的位置?

3 定义协议 : (在转场动画的类中实现协议方法定义)

//MARK: - 定义协议来实现消失动画
protocol DismissDelegate : class {
    //获取当前显示的图片的角标
    func currentIndexPath() ->NSIndexPath
    //获取imageView(用这张临时的图片来实现动画效果)
    func imageView() ->UIImageView
}

4 设置代理

class PhotoBrowserAnimator: NSObject {
    //设置消失动画的代理
    weak var dismissDelegate : DismissDelegate?
}

5 消失动画

—-> 5.1 获取当前展示图片位置
—-> 5.2 获取imageView
—-> 5.3 根据用户滑动后的角标,来显示最终消失动画后图片在home的位置(用presentDelegate代理)
—-> 5.4 代码 :
//消失
    func animateForDismissed (transitionContext: UIViewControllerContextTransitioning) {
        //1 取出消失的view
        let dismissView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
        dismissView.removeFromSuperview()
        //2 执行动画
        //2.1 校验(如果没有值,就直接返回)
        guard let dismissDelegate = dismissDelegate else {
            return
        }
        guard let presentDelegate = presentDelegate else {
            return
        }
        // 2.2获取一张图片
        let imageView = dismissDelegate.imageView()
        // 2.3将图片添加到父控件中
        transitionContext.containerView()?.addSubview(imageView)
        // 2.4 获取当前正在显示的图片的角标
        let indexPath = dismissDelegate.currentIndexPath()
        // 2.5 设置起始位置
        imageView.frame = presentDelegate.photoBrowserRect(indexPath)
        //执行动画
        UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
           imageView.frame = presentDelegate.homeRect(indexPath)
            }) { (_) -> Void in
            //完成动画
            transitionContext.completeTransition(true)
        }
    }
}

6 设置代理(因为创建PhotoBrowserController是在HomeViewController类中创建的)

//Mark: - 点击图片,弹出控制器
extension HomeViewController {
        //定义为私有的
        private func showPhotoBrowser( indexPath : NSIndexPath) {
        //创建控制器对象
        let showPhotoBrowserVC = PhotoBrowserController()
        //设置dismiss的代理
        photoBrowserAnimator.dismissDelegate = showPhotoBrowserVC
}
—-> 6.1 明确只有展示图片的控制器才能提供这些需求,所以将其设置为代理

7 实现代理方法

—-> 7.1 根据cell获取当前的indexPath值
//Mark: - 实现dismiss的代理方法
extension PhotoBrowserController : DismissDelegate {
    func currentIndexPath() -> NSIndexPath {
        //获取当前正在显示的cell
        let cell = collectionView.visibleCells().first!
        //根据cell获取indexPath
        return collectionView.indexPathForCell(cell)!
    }
}    
—-> 7.2 返回一张图片(和7.1写在同一个方法中)
func imageView() -> UIImageView {
        //创建UIImageView对象
        let imageView = UIImageView()
        //获取正在显示的cell
        let cell = collectionView.visibleCells().first! as! PhotoBrowerViewCell
        //取出cell中的图片
        imageView.image = cell.imageView.image
        //设置图片的属性
        imageView.contentMode = .ScaleAspectFill
        //将多出的图片裁剪
        imageView.clipsToBounds = true
        //返回图片
        return imageView
    }

八 细节处理

1 在弹出动画中点击home中的图片通过转场动画成大图,那么会发现在图片变大的过程中home背景并没有消失,而是完全展示了大图才消失的.怎么解决?

—-> 1.1 通过在动画的时候将背景颜色设置为黑色,遮住home
//设置弹出动画的时候背景颜色为黑色
        transitionContext.containerView()?.backgroundColor = UIColor.blackColor()
—-> 1.2 等动画完成后再讲颜色设置为clearColor
transitionContext.containerView()?.backgroundColor = UIColor.clearColor()

2 一定不要忘记转场动画完成后需要告诉系统转场动画完成,否则会出现莫名其妙的情况.

3 尽量的要对代理是否有值进行校验,但是也可以通过强制解包(不是很安全),还是通过判断安全点.

九 总结

1 该部分比较有难度,是对上一篇转场动画的加强.如果有看不懂的读者也是很正常,有什么问题可以直接给我留言,我会尽可能的解答.

2 代码可能考虑的还不是很完善,我只是尽量的去完成.最后如果大家觉得我写得还行,麻烦大家关注我的官方博客,谢谢!!!!

时间: 2024-10-24 07:38:04

swift自定义转场动画(比较有难度)的相关文章

iOS自定义转场动画实战讲解

iOS自定义转场动画实战讲解 转场动画这事,说简单也简单,可以通过presentViewController:animated:completion:和dismissViewControllerAnimated:completion:这一组函数以模态视图的方式展现.隐藏视图.如果用到了navigationController,还可以调用pushViewController:animated:和popViewController这一组函数将新的视图控制器压栈.弹栈. 下图中所有转场动画都是自定义的

关于自定义转场动画,我都告诉你。

原文出处: 伯恩的遗产(@翁呀伟呀 ) 概述 这篇文章,我将讲述几种转场动画的自定义方式,并且每种方式附上一个示例,毕竟代码才是我们的语言,这样比较容易上手.其中主要有以下三种自定义方法,供大家参考: Push & Pop Modal Segue 前两种大家都很熟悉,第三种是 Stroyboard 中的拖线,属于 UIStoryboardSegue 类,通过继承这个类来自定义转场过程动画. Push & Pop 首先说一下 Push & Pop 这种转场的自定义,操作步骤如下: 创

自定义转场动画--Swift3.0版本

转场动画这事,说简单也简单,可以通过presentViewController:animated:completion:和dismissViewControllerAnimated:completion:这一组函数以模态视图的方式展现.隐藏视图.如果用到了navigationController,还可以调用pushViewController:animated:和popViewController这一组函数将新的视图控制器压栈.弹栈. 下图中所有转场动画都是自定义的动画,这些效果如果不用自定义动

关于自定义转场动画,我都告诉你

作者:@翁呀伟呀 概述 这篇文章,我将讲述几种转场动画的自定义方式,并且每种方式附上一个示例,毕竟代码才是我们的语言,这样比较容易上手.其中主要有以下三种自定义方法,供大家参考: Push & Pop Modal Segue 前两种大家都很熟悉,第三种是 Stroyboard 中的拖线,属于 UIStoryboardSegue 类,通过继承这个类来自定义转场过程动画. Push & Pop 首先说一下 Push & Pop 这种转场的自定义,操作步骤如下: 1. 创建一个文件继承自

iOS 自定义转场动画篇

前言: 自定义转场动画其实并不难, 关键在于能够明白思路, 也就是操作步骤. 本篇博客主要以present转场动画为例, 进行分析, 操作, 如有错误欢迎简信与我交流. 不进行修改的话, presentViewController:animated:completion:相信这个方法很多人都是用过, 称作模态推出界面, 默认都是从屏幕下方推出新的控制器. 自定义的目的就是为了修改固定的推出方式, 同时加上你想要的动画. 一个关键的概念: UIViewControllerAnimatedTrans

指纹解锁以及自定义转场动画

这两天工作不忙,就去研究了下指纹解锁以及转场动画,其实这里面的单单一个动画就可以说半天了,但是今天不去说动画.这个以后有时间在讲讲.. 指纹解锁,很简单,官方文档上说的很清楚,其实我们要做的几乎没有.. 1.导入 #import <LocalAuthentication/LocalAuthentication.h> 这个框架 2.导入下面那段代码,指纹解锁就完成了, so easy!!!!然后再实现自己大逻辑就可以了.. LAContext *myContext = [[LAContext a

iOS 利用UIPresentationController自定义转场动画

1. 系统默认modal出来的动画效果默认是从屏幕底部爬出来的 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ secondVC *second = [[secondVC alloc] init];  [self presentViewController:second animated:YES completion:nil]; } 2. 想自定义转场动画,首先设置 展示样式 和 过渡代理 secondVC *s

第六十五篇、iOS7自定义转场动画

自定义转场动画,在iOS7及以上的版本才开始出现的,在一些应用中,我们常常需要定制自定义的的跳转动画 1.遵守协议:<UIViewControllerAnimatedTransitioning> 2.协议的方法主要的是两个: // 指定动画的持续时长 1. (NSTimeInterval)transitionDuration: // 转场动画的具体内容 2. (void)animateTransition:(id <UIViewControllerContextTransitioning

【Swift】IOS开发中自定义转场动画

在IOS开发中,我们model另外一个控制器的时候,一般都使用的自定义的转场动画. 其实我们可以自定义一些转场动画.达到不同的转场效果. 步骤如下:(photoBrowser是目标控制器) 1.在源控制器中,设置目标控制器的转场代理为 self 1 //设置Model转场代理 2  photoBrowser.transitioningDelegate = self 2.同时设置目标控制器的model类型 1 //设置Model类型 2 photoBrowser.modalPresentation