文章目录
前言
上文介绍了非交互的Modally转场动画,初步了解到了视觉动画的效果。本文是要在其基础上添加Pan平移手势交互,完成自定义的交互动画。
一、Modally转场交互动画
交互动画为持续性动画,通过实现系统中(百分比驱动交互过渡类)UIPercentDrivenInteractiveTransition(UIViewControllerInteractiveTransitioning的子类),将视觉动画按比例过渡,达到手势动画效果。
在现实开发中的页面会出现很多的detailView,使用interactionController中present的交互手势不太符合一般用户习惯,故会很少使用到交互动画中的present
extension MainViewController:UIViewControllerTransitioningDelegate{
//交互动画--主要是手势交互
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return panInteraction//实例化后,自动给DetailView添加Pan平移手势
}
}
PanInteraction类中添加平移手势代码如下:
class PanInteraction: UIPercentDrivenInteractiveTransition {
let detailVC:DetailViewController //可以是常量,因为仅在init时被赋值一次
var isInteractive = false
init(detailVC: DetailViewController) {
//存储属性指:一般属性,非计算属性
//Swift规定:先给所有存储属性(包括所有父类的)赋了值之后才能使用实例化后的本对象(self)
//Swift也规定:调用父类(super)前必须先给子类(本类)所有存储属性赋值--所以先1后2
self.detailVC = detailVC //1.先给本类所有的存储属性赋值
super.init() //2.再给所有父类的存储属性赋值(这句话系统默认帮我们加上的,但是是写在init最后的)
//3,使用本类对象(self),调用本类的方法,获取本类的属性的值,修改父类的变量等等都在这一步完成
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(pan:)))
detailVC.view.addGestureRecognizer(pan)
}
@objc func handlePan(pan: UIPanGestureRecognizer){
}
}
二、UIPercentDrivenInteractiveTransition
Pan平移手势实现
@objc func handlePan(pan: UIPanGestureRecognizer){
//使用滑动距离和200的比,作为进度--比如:用户滑100,动画进行1/2
//用户右滑>=200时,进度变成>=1,画面更新至完全dismiss
//如果想用户滑多少,detail页面就消失多少的话,除数可以改成屏幕宽度-UIScreen.main.bounds.width
let progress = pan.translation(in: pan.view).x / 200
switch pan.state {
case .began:
detailVC.dismiss(animated: true, completion: nil)
case .changed:
//比如0.5的时候,动画就进行到一半的位置;手在拖动的时候,不断的更新画面
//通过update设置转场过程动画进行的百分比,系统会根据百分比自动布局动画控件
update(progress)
case .cancelled , .ended:
if progress > 0.5{
finish()
}else{
cancel()
}
default:
break
}
}
三、transitionWasCancelled+交互状态
1.transitionWasCancelled
在实现Pan手势后,进行交互,会出现MainView的视图全部消失不见,这是因为交互动画器在完成交互动画后,要返回到DismissAnimator,且dismiss动画为视觉动画,中途不能被用户取消,则需使用transitionContext中transitionWasCancelled(仅在交互动画中有用)来判断动画是否真正的完成。
DismissAnimator(为了代码安全,在PresentAnimator上也要进行修改):
//transitionWasCancelled-如果动画被取消了,必须撤销之前对视图层级所做的修改
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
2.非交互动画与交互动画的顺序
在设置好交互动画后,出现新的问题:点击imageView后,无法dismiss到MainView界面。这是因为设置transitioningDelegate代理后,系统会先寻找是否有交互动画,如果有,则不会将非交互动画实例化。解决方法是为其设立一个交互状态的flag,用于判断当前用户是否处于交互状态(began,changed)中,是则执行交互动画,不是则恢复非交互状态,让系统delegate去执行非交互动画中的dismiss。
修改代码如下:
PanInteraction:
class PanInteraction: UIPercentDrivenInteractiveTransition {
let detailVC:DetailViewController
var isInteractive = false
init(detailVC:DetailViewController){
self.detailVC = detailVC
super.init()
let pan = UIPanGestureRecognizer(target: self, action: #selector(handelePan(pan:)))
detailVC.view.addGestureRecognizer(pan)
}
@objc func handelePan(pan:UIPanGestureRecognizer){
let progress = pan.translation(in: pan.view).x / 200
switch pan.state{
case .began:
isInteractive = true
detailVC.dismiss(animated: true,completion: nil)
case .changed:
update(progress)
case .cancelled , .ended:
isInteractive = false
if progress > 0.5{
finish()
}else{
cancel()
}
default:
break
}
}
}
MainViewController:
extension MainViewController:UIViewControllerTransitioningDelegate{
//交互动画--主要是手势交互
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return panInteraction.isInteractive ? panInteraction : nil
}
}
完成后总体效果如下:
总结
本次通过对交互动画认识,学习到了在interactionController中选择dismiss交互动画,跟之前的非交互动画相同,同样要返回UIViewControllerInteractiveTransitioning。与之不同的是,通过自定义的手势可将视觉动画变为播放,暂停,结束,使视觉动画变为视频播放的效果,即交互动画在视觉动画的基础上,持续性的通过用户的手势来完成动画,且交互式动画要在非交互式动画的基础上才能够实现。接下来就是对NavigationController转场动画的学习,进一步学习ViewController Transition中的知识,以便用于日后项目实例开发中。