iT邦幫忙

0

[Cmoney 菁英軟體工程師戰鬥營] IOS APP 菜鳥開發筆記(7)----自定義彈出視窗

ios

前言

因為UI和UX方面的需求,這幾天上網搜尋了如何自定義下一頁的彈出大小,彈出位置和動畫,發現有蠻多種方法都能達到成,於是選了個來實作。因為目前的進度壓力比較大,所以很多地方沒有寫得有彈性和封裝好,再請見諒了。

首先我們來設計一個由下往上彈出的頁面,自定義的部分為彈出後的高度,這邊用protocol來讓其他類修改。

public protocol PresentBottomVCProtocol {
    var controllerHeight: CGFloat {get}
}

再來要寫一個繼承 UIPresentationController 的類,裡面可以添加動畫和遮罩效果。

public class PresentBottom:UIPresentationController {

    ///  黑色遮罩
    lazy var blackView: UIView = {
        let view = UIView()
        if let frame = self.containerView?.bounds {
            view.frame = frame
        }
        view.backgroundColor = UIColor.black.withAlphaComponent(0.4)
        let gesture = UITapGestureRecognizer(target: self, action: #selector(sendHideNotification))
        view.addGestureRecognizer(gesture)
        return view
    }()
    
    /// 調整高度
    public var controllerHeight:CGFloat
    
    public override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
        //get height from an objec of PresentBottomVC class
        if case let vc as PresentBottomVC = presentedViewController {
            controllerHeight = vc.controllerHeight
        } else {
            controllerHeight = UIScreen.main.bounds.width
        }
        super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
    }
    
//    /// 在弹窗即将出现时把遮罩添加到containerView,并通过动画将遮罩的alpha设置为1
    public override func presentationTransitionWillBegin() {
        blackView.alpha = 1
        containerView?.addSubview(blackView)

    }

    ///  在弹窗即将消失时做的事
    public override func dismissalTransitionWillBegin() {
        
    }

    /// 在弹框消失之后将遮罩从containerView上移除
    public override func dismissalTransitionDidEnd(_ completed: Bool) {
        if completed {
            blackView.removeFromSuperview()
        }
    }
    
    ///   决定了弹出框的frame, 它决定了弹出框在屏幕中的位置,由于我们是底部弹出框,我们设定一个弹出框的高度controllerHeight,即可得出弹出框的frame
    public override var frameOfPresentedViewInContainerView: CGRect {
        return CGRect(x: 0, y: UIScreen.main.bounds.height-controllerHeight, width: UIScreen.main.bounds.width, height: controllerHeight)
    }
    
    
extension UIViewController: UIViewControllerTransitioningDelegate {
    

    /// - Parameter vc: class name of bottom view  
    public func presentBottom(_ vc: PresentBottomVC ) {
        vc.modalPresentationStyle = .custom
        vc.transitioningDelegate = self
        vc.modalTransitionStyle = UIModalTransitionStyle.coverVertical //由下跳轉的動畫
        self.present(vc, animated: true, completion: nil)
    }
    
    /// - Parameter vc: class name of bottom view
    public func presentMiddle(_ vc: PresentMiddleVC ) {
        vc.modalPresentationStyle = .custom
        vc.transitioningDelegate = self
        vc.modalTransitionStyle = UIModalTransitionStyle.crossDissolve //直接跳轉的動畫
        self.present(vc, animated: true, completion: nil)
    }
    
    
    
    // function refers to UIViewControllerTransitioningDelegate
    public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        
        if  case let vc as PresentBottomVC = presented {
            let present = PresentBottom(presentedViewController: vc, presenting: presenting)
            return present
        }else if case let vc2 as PresentMiddleVC = presented{
            let present = PresentMiddle(presentedViewController: vc2, presenting: presenting)
            return present
        }

       return nil
    }
}

接下來設計一個基底類實現剛才寫的protocol

public let PresentBottomHideKey = "ShouldHidePresentBottom"

public class PresentBottomVC: UIViewController, PresentBottomVCProtocol {
    public var controllerHeight: CGFloat {
        return 0
    }
    
    public override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(presentBottomShouldHide), name: NSNotification.Name(PresentBottomHideKey), object: nil)
    }
    
    public override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(PresentBottomHideKey), object: nil)
    }
    
    @objc func presentBottomShouldHide() {
        self.dismiss(animated: true, completion: nil)
    }
    
}

以後我們只要讓新的 Controller 繼承 PresentBottomVC 並覆寫controllerHeight後,就能愉快地設定彈出後的高度了!

class TestViewController:  PresentBottomVC {

    override var controllerHeight: CGFloat {
        return screenSize.height*4/7
    }

如果要設定彈出在畫面中間,類似 AlertView 的彈出視窗,也可以照以上流程設計。在上方的UIViewControllerTransitioningDelegate 那可以設定彈出的動畫效果,以及外界該如何呼叫我們的彈出視窗。


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言