Swift之模态弹窗自定义一 2024-09-22 周日

2024-09-22  本文已影响0人  勇往直前888

简介

iOS系统提供的模态弹窗已经足够好用了,所以这方面一直不用操心。
另外,自定义弹窗的实现方式过于复杂,很不好学,所以一直以来都不想学。
只是,现在自定义弹窗的需求越来越多,又不得不学一下。

测试VC

就一个背景为红色,最简单了。

class TempViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        /// 红色背景
        view.backgroundColor = .red
    }

}

调用的地方也采用最简单的方式:

                let tempVc = TempViewController()
                
                present(tempVc, animated: true) {
                    print("tempVc present 完成")
                }

系统的弹出方式

说实话,系统的弹出方式已经足够好了,从下到上弹出来,调用的VC有个往后缩的动画,最后头部留点空间,手势向下可以让弹窗消失。

系统弹窗

使用Lookin看视图结构,模态弹窗和导航栏push出来的是重叠的两套体系

视图层次结构

transitioningDelegate

extension UIViewController {
    @available(iOS 7.0, *)
    weak open var transitioningDelegate: UIViewControllerTransitioningDelegate?
}
@MainActor public protocol UIViewControllerTransitioningDelegate : NSObjectProtocol {
    @available(iOS 2.0, *)
    optional func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning?

    
    @available(iOS 2.0, *)
    optional func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?

    
    optional func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?

    
    optional func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?

    
    @available(iOS 8.0, *)
    optional func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController?
}

自定义过渡动画

import UIKit

class TempPresentation: UIPresentationController {

}
class TempTransitionDelegate: NSObject {
    /// 默认单例
    public static let `default`: TempTransitionDelegate = {
        print("TempTransitionDelegate `default` 实例被创建")
        return TempTransitionDelegate()
    }()
}

/// 代理方法
extension TempTransitionDelegate: UIViewControllerTransitioningDelegate {
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        print("TempTransitionDelegate `animationController` forPresented 被调用")
        return nil
    }
    
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        print("TempTransitionDelegate `animationController` forDismissed 被调用")
        return nil
    }
    
    func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        print("TempTransitionDelegate `interactionControllerForPresentation` 被调用")
        return nil
    }
    
    func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        print("TempTransitionDelegate `interactionControllerForDismissal` 被调用")
        return nil
    }
    
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        presented.modalPresentationStyle = .custom
        print("TempTransitionDelegate `presentationController` 被调用")
        return TempPresentation(presentedViewController: presented, presenting: presenting)
    }
}
class TempViewController: UIViewController {
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        
        /// 自定义弹窗方式
        modalPresentationStyle = .custom
        transitioningDelegate = TempTransitionDelegate.default
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        /// 红色背景
        view.backgroundColor = .red
    }
}
空的自定义视图层级

实现Sheet效果

(1)背景色就50%黑色,实现渐隐渐显效果,过场动画保持从底部弹窗的方式不变。
(2)点击背景还能消除对话框。
(3)红色的弹出视图,高度只要500pt就可以的,一半多点,只是做一些简单的交互操作。

背景视图

    // The view in which a presentation occurs. It is an ancestor of both the presenting and presented view controller's views.
    // This view is being passed to the animation controller.
    open var containerView: UIView? { get }
class TempPresentation: UIPresentationController {
    /// containerView在这个时候已经存在,所以在这里加入自定义的view和oViewDidLoad有点像
    /// 逐渐显现的动画做在自定义的
    override func presentationTransitionWillBegin() {
        containerView?.insertSubview(alphaCover, at: 0)
        alphaCover.alpha = 0
        UIView.animate(withDuration: 3) {
            self.alphaCover.alpha = 1
        }
    }
    
    /// 逐渐消失的动画
    override func dismissalTransitionWillBegin() {
        alphaCover.alpha = 1
        UIView.animate(withDuration: 3) {
            self.alphaCover.alpha = 0
        }
    }
    
    /// 退出动画完成,去掉添加的辅助视图
    override func dismissalTransitionDidEnd(_ completed: Bool) {
        if completed {
            alphaCover.removeFromSuperview()
        }
    }
    
    /// 设置弹窗视图的高度
    public var sheetHeight: CGFloat = 500
    let phoneWidth = UIScreen.main.bounds.width
    let phoneHeight = UIScreen.main.bounds.height

    override var frameOfPresentedViewInContainerView: CGRect {
        let frame = CGRect(origin: CGPoint(x: 0, y: (phoneHeight - sheetHeight)), size: CGSize(width: phoneWidth, height: sheetHeight))
        return frame
    }
    
    /// 背景板,50%黑,退出手势
    lazy var alphaCover: UIView = {
        let cover = UIView()
        cover.backgroundColor = .black.withAlphaComponent(0.5)
        if let containerView = containerView, containerView.bounds.width > 0 {
            cover.frame = containerView.bounds
        } else {
            cover.frame = CGRect(x: 0, y: 0, width: phoneWidth, height: phoneWidth)
        }
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(coverTapAction))
        cover.addGestureRecognizer(tapGesture)
        return cover
    }()
}

/// actions
extension TempPresentation {
    @objc func coverTapAction() {
        presentedViewController.dismiss(animated: true)
    }
}
现在的视图层次
上一篇 下一篇

猜你喜欢

热点阅读