iOS怎么让Modal出来的控制器只显示一部分
(文末附Demo)
效果类似于淘宝点击了加入购物车之后弹出来的选择大小和颜色的框.
效果示意图文中代码为了通俗易懂,让读者大致了解具体实现与原理,只注重功能的实现。
封装优化过的代码在Demo中,Demo中的各个文件可以直接拖到自己工程中使用。
首先,你要创建一个控制器,继承UIPresentationController。
在-containerViewWillLayoutSubviews设置布局。
核心代码:(此控制器我未写懒加载部分)
@interface XYPresentationController()
//蒙版
@property(nonatomic,strong)UIView* dimView;
//(加在蒙版上的)点击手势,点击dismiss控制器
@property(nonatomic,strong)UITapGestureRecognizer* tap;
@end
@implementation XYPresentationController
- (void)containerViewWillLayoutSubviews {
[super containerViewWillLayoutSubviews];
//presentedView即我们主要内容显示的区域
UIView* mainView = self.presentedView;
[self.containerView addSubview:mainView];
//弹出Controller之后,上面空白的部分加个蒙版,点击后可以dismiss掉控制器
self.dimView.backgroundColor = [UIColor blackColor];
//此处写死0.5,demo中有封装接口可以在外部传参设置,为了整洁美观,↑↓这两句应该放在懒加载里写,但我这里是为了直观易懂,放在了这里。
self.dimView.alpha = 0.5;
[self.containerView insertSubview:self.dimView atIndex:0];
[self.dimView addGestureRecognizer:self.tap];
[self frameSetup];
}
- (void)frameSetup
{
CGFloat x,y,w,h,cHeight;
//这里写死弹出控制器高度为500.Demo中有封装接口给外界,可以直接传参设置。
cHeight = 500;
x = 0;
//y = 屏幕高度 - 要弹出的控制器的高度
y = [UIScreen mainScreen].bounds.size.height - cHeight;
w = [UIScreen mainScreen].bounds.size.width;
//要弹出的控制器的高度
h = cHeight;
self.presentedView.frame = CGRectMake(x, y, w, h);
y = 0;
h = [UIScreen mainScreen].bounds.size.height;
self.dimView.frame = CGRectMake(x, y, w, h);
}
- (void)tap:(UITapGestureRecognizer *)sender{
[self.presentedViewController dismissViewControllerAnimated:YES completion:nil];
}
然后再添加自定义Animator类继承NSObject,遵守<UIViewControllerTransitioningDelegate>协议。
在类的内部重写方法,新建的控制器就是刚刚你创建的继承UIPresentationController的控制器。
- (nullableUIPresentationController*)presentationControllerForPresentedViewController:(UIViewController*)presented presentingViewController:(nullableUIViewController*)presenting sourceViewController:(UIViewController*)source{
//此处Demo中使用的自定义的init方法,以将外部传进来的参数传给XYPresentationController,所以此处Demo中多加了一步,调用父类的init方法
XYPresentationController* prensentVc = [[XYPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting];
return prensentVc;
}
然后就是创建你想要弹出的UIViewController控制器了。要有个私有属性,即刚刚创建的自定义Animator类。Animator的创建和赋值一定要写在init方法内。布局代码我就不详细写了。
@interface XYHalfViewController()
@end
@implementation XYHalfViewController {
Animator * _myAnimator;
}
- (instancetype)init
{
self = [super init];
if(self)
{
self.modalPresentationStyle = UIModalPresentationCustom;
_myAnimator = [[Animator alloc] init];
self.transitioningDelegate = _myAnimator;
self.view.backgroundColor = [UIColor redColor];
}
return self;
}
然后就是在最初的控制器ViewController里,模态弹出控制器即可。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event{
XYHalfViewController * vc = [[XYHalfViewController alloc] init];
[self presentViewController:vc animated:YES completion:nil];
}
类与类之间的关系图如下图所示:
关系图这样看来,关系是不是清晰很多~
附Demo效果图:
效果图2018.1.18 编辑
注:感谢简书用户 @liusong007 提出的宝贵意见,我在上面多加了一点优化。
要弹出的控制器增加了自定义init方法,可以设置要弹出的控制器的高度和上面留空部分的蒙版的透明度。实现一个方法设置所有需要设定的参数,并增加了参数合理性的判断。
Demo已经改好,放在了微云。。。感兴趣的小伙伴下载下来看看,有什么建议或者意见要提的话,不胜荣幸。🌹
2018.1.27 编辑:
注:Demo中做了一些封装和用户体验优化,上述代码中没有体现
例如半透明蒙版的显示和消失我都加了动画,还有成员属性的设置我都放在了懒加载里,让代码逻辑更加清晰严谨。
文章内容已更新至最新,代码和注释都已经完善。Demo也已经放在了Github上。
小伙伴们看了文章或Demo有什么不懂的地方或者意见和建议欢迎在评论区留言交流。
2020.4.11编辑:
时隔两年博主又来更新了~~有位小伙伴提到如果在半弹出的控制器里点击按钮dismiss,黑色的蒙版就没有那个消失的动画了,而且会延迟消失。针对这个问题做了一下优化。代码已更新至微云和Github。
Demo微云地址:XYHalfModalDemo
Github Demo地址: https://github.com/DXY123/XYHalfModalDemo