重新认识转场动画

一直觉得之前写的图片浏览控件有问题。趁着空余时间,简单重构了图片显示和隐藏动画,

问题

前段时间,实现了点击显示大图的组件,支持左右滑动,双击放大常用手势。为了展示从小图到大图的动画,做了很多判断逻辑如:present一个BrowserVC后,在UICollectionView delegate中,增加判断

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    //如果没有做过动画,且是当前点击的图片,则做一个从originFrame到 destinationFrame的动画
    //模拟从小图位移到大图的过程
    if indexPath.row == currentIndex, firstAnimataion {
        firstAnimataion = false
        guard let visibleCell = cell as? PWImageBrowserCell else {
            return
        }
        visibleCell.showBeginAnimation(from: originFrame)
    }
}

当拖动一张大图时,如果位移超过阀值 则大图放缩成小图 并且dismis当前VC。

private func hideBrowserAnimate(source: UIView, parent: UIView, originFrame: CGRect) {
    UIView.animate(withDuration: 0.3, animations: {
        source.frame = originFrame
        self.view.backgroundColor = UIColor.clear
        self.pageLabel?.alpha = 0
    }, completion: { (finish) in
        self.dismiss(animated: true, completion: nil)
    })
}

虽然present 和 dismis过程中都使用了自定义转场动画。但在 UIViewControllerAnimatedTransitioning 协议中只是简单显示隐藏背景颜色,有点大材小用。如何将上面在BrowserVC的两个动画转移到转场协议中?确保BrowserVC只负责图片展示,转场协议负责present 和 dismis的位移动画。

重构

如果使用自定义转场动画,VC的present 和 dismiss 时都会调用自己实现的转场代理 UIViewControllerTransitioningDelegate 来代替系统行为。

extension BrowserVC: UIViewControllerTransitioningDelegate {

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return PresentAnimator
} 
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return DismissAnimator
}

}

同时需要提供动画控制器,遵循 UIViewControllerAnimatedTransitioning , 实现具体的转场动画

class PresentAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning){ }
}        

所以只要将上面两个动画行为重新在动画控制器中实现,即可实现解耦。

prsent 动画

脑补一下present动画:首先是显示一个空白背景图,遮盖原VC。接着是一个UIImageView从原位置位移到中间,并且从小图放大成大图。所以代码可以(简写):

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

    let presentView = transitionContext.view(forKey: .to)
    transitionContext.containerView.addSubview(presentView!)

      //白色背景
    let bgView = UIView(frame: transitionContext.containerView.bounds)
    transitionContext.containerView.addSubview(bgView)


    //源图片
    let imageView = UIImageView(frame: CGRect.zero)
    imageView.frame = sourceFrame
    transitionContext.containerView.addSubview(imageView)


    //执行动画
    UIView.animate(withDuration: 0.3, animations: {
        imageView.frame = destinationFrame            
    }, completion: { finished in
          //动画结束时移除视图
        bgView.removeFromSuperview()
        imageView.removeFromSuperview()
        presentView?.isHidden = false
        transitionContext.completeTransition(true)
    })
}

同理dismiss动画 也是相同实现。

参考

转场详解

显示 Gitment 评论
0%