美文网首页IOSiOS开发精进iOS开发
iOS 自定义弹出视图的正确姿势

iOS 自定义弹出视图的正确姿势

作者: CatchZeng | 来源:发表于2018-01-11 12:19 被阅读179次

    前言

    开发时经常会自定义一些弹出视图,比如弹框(Alert)、底部弹出框(Action Sheet)等。自定义弹出视图有许多方法,到底哪种才更正确呢?下面我列举几种方法,一起讨论。先附上我的方案https://github.com/CatchZeng/SwiftPopup

    addSubview

    顾名思义,就是造出一个View,然后add到ViewController的View上显示出来,如https://www.jianshu.com/p/de2ecfd770c2的做法。

    • 好处:够简单
    • 坏处:视图&逻辑都在View上,职能不清晰,扩展性差,显示依赖于ViewController

    addChildViewController

    使用addChildViewController方法,将视图显示出来。如https://www.jianshu.com/p/6d790e6eb2ba的做法。

    • 好处:简单、职能清晰...
    • 坏处:在导航栏控制器或者标签栏控制器下弹出,会出现没完全覆盖到的情况。

    window

    新建一个window,切换成KeyWindow,将弹出视图作为rootViewController。代码如下

    func show() {
      previousWindow = UIApplication.shared.keyWindow
      keyWindow = UIWindow(frame: UIScreen.main.bounds)
      keyWindow?.rootViewController = self
      keyWindow?.windowLevel = UIWindowLevelNormal + 100
      keyWindow?.backgroundColor = UIColor.clear
      keyWindow?.makeKeyAndVisible()
    }
    
    • 好处:简单、职能清晰...
    • 坏处:在横屏的时候,第二次弹出时,出现显示问题【暂时不知道为什么】
    第二次显示问题

    UIPresentationController

    使用UIPresentationController自定义转场动画,这也是我的实现方法。
    UIPresentationController是提供高级视图切换的类。它让管理present ViewController的过程变得简单。

    Presentation

    如下图弹出一个UIViewController,可以和用户交互的Controller叫做PresentedViewController,而后面那个被部分遮挡的UIViewController叫做PresentingViewController.

    Presentation

    UIPopoverController的使用方法

    • 设置style&delegate
     modalPresentationStyle = .custom
     transitioningDelegate = self
    
    • 实现delegate,此代理需要UIPresentationController,以及UIViewControllerAnimatedTransitioning来自定义转场的动画
    extension SwiftPopup: UIViewControllerTransitioningDelegate {
        
        public func presentationController(forPresented presented: UIViewController,
                                           presenting: UIViewController?,
                                           source: UIViewController) -> UIPresentationController? {
            let controller = SwiftPopupPresentationController(presentedViewController: presented,
                                                           presenting: presenting)
            controller.backViewColor = backViewColor
            
            return controller
        }
        
        public func animationController(forPresented presented: UIViewController,
                                        presenting: UIViewController,
                                        source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return showAnimation
        }
        
        public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return dismissAnimation
        }
    }
    
    • 实现自定义的UIPresentationController
    open class SwiftPopupPresentationController: UIPresentationController {
        // MARK: Override Methods 
    
        // 将要开始转场
        override open func presentationTransitionWillBegin() {
        }
    
        // 将要移除页面
        override open func dismissalTransitionWillBegin() {
        }
        
       // 是否要移除PresentersView,也就是转场之前的视图,这里为false,因为弹框需要看到之前的视图
        override open var shouldRemovePresentersView: Bool {
            return false
        }
    
    • 实现自定义的动画
    open class SwiftPopupShowAnimation: NSObject, UIViewControllerAnimatedTransitioning {
        
        public var duration: TimeInterval = 0.8
        public var delay: TimeInterval = 0.0
        public var springWithDamping: CGFloat = 0.8
        public var springVelocity: CGFloat = 2.0
        
        open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return duration
        }
        
        open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            guard let toViewController = transitionContext.viewController( forKey: UITransitionContextViewControllerKey.to),
                let toView = transitionContext.view( forKey: UITransitionContextViewKey.to)
                else {
                return
            }
            
            let containerView = transitionContext.containerView
            toView.frame = transitionContext.finalFrame(for: toViewController)
            containerView.addSubview(toView)
            
            toView.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
            UIView.animate(withDuration: duration,
                           delay: delay,
                           usingSpringWithDamping: springWithDamping,
                           initialSpringVelocity: springVelocity,
                           options: .curveEaseInOut, animations: {
                            toView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
            }) { (finished) in
                transitionContext.completeTransition(finished)
            }
        }
    }
    
    • Show
    public func show(above viewController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController,
                               completion: (()-> Void)? = nil) {
            mIsShowing = true
            
            viewController?.present(self, animated: true, completion: completion)
        }
    
    • Dismiss
    public func dismiss(completion: (()-> Void)? = nil) {
            dismiss(animated: true) {
                self.mIsShowing = false
                completion?()
            }
        }
    

    完整代码 https://github.com/CatchZeng/SwiftPopup

    Demo

    小结

    如遇问题或者有更好的方式,欢迎加QQ群讨论:157672725

    相关文章

      网友评论

        本文标题:iOS 自定义弹出视图的正确姿势

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