一直觉得之前写的图片浏览控件有问题。趁着空余时间,简单重构了图片显示和隐藏动画,
问题
前段时间,实现了点击显示大图的组件,支持左右滑动,双击放大常用手势。为了展示从小图到大图的动画,做了很多判断逻辑如: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动画 也是相同实现。