【iOS学习】用UIPresentationController来写一个简洁漂亮的底部弹出控件

iOS App开发过程中,底部弹出框是一个非常常见的需求。如何写一个漂亮的底部弹出框呢?方式有很多,直接添加一个自定义的View让它动画展示和隐藏都是一种非常简单的操作,不过看起来似乎不那么优雅,我们可以使用UIPresentationController来方便快捷地创建一个高定制化的底部弹出框。UIPresentationController的官方文档地址如下:

UIPresentationController: an object that manages the transition animations and the presentation of view controllers onscreen.

先上最终效果:

我们需要在iOS8及以上的系统中使用UIPresentationController,使用时需要新建一个类继承UIPresentationController并重写以下几个方法和属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//决定了弹出框的frame
override   var   frameOfPresentedViewInContainerView
 
//重写此方法可以在弹框即将显示时执行所需要的操作
override   func presentationTransitionWillBegin()
 
//重写此方法可以在弹框显示完毕时执行所需要的操作

override func presentationTransitionDidEnd(_ completed: Bool)

 
//重写此方法可以在弹框即将消失时执行所需要的操作
override   func dismissalTransitionWillBegin()
 
//重写此方法可以在弹框消失之后执行所需要的操作
override   func dismissalTransitionDidEnd(_ completed: Bool)

重写初始化方法:

1
2
3
override   init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
     super .init(presentedViewController:presentedViewController,presenting: presentingViewController)
}

在大多数时候,我们希望底部弹出框出现时,先前的显示区域能够灰暗一些,来强调弹出框的显示区域是用户当前操作的首要区域。因此,我们给这个自定义的类添加一个遮罩:

1
2
3
4
5
6
7
8
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上移除:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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:

1
2
3
override   var   frameOfPresentedViewInContainerView: CGRect {
     return   CGRect(x:  0 , y: UIScreen.main.bounds.height-controllerHeight, width: UIScreen.main.bounds.width, height: controllerHeight)
}

为了便于我们创建各种各样的底部弹出框,我们创建一个基类PresentBottomVC继承自UIViewController,并添加一个属性controllerHeight用于得到弹出框的高度:

1
2
3
4
5
6
7
public   class   PresentBottomVC: UIViewController {
     public   var   controllerHeight: CGFloat? {
         get   {
             return   self.controllerHeight
         }
     }
}

之后,我们就可以新建继承自PresentBottomVC并重写controllerHeight属性的类来实现定制化底部弹出框。为了方便调用弹出方法,我们给UIViewController添加一个Extension,并实现UIViewControllerTransitioningDelegate协议:

1
2
3
4
5
6
7
8
9
10
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,在页面中添加一个关闭按钮:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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的子类就可以方便快捷的实现各种各样的底部弹出框啦。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值