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源码