代码基于 swift 4.2
概念
自定义转场其实就是 modal控制的时候,实现UIViewControllerTransitioningDelegate转场代理
@objc private func titleBtnClick(titleBtn:TitleButton){
titleBtn.isSelected = !titleBtn.isSelected
// 1.创建弹出的控制器
let popoverVc = PopoverViewController()
// 2.设置控制器的modal样式
popoverVc.modalPresentationStyle = .custom
// 3.设置转场的代理
popoverVc.transitioningDelegate = popoverAnimator
//弹出view的frame
popoverAnimator.presentedFrame = CGRect(x: 100, y: 64, width: 180, height: 250)
// 4弹出控制器
present(popoverVc, animated: true, completion: nil)
}
转场代理实现
返回一个自定义的UIPresentationController 。
import UIKit
class PopoverAnimator: NSObject {
// MARK:- 属性
var isPresented : Bool = false
var presentedFrame : CGRect = CGRect.zero
}
// MARK: - 自定义转场代理
extension PopoverAnimator:UIViewControllerTransitioningDelegate{
// 目的:改变弹出View的尺寸
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let presentation = CLPresentationController(presentedViewController: presented, presenting: presenting)
presentation.presentedFrame = presentedFrame
return presentation
}
// 目的:自定义弹出的动画
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
isPresented = true
return self
}
// 目的:自定义消失的动画
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
isPresented = false
return self
}
}
// MARK:- 弹出和消失动画代理的方法
extension PopoverAnimator : UIViewControllerAnimatedTransitioning{
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}
/// 获取`转场的上下文`:可以通过转场上下文获取弹出的View和消失的View
// UITransitionContextFromViewKey : 获取消失的View
// UITransitionContextToViewKey : 获取弹出的View
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
isPresented ? animationForPresentedView(transitionContext: transitionContext) :animationForDismissedView(transitionContext: transitionContext)
}
/// 自定义弹出动画
private func animationForPresentedView(transitionContext:UIViewControllerContextTransitioning){
// 1.获取弹出的View
let presentedView = transitionContext.view(forKey: .to)
// 2.将弹出的View添加到transitionContext的containerView中
transitionContext.containerView.addSubview(presentedView!)
//设置锚点位置(缩放位置)
presentedView?.layer.anchorPoint = CGPoint(x: 0.5, y: 0)
// 3.执行动画
presentedView?.transform = CGAffineTransform.init(scaleX: 1, y: 0)
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
presentedView?.transform = CGAffineTransform.identity
}) { (_) in
// 必须告诉转场上下文你已经完成动画
transitionContext.completeTransition(true)
}
}
/// 自定义消失动画
private func animationForDismissedView(transitionContext:UIViewControllerContextTransitioning){
// 1.获取消失的View
let dismissView = transitionContext.view(forKey: .from)
// 2.执行动画
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
dismissView?.transform = CGAffineTransform.init(scaleX: 1, y: 0.00001)
}) { (_) in
dismissView?.removeFromSuperview()
// 必须告诉转场上下文你已经完成动画
transitionContext.completeTransition(true)
}
}
}
UIPresentationController 自定义
modal 其实是先弹出一个containerView,再把要弹出的控制器加入containerView,默认弹出的控制器大小和containerView相同,所以我需要自定义UIPresentationController重新布局containerView 的子控件来实现自定义弹出控制器的大小。
需要重写containerViewWillLayoutSubviews 方法。
presented是被弹出的控制器
presentedView是被弹出的控制器的 VIew
import UIKit
class CLPresentationController: UIPresentationController {
// MARK:- 对外提供属性
var presentedFrame : CGRect = CGRect.zero
private lazy var coverView:UIView = UIView()
override func containerViewWillLayoutSubviews() {
super.containerViewWillLayoutSubviews()
//弹出 view的尺寸
presentedView?.frame = presentedFrame
setupCoverView()
}
}
// MARK: - 设置 UI
extension CLPresentationController{
private func setupCoverView(){
//底部插入蒙版
containerView?.insertSubview(coverView, at: 0)
// 2.设置蒙版的属性
coverView.backgroundColor = UIColor(white: 0.8, alpha: 0.2)
coverView.frame = containerView?.frame ?? CGRect.zero
// 3.添加手势
let tapGes = UITapGestureRecognizer(target: self, action: #selector(coverViewClick))
coverView.addGestureRecognizer(tapGes)
}
}
// MARK:- 事件监听
extension CLPresentationController {
/// 移除 modal
@objc private func coverViewClick() {
presentedViewController.dismiss(animated: true, completion: nil)
}
}
网友评论