需求:点击一个按钮,modal一个控制器出来,该控制器不完全覆盖控制器。展开方式:按钮处从上往下展开,要求不能使得将底部的控制器移除。
思路:通过设置控制器的控制器代理,并自定义modal动画样式,再自定义UIPresentation;实现转场代理方法和遵守转场动画协议来实现。
1,添加按钮点击事件
func titleBtnClicked(btn:TitleButton) -> Void {
btn.selected = !btn.selected
//从xib加载控制器
guard let sb:UIStoryboard? = UIStoryboard.init(name: "Popover", bundle: nil) else{
DyLog("the storyboard is NULL")
return
}
//加载控制器
guard let vc:UIViewController? = sb!.instantiateInitialViewController() else{
DyLog("the viewcontroller is NULL")
return
}
//1,设置被modal出来的控制器的转场代理
vc!.transitioningDelegate = self
//2,自定义modal动画样式
vc!.modalPresentationStyle = UIModalPresentationStyle.Custom
//modal出控制器
presentViewController(vc!, animated: true, completion: nil)
}
2,实现转场代理方法
///MARK - 实现UIViewControllerTransitioningDelegate代理方法
extension HomeTableViewController:UIViewControllerTransitioningDelegate{
//返回一个自定义的UIPresentViewController来负责modal转场动画的对象,可以在该对象中控制弹出视图的尺寸等
func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController?
{
return DyPresentationController.init(presentedViewController:presented,presentingViewController:presenting)
}
//该方法用于返回一个负责modal如何转场的控制器
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning?
{
isPresented = true
return self
}
//该方法用于返回一个负责modal如何消失的控制器
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?
{
isPresented = false
return self
}
}
2.1 自定义UIPresentation
- 自定义modal转场,可以让modal后,不会移除原有控制器
- 自定义modal转场,modal出来的控制器的尺寸我们可以自己在containerViewWillLayoutSubviews方法中控制
- containerView容器视图, 所有modal出来的视图都是添加到containerView上的
- presentedView(), 通过该方法能够拿到弹出的视图,可以设置弹出的视图放在什么位置,如果不自定一转场,那么弹出的视图与屏幕尺寸相同
import UIKit
class DyPresentationController: UIPresentationController {
///MARK - 属性
private lazy var coverBtn:UIButton = {
()->UIButton in
let btn = UIButton.init()
btn.frame = UIScreen.mainScreen().bounds
btn.backgroundColor = UIColor.clearColor()
return btn
}()
//Popover的宽度
let width = 200.0
let height = 350.0
let screenW = UIScreen.mainScreen().bounds.width*1.0
///MARK - 内部控制方法
//如果自定义了PresentationController,那么就不会移除modal前的控制器,否则用默认的是会移除的,
override init(presentedViewController: UIViewController, presentingViewController: UIViewController) {
super.init(presentedViewController: presentedViewController, presentingViewController: presentingViewController)
}
//通知负责转场的控制器,被modal的控制器即将要布局在containView
override func containerViewWillLayoutSubviews(){
//设置弹出的控制器的frame
presentedView()?.frame = CGRectMake((screenW-CGFloat.init(width))*0.5, 35, CGFloat.init(width), CGFloat.init(height))
//将蒙版按钮改在屏幕上,当点击它时退出modal控制器
containerView?.insertSubview(coverBtn, atIndex: 0)
coverBtn.addTarget(self, action: #selector(DyPresentationController.coverBtnClicked), forControlEvents: UIControlEvents.TouchUpInside)
}
//退出modal控制器
func coverBtnClicked()->Void{
presentedViewController.dismissViewControllerAnimated(true, completion: nil)
}
}
2 实现自定义转场动画
///MARK - 实现转场动画的代理方法,就是从A->B的动画
extension HomeTableViewController:UIViewControllerAnimatedTransitioning{
//该方法统一设置转场的动画时间
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval
{
return 0.5
}
//该方法负责动画如何执行
func animateTransition(transitionContext: UIViewControllerContextTransitioning)
{
//如果是modal控制器
if isPresented {
guard let toView = transitionContext.viewForKey(UITransitionContextToViewKey) else
{
return
}
//1,先将要弹出的view加到containView上
let containView = transitionContext.containerView()
containView?.addSubview(toView)
//2,设置锚点
toView.layer.anchorPoint = CGPoint.init(x: 0.5, y: 0.0)
//3,设置锚点
toView.transform = CGAffineTransformMakeScale(1.0, 0.0)
//4,开始动画
UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
toView.transform = CGAffineTransformMakeScale(1.0, 1.0)
}) { (_) in
DyLog("done")
//自定义转场动画,执行完后一定要告诉系统已经执行完毕了
transitionContext.completeTransition(true)
}
//如果是modal控制器
}else{
guard let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey) else{
return
}
fromView.layer.anchorPoint = CGPoint.init(x: 0.5, y: 0.0)
fromView.transform = CGAffineTransformMakeScale(1.0, 1.0)
UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
fromView.transform = CGAffineTransformMakeScale(1.0, 0.0001)
}, completion: { (_) in
//自定义转场动画,执行完后一定要告诉系统已经执行完毕了
transitionContext.completeTransition(true)
})
}
}
}
PS
1,注意锚点的设置,锚点主要是设置动画的原坐标,以这个位置为参考进行动画
toView.layer.anchorPoint = CGPoint.init(x: 0.5, y: 0.0)
2,注意动画消失的时间,不能是0.0,在计算机存储这个数值是有误差的,会造成动画异常,因此要设置成0.0001这样较小的数
fromView.transform = CGAffineTransformMakeScale(1.0, 0.0001)
网友评论