githubDEMO地址 是用swift实现的,oc的原理都是一样的
一.首先,为什么要用转场动画,转场动画相对于系统的push/pop,present/dismiss有什么优势?
1.如果不自定义转场modal出来的控制器会移除原有的控制器
2.如果自定义转场modal出来的控制器不会移除原有的控制器,这样可以把前面的vc当成背景来实现侧滑菜单,下拉菜单等等功能
3.如果不自定义转场modal出来的控制器的尺寸和屏幕一样
4.如果自定义转场modal出来的控制器的尺寸我们可以自定义,并且可以自定义转场动画,实现各种特效
二 我们来具体实现以下
实现步骤摘要
1.自定义一个转场动画对象,继承自UIPresentationController
2.vc1弹出vc2,设置vc2的transitioningDelegate为vc1,实现代理的三个方法,代理方法里面还要实现一个UIViewControllerAnimatedTransitioning代理。总共要实现两个代理,五个方法
下面具体介绍一下
1.自定义转场对象
class MypreVC: UIPresentationController {
override init(presentedViewController: UIViewController, presentingViewController: UIViewController) {
super.init(presentedViewController: presentedViewController, presentingViewController: presentingViewController)
}
// 用于布局转场动画弹出的控件
override func containerViewWillLayoutSubviews(){
// 设置弹出视图的尺寸
presentedView()?.frame = CGRect.init(x: 0, y: 64, width: 200, height: (containerView?.frame.size.height)!)
// 添加透明button背景
xyf = UIButton()
xyf!.frame = UIScreen.mainScreen().bounds
containerView?.insertSubview(xyf!, atIndex: 0)
xyf?.tag = 66
xyf?.addTarget(self, action: #selector(MypreVC.funcyion2(_:)), forControlEvents: UIControlEvents.TouchUpInside)
}
private var xyf:UIButton?
func funcyion2(sender:UIButton) {
presentedViewController.dismissViewControllerAnimated(true, completion: nil)
}
}
在第一个vc1里面有一个btn,点击弹出第二个界面
@IBAction func btnClick(sender: UIButton) {
let neextvc = NextVC()
//自定义转场动画
//设置转场代理
neextvc.transitioningDelegate = self
//动画样式
neextvc.modalPresentationStyle = UIModalPresentationStyle.Custom
//
presentViewController(neextvc, animated: true, completion: nil)
}
因为上面遵守了一个协议,现在我们来实现代理
UIViewControllerTransitioningDelegate实现
//extension ViewController:UIViewControllerTransitioningDelegate{
// 该方法用于返回一个负责转场动画的对象
// 可以在该对象中控制弹出视图的尺寸等
func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController?{
//MypreVC是我们自定义的转场对象
return MypreVC(presentedViewController: presented, presentingViewController: presenting)
}
// 该方法用于返回一个负责转场如何消失的对象
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?{
//自定义变量,记录是否是弹出,下面的代理方法要用到
//UIViewControllerAnimatedTransitioning所以还需实现一个协议
isPresent = false
return self
}
// 该方法用于返回一个负责转场如何出现的对象
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning?{
isPresent = true
return self
}
UIViewControllerAnimatedTransitioning实现
// 告诉系统展现和消失的动画时长
// 暂时用不上
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval{
return 2
}
// 专门用于管理modal如何展现和消失的, 无论是展现还是消失都会调用该方法
/*
注意点: 只要我们实现了这个代理方法, 那么系统就不会再有默认的动画了
也就是说默认的modal从下至上的移动系统不帮再帮我们添加了, 所有的动画操作都需要我们自己实现, 包括需要展现的视图也需要我们自己添加到容器视图上(containerView)
*/
// transitionContext: 所有动画需要的东西都保存在上下文中, 换而言之就是可以通过transitionContext获取到我们想要的东西
func animateTransition(transitionContext: UIViewControllerContextTransitioning){
// 0.判断当前是展现还是消失
if isPresent {
// 通过ToViewKey取出的就是toVC对应的view
guard let toView = transitionContext.viewForKey(UITransitionContextToViewKey) else{
return
}
// 2.将需要弹出的视图添加到containerView上
transitionContext.containerView()?.addSubview(toView)
// 3.执行动画
toView.transform = CGAffineTransformMakeScale(1.0, 0.0)
// 设置锚点
toView.layer.anchorPoint = CGPoint (x: 0.5, y: 0)
UIView.animateWithDuration(2, animations: {
toView.transform = CGAffineTransformIdentity
}) { (_) in
// 注意: 自定转场动画, 在执行完动画之后一定要告诉系统动画执行完毕了
transitionContext.completeTransition(true)
}
}
else{
//dismiss
// 消失
// 1.拿到需要消失的视图
guard let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey) else{
return
}
//transitionContext.containerView()?.addSubview(toView)
// 2.执行动画让视图消失
fromView.transform = CGAffineTransformIdentity
fromView.layer.anchorPoint = CGPoint (x: 0.5, y: 0)
UIView.animateWithDuration(2, animations: {
// 突然消失的原因: CGFloat不准确, 导致无法执行动画, 遇到这样的问题只需要将CGFloat的值设置为一个很小的值即可
fromView.transform = CGAffineTransformMakeScale(1.0, 0.0001)
}) { (_) in
// 注意: 自定转场动画, 在执行完动画之后一定要告诉系统动画执行完毕了
transitionContext.completeTransition(true)
}
}
}
到上面就结束了
总结一下:
在继承自UIPresentationController的转场类中,有几个重要属性和方法
containerView属性 非常重要, 容器视图, 所有modal出来的视图都是添加到containerView上的
presentedView() 非常重要, 通过该方法能够拿到弹出的视图
func containerViewWillLayoutSubviews()用来对弹出视图做一些布局
利用好上面的内容可以把弹出的视图便成我们想要的样子
协议里面的关键是func animateTransition(transitionContext: UIViewControllerContextTransitioning){}
通过它才能实现转场动画,对转场进行控制
具体的demo:https://github.com/xuyunfan2015/CustomPresentDemo.git