效果图:

设置模态自定义
- UIModalPresentationStyle 用来设置模态的时候自定义转场动画。
typedefNS_ENUM(NSInteger, UIModalPresentationStyle) {
UIModalPresentationFullScreen =0,//由下到上,全屏覆盖
UIModalPresentationPageSheet,//在portrait时是FullScreen,在landscape时和FormSheet模式一样。
UIModalPresentationFormSheet,// 会将窗口缩小,使之居于屏幕中间。在portrait和landscape下都一样,但要注意landscape下如果软键盘出现,窗口位置会调整。
UIModalPresentationCurrentContext,//这种模式下,presented VC的弹出方式和presenting VC的父VC的方式相同。
UIModalPresentationCustom,//自定义视图展示风格,由一个自定义演示控制器和一个或多个自定义动画对象组成。符合UIViewControllerTransitioningDelegate协议。使用视图控制器的transitioningDelegate设定您的自定义转换。
UIModalPresentationOverFullScreen,//如果视图没有被填满,底层视图可以透过
UIModalPresentationOverCurrentContext,//视图全部被透过
UIModalPresentationPopover,
UIModalPresentationNone ,
};
- 我们在ViewController中设置自定义模式和代理。
// 设置代理和present样式
self.transitioningDelegate = self;
self.modalPresentationStyle = UIModalPresentationCustom;
- 添加手势,用来滑动dismiss上一个页面
// 添加手势
UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc] init];
pan.delegate = self;
[pan addTarget:self action:@selector(panGestureRecognizerAction:)];
self.view.tag = 10001;
[self.view addGestureRecognizer:pan];
- 手势处理滑动的逻辑
- (void)panGestureRecognizerAction:(UIPanGestureRecognizer *)pan
{
// 产生百分比
CGFloat process = ([pan translationInView:self.view].y) / ([UIScreen mainScreen].bounds.size.height);
process = MIN(1.0,(MAX(0.0, process)));
// 开始滑动的时候
if (pan.state == UIGestureRecognizerStateBegan)
{
self.interactiveTransition = [UIPercentDrivenInteractiveTransition new];
// 触发dismiss转场动画
[self dismissViewControllerAnimated:YES completion:nil];
}
// 手势滑动的时候
else if (pan.state == UIGestureRecognizerStateChanged)
{
[self.interactiveTransition updateInteractiveTransition:process];
}
// 滑动手势结束或者取消
else if (pan.state == UIGestureRecognizerStateEnded
|| pan.state == UIGestureRecognizerStateCancelled)
{
// 滑动百分比大于0.3完成动画
if (process >= 0.3)
{
[ self.interactiveTransition finishInteractiveTransition];
}
else
{
[ self.interactiveTransition cancelInteractiveTransition];
}
self.interactiveTransition = nil;
}
}
- 实现代理 UIViewControllerTransitioningDelegate
#pragma mark - UIViewControllerTransitioningDelegate
// 设置继承自UIPresentationController 的自定义类的属性
- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
{
ZJPresentationController *presentVC = [[ZJPresentationController alloc]initWithPresentedViewController:presented presentingViewController:presenting];
// 设置距离顶部的高度
presentVC.height = STATUSBAR_H;
return presentVC;
}
//控制器创建执行的动画(返回一个实现UIViewControllerAnimatedTransitioning协议的类)
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
//创建实现UIViewControllerAnimatedTransitioning协议的类(命名为AnimatedTransitioning)
ZJPresentAnimation *animation = [[ZJPresentAnimation alloc] init];
//将其状态改为出现
animation.presented = YES;
return animation;
}
// 控制器销毁执行的动画(返回一个实现UIViewControllerAnimatedTransitioning协议的类)
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
////创建实现UIViewControllerAnimatedTransitioning协议的类(命名为AnimatedTransitioning)
ZJPresentAnimation *animation = [[ZJPresentAnimation alloc] init];
//将其状态改为出现
animation.presented = NO;
return animation;
}
// 返回一个交互的对象(实现UIViewControllerInteractiveTransitioning协议的类)
-(id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator{
return self.interactiveTransition;
}
自定义转成动画
- 实现UIViewControllerAnimatedTransitioning协议,包含了动画的时间和具体的实现。
// 自定义转场动画时间
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
return 0.34f;
}
// 自定义转成动画具体实现
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
// 到哪个vc
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// 从哪个vc来
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
// 获取过渡时候的容器view
UIView *contentView = [transitionContext containerView];;
// 移除之前的
for (UIView *subView in [contentView subviews]) {
if (subView.tag == 1001 ) {
[subView removeFromSuperview];
}
}
UIView * transView = nil;
// 如果是present
if (_presented) {
// 记录present的view
transView = toViewController.view;
}
else {
transView = fromViewController.view;
}
// y值
CGFloat y = transView.frame.origin.y;
transView.frame = CGRectMake(0, _presented ?SCREEN_H :y, SCREEN_W, SCREEN_H);
// 黑色背景
UIView *viewCover = [[UIView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_W, SCREEN_H)];
viewCover.backgroundColor = [UIColor blackColor];
viewCover.alpha = 0.5;
viewCover.tag = 1001;
[contentView insertSubview:viewCover belowSubview:transView];
if (_presented) {
// 执行动画过程
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:2 options:UIViewAnimationOptionLayoutSubviews animations:^{
transView.frame = CGRectMake(0, y , SCREEN_W, SCREEN_H);
} completion:^(BOOL finished) {
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
else
{
// 下拉动画
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
transView.frame = CGRectMake(0, SCREEN_H, SCREEN_W, SCREEN_H);
viewCover.alpha= 0;
} completion:^(BOOL finished) {
if (!transitionContext.transitionWasCancelled) {
[viewCover removeFromSuperview];
}
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
}
手势冲突的处理
- 弹出的ViewController里面包含UITableView的话,在滑动的顶部的时候想要触发滑动dismiss的手势的处理方式。
#pragma mark --UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
// 支持多手势
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0)
{
// 这个方法返回YES,第一个手势和第二个互斥时,第一个会失效
if ([otherGestureRecognizer.view isKindOfClass:[UITableView class]]) {
return YES;
}
return NO;
}
- 在处理UITableView滑动到顶部时候的逻辑
#pragma mark --UIScrollViewDelegate
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
[self setGestureRecognizerEnable:YES scrollView:scrollView];
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self setGestureRecognizerEnable:YES scrollView:scrollView];
}
-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
// 结束的时候手势可用
[self setGestureRecognizerEnable:YES scrollView:scrollView];
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// 滑动到顶部的时候设置手势失效
if (scrollView.contentOffset.y <=0) {
[self setGestureRecognizerEnable:NO scrollView:scrollView];
}
else
{
// 手势可用
[self setGestureRecognizerEnable:YES scrollView:scrollView];
}
}
-(void)setGestureRecognizerEnable:(BOOL)isEnable scrollView:(UIScrollView *)scrollView
{
for (UIGestureRecognizer *gesRec in scrollView.gestureRecognizers) {
if ([gesRec isKindOfClass:[UIPanGestureRecognizer class]]) {
gesRec.enabled =isEnable;
}
}
}
处理方式是滑动到顶部的时候,禁用UITableview的手势,然后触发当前ViewController中的手势滑动dismiss会上一个页面的手势。