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

Posted 萧家大公子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 代码可能考虑的还不是很完善,我只是尽量的去完成.最后如果大家觉得我写得还行,麻烦大家关注我的官方博客,谢谢!!!!

以上是关于swift自定义转场动画(比较有难度)的主要内容,如果未能解决你的问题,请参考以下文章

浏览大图的一种实现方式

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

舒服了~玩转Android转场动画!

提交前 Swift segue 自定义转换布局

iOS 开发--转场动画

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