ViewController Transitions——非交互式自定义Modally转场动画


前言

此次学习的是View Controller Transitions中的自定义Modally转场动画中的非交互动画


一、设置页面MianViewController和DetailViewController

页面

二、点击按钮跳转页面

1.在MainViewController中使用showDetail来present出DetailView

 @IBAction func showDetail(_ sender: Any) { 
        let detailVC = storyboard!.instantiateViewController(withIdentifier: "DetailVC") as! DetailViewController
        detailVC.modalPresentationStyle = .fullScreen
        present(detailVC, animated: true, completion: nil)
    }

在present中获取UIViewController,既Storyboard中已设计的DetailViewController,不能直接实例化,否则点击按钮跳转页面为空。需在Storyboard的DetailViewController下找到identify中的storyboard ID,赋值为DetailVC,再在showDetail中初始ViewController

let detailVC = storyboard!.instantiateViewController(withIdentifier: "DetailVC") as! DetailViewController

iOS13版本中modally方式弹出的页面默认变为非全屏状态,所以需在prsent前添加:detailVC.modalPresentationStyle = .fullScreen

2.使用Modally方式展示DetailVC

2.1、detailVC的present和dismiss委托给当前View Controller处理

在present之前指定代理人,让代理人return自定义动画器,系统找到非nil的动画器后执行自定义动画,否则继续执行系统动画

detailVC.transitioningDelegate = self

extension MainViewController: UIViewControllerTransitioningDelegate{
    //非交互动画--视觉动画
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        //创建PresentAnimator动画实例,遵循UIViewControllerAnimatedTransitioning
        return PresentAnimator()
    }
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return DismissAnimator()
    }
}

2.2、创建PresentAnimator动画实例

PresentAnimator类要遵循NSObject协议,才可以继承UIViewControllerAnimatedTransitioning

class PresentAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        
        //因整个动画过程千变万化,一定要直接使用transitionContext中的数据
        //不要用变量来缓存数据然后再用,或比如:获取fromView时用fromVC.view
        
        //let fromVC = transitionContext.viewController(forKey: .from)
        //let toVC = transitionContext.viewController(forKey: .to)
        
        guard let fromView = transitionContext.view(forKey: .from),
        let toView = transitionContext.view(forKey: .to) else{return}
        
        let containerView = transitionContext.containerView
        
        //containerview会自动把fromview加到视图层级中,我们需要自己加上toview--两个view到齐才能开始动画
        //containerview在动画结束后会自动移除fromview防止资源浪费,不需要我们手动移除
        containerView.addSubview(toView)
        
        //动画起始状态
        toView.alpha = 0
        toView.transform = CGAffineTransform(translationX: containerView.frame.width, y: 0)
        
        UIView.animate(
            withDuration: transitionDuration(using: transitionContext),
            animations: {
                //动画结束状态
                fromView.alpha = 0
                fromView.transform = CGAffineTransform(translationX: -containerView.frame.width, y: 0)
                toView.alpha = 1
                toView.transform = .identity
        }) { _ in
        //fromview虽然被自动从containerview里面移除了,但附在身上的transform属性还在,为了防止两次动画交错,这里要置空
            fromView.transform = .identity
    //虽然toview的transform在动画块里面置空了,但如果交互动画时用户取消转场了,就会被附上初始时定的transform,所以为了不干扰下一次动画,这里也置空
            toView.transform = .identity
            
            
            //transitionWasCancelled-如果动画被取消了,必须撤销之前对视图层级所做的修改
            //            !transitionContext.transitionWasCancelled
            
            //一定要调用completeTransition,不然页面会卡住
            //调用后UIKit才会结束整个动画流程,并把控制权交给App,用户才可以继续操作页面
            transitionContext.completeTransition(true)
        }
        
    }
    

}

三、自定义dismiss转场动画

1.imageView添加tap手势

@IBOutlet weak var detailImageView: UIImageView!

override func viewDidLoad() {
    super.viewDidLoad()
    let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(tap:)))
    detailImageView.addGestureRecognizer(tap)
}

@objc func handleTap(tap:UITapGestureRecognizer){
    dismiss(animated: true,completion: nil)
}

imageView不是UIViewController类型,在默认情况下,是不允许用户进行交互操作的,为了防止与其他的可交互的按键起冲突。故给其添加手势要在Interaction中将可交互勾上。
imageView交互

2.创建DissmissAnimator动画实例

class DismissAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        
        let containerView = transitionContext.containerView
        guard let fromView = transitionContext.view(forKey: .from),
            let toView = transitionContext.view(forKey: .to) else{return}
        
        containerView.addSubview(toView)
        
        toView.alpha = 0
        toView.transform = CGAffineTransform(translationX: -containerView.frame.width, y: 0)
        
        UIView.animate(
            withDuration: transitionDuration(using: transitionContext),
            animations: {
                fromView.alpha = 0
                fromView.transform = CGAffineTransform(translationX: containerView.frame.width, y: 0)
                toView.alpha = 1
                toView.transform = .identity
                
        }) { _ in
        //一旦给fromView和toView修改了它们transform属性,都需在完成后的回调函数中置空
        //fromview虽然被自动从containerview里面移除了,但附在身上的transform属性还在,为了防止两次动画交错,这里要置空
            fromView.transform = .identity
    //虽然toview的transform在动画块里面置空了,但如果交互动画时用户取消转场了,就会被附上初始时定的transform,所以为了不干扰下一次动画,这里也置空
            toView.transform = .identity
            
            transitionContext.completeTransition(true)
        }
    }
    
    
}

再将DismissAnimator在forDismissed dismissed函数中return

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
      return DismissAnimator()
  }

完成后整体的效果如下所示
效果展示


总结

此次是对ViewController Transition中非交互动画Modally的学习,主要了解到自定义的present和dismiss,以及imageView添加手势动画要将其交互设计为true。fromView、toView的展示切换中,一旦给fromView和toView修改了它们transform属性,都需在完成后的回调函数中置空。总体上加深了对自定义非交互式动画的的了解,接下来学习交互式动画打下基础。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值