刚开始从 Android 转到 iOS 写应用时,发现 iOS 的界面之间切换真方便,什么都不用写都有不错的转场动画,除了简单的平移、底部弹出,甚至还有 3D 旋转和翻页效果,而 Android 那系统默认转场是屏幕闪一下。
尽管系统提供的方式已经满足大部分场景了,但也没法挡住审美疲劳啊,所以就需要自定义转场动画了。
Present&dismiss Transition
先看个效果图:
刚开始写 iOS 时看到这种会动会弹的效果一直都是懵的,心里觉得这一定非常复杂,等到真正沉下心去学时,发现只要搞清楚每个步骤,这一点都不难。
1、实现 UIViewControllerTransitioningDelegate
比如我的第一个页面叫 FirstViewController,单击底部任意一个按钮都会以 present 方式打开 SecondViewController。点击按钮触发的代码如下:
12345678 | self.tappedButton = buttonlet sb = UIStoryboard(name: "Main", bundle: nil)let vc = sb.instantiateViewController(withIdentifier: "SBSecond")vc.view.backgroundColor = button.backgroundColor//下面这行就表示我们要自定义转场了vc.transitioningDelegate = selfself.present(vc, animated: true, completion: nil) |
实现 UIViewControllerTransitioningDelegate 的两个方法
1234567891011 | extension : UIViewControllerTransitioningDelegate { func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return nil } func animationController(fordismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return nil } } |
暂时给 present 和 dismiss 都返回 nil
2、编写具体的转场动画
先分析效果图的中的打开动画,SecondViewController 是从当前点击按钮的位置和大小,放大的同时改变中心点,达到了最终的全屏显示。
12345678910111213141516171819202122232425262728293031323334353637383940 | class FirstPresentTransition: NSObject, UIViewControllerAnimatedTransitioning { //动画时长 let duration = 1.0 //点击按钮的 frame,因为 SecondViewController 的显示动画是由按钮的位置开始的 var fromFrame = CGRect.zero //返回动画时长 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return duration } //具体的转场代码 func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let containerView = transitionContext.containerView //present 是从 FirstViewController 到 SecondViewController //toView 就是 SecondViewController 的 view let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)! //SecondViewController 完全显示后的 frame let toFrame = toView.frame //缩放的比例,按钮的宽高 / 最终的宽高 let scaleX = fromFrame.size.width / toFrame.size.width let scaleY = fromFrame.size.height / toFrame.size.height //其实最终的动画效果并不是真的把按钮放大了或者两者之间真的有衔接动画 //而是先把全屏的 SecondViewController 缩放到按钮一样的大小并位于同一位置 //然后根据计算的结果使用动画再"恢复"到全屏 toView.transform = toView.transform.scaledBy(x: scaleX, y: scaleY) toView.center = CGPoint(x: fromFrame.midX, y: fromFrame.midY) containerView.addSubview(toView) //执行动画,就是将 SecondViewController "恢复"到全屏 UIView.animate(withDuration: duration, delay: 0, usingSpringWithdamping: 0.3, initialSpringVeLocity: 0, options: [], animations: { toView.transform = CGAffineTransform.identity toView.center = CGPoint(x: toFrame.midX, y: toFrame.midY) }) { (_) in //转场完成后需要调用 transitionContext.completeTransition(true) } } } |
再看 dismiss 动画。SecondViewController 由全屏缩小一半到屏幕中心,再向上平移到屏幕外。
12345大专栏 |
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。