美文网首页
Modal自定义转场

Modal自定义转场

作者: alige | 来源:发表于2017-01-16 09:05 被阅读90次

    需求:点击一个按钮,modal一个控制器出来,该控制器不完全覆盖控制器。展开方式:按钮处从上往下展开,要求不能使得将底部的控制器移除。

    思路:通过设置控制器的控制器代理,并自定义modal动画样式,再自定义UIPresentation;实现转场代理方法和遵守转场动画协议来实现。

    1,添加按钮点击事件

        func titleBtnClicked(btn:TitleButton) -> Void {
            
            btn.selected = !btn.selected
            //从xib加载控制器
            guard let sb:UIStoryboard? = UIStoryboard.init(name: "Popover", bundle: nil) else{
                DyLog("the storyboard is NULL")
                return
            }
            //加载控制器
            guard let vc:UIViewController? = sb!.instantiateInitialViewController() else{
                DyLog("the viewcontroller is NULL")
                return
            }
            //1,设置被modal出来的控制器的转场代理
            vc!.transitioningDelegate = self
            //2,自定义modal动画样式
            vc!.modalPresentationStyle = UIModalPresentationStyle.Custom
            
            //modal出控制器
            presentViewController(vc!, animated: true, completion: nil)
        }
    

    2,实现转场代理方法

    ///MARK - 实现UIViewControllerTransitioningDelegate代理方法
    extension HomeTableViewController:UIViewControllerTransitioningDelegate{
        //返回一个自定义的UIPresentViewController来负责modal转场动画的对象,可以在该对象中控制弹出视图的尺寸等
        func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController?
        {
            return DyPresentationController.init(presentedViewController:presented,presentingViewController:presenting)
        }
        
        //该方法用于返回一个负责modal如何转场的控制器
        func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning?
        {
            isPresented = true
            return self
        }
        //该方法用于返回一个负责modal如何消失的控制器
        func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?
        {
            isPresented = false
            return self
        }
    }
    

    2.1 自定义UIPresentation

    • 自定义modal转场,可以让modal后,不会移除原有控制器
    • 自定义modal转场,modal出来的控制器的尺寸我们可以自己在containerViewWillLayoutSubviews方法中控制
    • containerView容器视图, 所有modal出来的视图都是添加到containerView上的
    • presentedView(), 通过该方法能够拿到弹出的视图,可以设置弹出的视图放在什么位置,如果不自定一转场,那么弹出的视图与屏幕尺寸相同
    import UIKit
    class DyPresentationController: UIPresentationController {
    
        ///MARK - 属性
        private lazy var coverBtn:UIButton = {
            ()->UIButton in
            let btn = UIButton.init()
            btn.frame = UIScreen.mainScreen().bounds
            btn.backgroundColor = UIColor.clearColor()
            return btn
        }()
        //Popover的宽度
        let width = 200.0
        let height = 350.0
        let screenW = UIScreen.mainScreen().bounds.width*1.0
    
        ///MARK - 内部控制方法
        //如果自定义了PresentationController,那么就不会移除modal前的控制器,否则用默认的是会移除的,
        override init(presentedViewController: UIViewController, presentingViewController: UIViewController) {
            super.init(presentedViewController: presentedViewController, presentingViewController: presentingViewController)
        }
        //通知负责转场的控制器,被modal的控制器即将要布局在containView
        override func containerViewWillLayoutSubviews(){
    
            //设置弹出的控制器的frame
            presentedView()?.frame = CGRectMake((screenW-CGFloat.init(width))*0.5, 35, CGFloat.init(width), CGFloat.init(height))
           
            //将蒙版按钮改在屏幕上,当点击它时退出modal控制器
             containerView?.insertSubview(coverBtn, atIndex: 0)
       
            coverBtn.addTarget(self, action: #selector(DyPresentationController.coverBtnClicked), forControlEvents: UIControlEvents.TouchUpInside)
        }
        //退出modal控制器
        func coverBtnClicked()->Void{
            presentedViewController.dismissViewControllerAnimated(true, completion: nil)
        }
    }
    

    2 实现自定义转场动画

    ///MARK - 实现转场动画的代理方法,就是从A->B的动画
    extension HomeTableViewController:UIViewControllerAnimatedTransitioning{
       //该方法统一设置转场的动画时间
        func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval
        {
            return 0.5
        }
       //该方法负责动画如何执行
        func animateTransition(transitionContext: UIViewControllerContextTransitioning)
        {
            //如果是modal控制器
            if isPresented {
                guard let toView = transitionContext.viewForKey(UITransitionContextToViewKey) else
                {
                    return
                }
                //1,先将要弹出的view加到containView上
                let containView = transitionContext.containerView()
                containView?.addSubview(toView)
                
                //2,设置锚点
                toView.layer.anchorPoint = CGPoint.init(x: 0.5, y: 0.0)
                //3,设置锚点
                toView.transform = CGAffineTransformMakeScale(1.0, 0.0)
                
                //4,开始动画
                UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
                    toView.transform = CGAffineTransformMakeScale(1.0, 1.0)
                }) { (_) in
                    DyLog("done")
                    //自定义转场动画,执行完后一定要告诉系统已经执行完毕了
                    transitionContext.completeTransition(true)
                }
           //如果是modal控制器
            }else{
                guard let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey) else{
                    return
                }
                
                fromView.layer.anchorPoint = CGPoint.init(x: 0.5, y: 0.0)
                fromView.transform = CGAffineTransformMakeScale(1.0, 1.0)
                UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
                    fromView.transform = CGAffineTransformMakeScale(1.0, 0.0001)
                    }, completion: { (_) in
                    //自定义转场动画,执行完后一定要告诉系统已经执行完毕了
                        transitionContext.completeTransition(true)
                })
            }
        }  
    }
    

    PS

    1,注意锚点的设置,锚点主要是设置动画的原坐标,以这个位置为参考进行动画
    toView.layer.anchorPoint = CGPoint.init(x: 0.5, y: 0.0)
    2,注意动画消失的时间,不能是0.0,在计算机存储这个数值是有误差的,会造成动画异常,因此要设置成0.0001这样较小的数
    fromView.transform = CGAffineTransformMakeScale(1.0, 0.0001)

    相关文章

      网友评论

          本文标题:Modal自定义转场

          本文链接:https://www.haomeiwen.com/subject/icxubttx.html