iOS App開發過程當中,底部彈出框是一個很是常見的需求。如何寫一個漂亮的底部彈出框呢?方式有不少,直接添加一個自定義的View讓它動畫展現和隱藏都是一種很是簡單的操做,不過看起來彷佛不那麼優雅,咱們可使用UIPresentationController
來方便快捷地建立一個高定製化的底部彈出框。UIPresentationController
的官方文檔地址以下:git
UIPresentationController: an object that manages the transition animations and the presentation of view controllers onscreen.github
先上最終效果:app
GitHub: github.com/IkeBanPC/Pr…ide
咱們須要在iOS8及以上的系統中使用UIPresentationController
,使用時須要新建一個類繼承UIPresentationController
並重寫如下幾個方法和屬性:測試
//決定了彈出框的frame
override var frameOfPresentedViewInContainerView
//重寫此方法能夠在彈框即將顯示時執行所須要的操做
override func presentationTransitionWillBegin()
//重寫此方法能夠在彈框顯示完畢時執行所須要的操做
override func presentationTransitionDidEnd(_ completed: Bool)
//重寫此方法能夠在彈框即將消失時執行所須要的操做
override func dismissalTransitionWillBegin()
//重寫此方法能夠在彈框消失以後執行所須要的操做
override func dismissalTransitionDidEnd(_ completed: Bool)
複製代碼
重寫初始化方法:動畫
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
super.init(presentedViewController:presentedViewController,presenting: presentingViewController)
}
複製代碼
在大多數時候,咱們但願底部彈出框出現時,先前的顯示區域可以灰暗一些,來強調彈出框的顯示區域是用戶當前操做的首要區域。所以,咱們給這個自定義的類添加一個遮罩:ui
lazy var blackView: UIView = {
let view = UIView()
if let frame = self.containerView?.bounds {
view.frame = frame
}
view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
return view
}()
複製代碼
重寫presentationTransitionWillBegin
、dismissalTransitionWillBegin
和dismissalTransitionDidEnd(_ completed: Bool)
方法。在彈窗即將出現時把遮罩添加到containerView,並經過動畫將遮罩的alpha設置爲1;在彈窗即將消失時經過動畫將遮罩的alpha設置爲0;在彈框消失以後將遮罩從containerView上移除:spa
override func presentationTransitionWillBegin() {
blackView.alpha = 0
containerView?.addSubview(blackLayerView)
UIView.animate(withDuration: 0.5) {
self.blackView.alpha = 1
}
}
override func dismissalTransitionWillBegin() {
UIView.animate(withDuration: 0.5) {
self.blackView.alpha = 0
}
}
override func dismissalTransitionDidEnd(_ completed: Bool) {
if completed {
blackView.removeFromSuperview()
}
}
複製代碼
接下來,咱們重寫frameOfPresentedViewInContainerView
這個屬性。它決定了彈出框在屏幕中的位置,因爲咱們是底部彈出框,咱們設定一個彈出框的高度controllerHeight,便可得出彈出框的frame:code
override var frameOfPresentedViewInContainerView: CGRect {
return CGRect(x: 0, y: UIScreen.main.bounds.height-controllerHeight, width: UIScreen.main.bounds.width, height: controllerHeight)
}
複製代碼
爲了便於咱們建立各類各樣的底部彈出框,咱們建立一個基類PresentBottomVC
繼承自UIViewController
,並添加一個屬性controllerHeight
用於獲得彈出框的高度:orm
public class PresentBottomVC: UIViewController {
public var controllerHeight: CGFloat? {
get {
return self.controllerHeight
}
}
}
複製代碼
以後,咱們就能夠新建繼承自PresentBottomVC
並重寫controllerHeight
屬性的類來實現定製化底部彈出框。爲了方便調用彈出方法,咱們給UIViewController
添加一個Extension,並實現UIViewControllerTransitioningDelegate
協議:
public func presentBottom(_ vc: PresentBottomVC.Type) {
let controller = vc.init()
controller.modalPresentationStyle = .custom
controller.transitioningDelegate = self
self.present(controller, animated: true, completion: nil)
}
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let present = PresentBottom(presentedViewController: presented, presenting: presenting)
return present
}
複製代碼
能夠看到,全部繼承自PresentBottomVC
的ViewController均可以經過該方法來從另外一個ViewController的底部彈出。例如,咱們新建一個類FirstBottomVC
,重寫controllerHeight
並設爲200,在頁面中添加一個關閉按鈕:
final class FirstBottomVC: PresentBottomVC {
lazy var closeButton:UIButton = {
let button = UIButton(frame: CGRect(x: 15, y: 30, width: 80, height: 30))
button.setTitle("Close", for: .normal)
button.setTitleColor(.black, for: .normal)
button.addTarget(self, action: #selector(closeButtonClicked), for: .touchUpInside)
return button
}()
override var controllerHeight: CGFloat? {
return 200
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .cyan
view.addSubview(closeButton)
}
@objc func closeButtonClicked() {
self.dismiss(animated: true, completion: nil)
}
}
複製代碼
以後在須要彈出的時候調用UIViewController
的presentBottom(_ vc: PresentBottomVC.Type)
方法就能夠一句代碼搞定啦:
self.presentBottom(FirstBottomVC.self)
複製代碼
效果以下圖:
測試用的彈框寫好了,咱們只要根據本身的需求去建立不一樣的PresentBottomVC
的子類就能夠方便快捷的實現各類各樣的底部彈出框啦。實例中的兩個效果能夠參考GitHub源碼