一 转场效果图和采用转场方式
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