dismiss返回 ios_ios dismiss 左侧 - CSDN
  • iOS开发--下滑返回dismiss

    千次阅读 2018-05-18 19:47:28
    现在越来越多的应用有看大图或者进入详情页面,但是,再返回的时候,普通意义上,会点击左上角的返回,这时候你就会发现,还需要将手指移动到左上角,这样,无意给用户增添了麻烦,并且,现在手机屏幕越来越大,这样返回的...

    简介

    现在越来越多的应用有看大图或者进入详情页面,但是,再返回的时候,普通意义上,会点击左上角的返回,这时候你就会发现,还需要将手指移动到左上角,这样,无意给用户增添了麻烦,并且,现在手机屏幕越来越大,这样返回的越来越困难,在体验上特别的差劲.

    尽管苹果推出了从左边缘右滑返回,FDFullscreenPopGesture这个很强大的,全屏右滑返回…..尽管现在考虑到用户体验上,已经有了很大的提升,但是,仍然,在大屏上,不是很好操作…因为,你在正常使用手机的时候,大拇指使用的频率要远远大于其他手指,而且,大拇指,上下滑动的体验度是要大于左右滑动的体验度的…..所以,这里就有着下滑返回的需求必要的…

    开始

    解决完需求的原因,下面我们来看看如何做?

    1.转场动画的设置

    1. 遵循协议

    需要转场的ViewCointroller遵循:UIViewControllerTransitioningDelegate

    2. 转场代理设置

    // 设置Presented的动画
    - (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
        return [[PresentVCAnimation alloc] init];
    }
    
    /// 设置Dismiss返回的动画设置
    - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
        return [[DismissVCAnimation alloc] init];
    }
    
    /// 设置过场动画
    - (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator {
        return (self.interactiveTransition.isInteracting ? self.interactiveTransition : nil);
    }

    2. PresentVCAnimation

    PresentVCAnimation.h

    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    @interface PresentVCAnimation : NSObject<UIViewControllerAnimatedTransitioning>
    
    @end

    PresentVCAnimation.m

    #import "PresentVCAnimation.h"
    
    @implementation PresentVCAnimation
    // 设置动画的时间长度
    -(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
        return 0.25;
    }
    
    -(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
        // 前一个ViewController,动画的发起者
        UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        // 后一个ViewController,动画的结束者
        UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        // 转场动画的最终的frame
        CGRect finalFrameForVC = [transitionContext finalFrameForViewController:toVC];
        // 下面敲黑板啦
        // 转换的容器view,这里是存放转场动画的容器
        UIView *containerView = [transitionContext containerView];
        // 这里一般情况下,没有涉及到VC的View放大或者缩小,即可看做是屏幕的尺寸
        CGRect bounds = [UIScreen mainScreen].bounds;
        // 这是后一个ViewController的frame
        toVC.view.frame = CGRectOffset(finalFrameForVC, 0, bounds.size.height);
        [containerView addSubview:toVC.view];
    
        // 下面是改变前一个ViewController和后一个ViewController的动画
        [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
            fromVC.view.alpha = 0.5;
            toVC.view.frame = finalFrameForVC;
        } completion:^(BOOL finished) {
            [transitionContext completeTransition:YES];
            fromVC.view.alpha = 1.0;
        }];
    }
    
    -(void)animationEnded:(BOOL)transitionCompleted {
    }

    3.DismissVCAnimation

    DismissVCAnimation.h

    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    @interface DismissVCAnimation : NSObject<UIViewControllerAnimatedTransitioning>
    
    @end

    DismissVCAnimation.m

    #import "DismissVCAnimation.h"
    
    @implementation DismissVCAnimation
    -(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
        return 0.25;
    }
    
    -(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
        // 前一个ViewController,动画的发起者
        UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        // 后一个ViewController,动画的结束者
        UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
        CGRect screenBounds = [UIScreen mainScreen].bounds;
        // 获取前一个页面的frame
        CGRect initFrame = [transitionContext initialFrameForViewController:fromVC];
         // 转场动画的toView的最终的frame
        CGRect finalFrame = CGRectOffset(initFrame, 0, screenBounds.size.height);
        // 转换的容器view
        UIView *containerView = [transitionContext containerView];
        // 下面这里是为了让转场动画衔接的更和谐,不然,下滑一点距离就直接看到之前页面的内容,体验不好
        UIView *bgView = [[UIView alloc] initWithFrame:fromVC.view.bounds];
        bgView.backgroundColor = [UIColor blackColor];
        [toVC.view addSubview:bgView ];
    
        [containerView addSubview:toVC.view];
        [containerView sendSubviewToBack:toVC.view];
    
        [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
            fromVC.view.frame = finalFrame;
            bgView.alpha = 0;
        } completion:^(BOOL finished) {
            [bgView removeFromSuperview];
            BOOL complate = [transitionContext transitionWasCancelled];
            [transitionContext completeTransition:(!complate)];
        }];
    }
    
    -(void)animationEnded:(BOOL)transitionCompleted {
    }
    @end

    4.SwipeUpInteractiveTransition

    这个是针对滑动手势对转场动画的影响的类.

    这里面主要处理了滑动行为的状态,需要重点了解的是:
    1.判断用户是不是存在返回意图的行为,这里面规定了两种行为,而这两种行为,均为在当前页面手势结束的时候处理

    返回意图的行为的判断条件:

    • 当下滑距离大于当前屏幕比例的 0.382 (无耻的取了黄金比例的对半,因为,感觉有逼格,可进行相应调整)
    • 快速滑动行为,也就是滑动速度 : iOS开发–手势滑动的速度

    SwipeUpInteractiveTransition.h

    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    
    @interface SwipeUpInteractiveTransition : UIPercentDrivenInteractiveTransition
    /// 手势中...
    @property (nonatomic, assign) BOOL isInteracting;
    /// 完成动画
    @property (nonatomic, assign) BOOL shouldComplete;
    // 初始化
    - (instancetype)initWithGestureViewController:(UIViewController *)gestureVC;
    @end

    SwipeUpInteractiveTransition.m

    #define KEY_WINDOW  [[UIApplication sharedApplication].delegate window]
    
    #import "SwipeUpInteractiveTransition.h"
    
    @interface SwipeUpInteractiveTransition()
    // 手势添加的View
    @property (nonatomic, strong) UIViewController *gestureVC;
    // 记录手势结束前的点击位置
    @property (nonatomic, assign) CGPoint oldTranslation;
    // 是不是需要返回,这里是需要猜想并判断用户是不是存在返回行为
    @property (nonatomic, assign) BOOL isNeedDismiss;
    @end
    
    @implementation SwipeUpInteractiveTransition
    - (instancetype)initWithGestureViewController:(UIViewController *)gestureVC
    {
        self = [super init];
        if (self) {
            _gestureVC = gestureVC;
            // 添加手势
            UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureHandler:)];
            [_gestureVC.view addGestureRecognizer:pan];
        }
        return self;
    }
    
    - (void)panGestureHandler:(UIPanGestureRecognizer *)gesture {
        // 获取手势触控的点在作用View的相对位置
        CGPoint translation = [gesture translationInView:gesture.view];
        // 每次手势触发的时候,重置,也就是用户不存在返回意图
        self.isNeedDismiss = NO;
        switch (gesture.state) {
            case UIGestureRecognizerStateBegan: {
                // 手势开始
                // 交互动画判断
                _isInteracting = YES;
                [_gestureVC dismissViewControllerAnimated:YES completion:nil];
    
                break;
            }
            case UIGestureRecognizerStateChanged: {
            // 当前触控点的赋值
                self.oldTranslation = translation;
                // 触控点的转化,因为updateInteractiveTransition的参数范围是[0.1],所以这里需要左边比例的转换
                CGFloat fraction = (translation.y / KEY_WINDOW.bounds.size.height);
                // 保证范围
                fraction = fmin(fmaxf(fraction, 0.0), 1.0);
                // 这里进行滑动中的判断,取的是黄金比例,也就是,如果滑动距离占比约38%,即可判断用户存在返回的行为
                _shouldComplete = fraction > 0.382;
                // 更新进度
                [self updateInteractiveTransition:fraction];
                break;
            }
            case UIGestureRecognizerStateEnded: {
               // 这里是重要的判断
               // 如果用户存在快速向下滑动的行为(等同于全屏快速向右滑动返回的行为),self.isNeedDismiss为YES
               // 而这个判断的快速范围如下 
                CGPoint speed = [gesture velocityInView:gesture.view];
               // 这个数据经过了大量测试和舒适度的数据,大家可以参考,具体的还需要根据实际情况而定.可以看我的量
                if (speed.y >= 920) {
                    self.isNeedDismiss = YES;
                    _shouldComplete = YES;
                }
                // 手势交互结束
                _isInteracting = NO;
                // 下面进行之前判断的处理
                // 使用dispatch_source_t定时回调,进行改变self.oldTranslation的数据,进行模拟手势移动,因为如果直接调用[self cancelInteractiveTransition];或者[self updateInteractiveTransition:fraction];就会发现,动画瞬间变化,瞬间复位或者瞬间消失返回.考虑到用户体验,如下
                dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
                //定时器模式  事件源
                dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, quene);
                //NSEC_PER_SEC是秒,*1是每秒
                dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), NSEC_PER_SEC * 0.0001, 0);
                //设置响应dispatch源事件的block,在dispatch源指定的队列上运行
                dispatch_source_set_event_handler(timer, ^{
                    //回调主线程,在主线程中操作UI
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if (!_isNeedDismiss && (!_shouldComplete || gesture.state == UIGestureRecognizerStateCancelled)) {
                            if (self.oldTranslation.y <= 0) {
                            // 当满足条件,执行取消动画
                                [self cancelInteractiveTransition];
                                // 移除时间回调
                                dispatch_source_cancel(timer);
                            } else {
                            // 模拟上滑行为
                                CGFloat fraction = (self.oldTranslation.y / KEY_WINDOW.bounds.size.height);
                                fraction = fmin(fmaxf(fraction, 0.0), 1.0);
                                self.oldTranslation = CGPointMake(self.oldTranslation.x, self.oldTranslation.y - 0.3);
    
                                [self updateInteractiveTransition:fraction];
                            }
                        } else {
                            if (self.oldTranslation.y > KEY_WINDOW.bounds.size.height) {
                            // 当滑出当前屏幕,执行完成动画
                                [self finishInteractiveTransition];
                                                            // 移除时间回调
                                dispatch_source_cancel(timer);
                            } else {
                            // 模拟下滑行为
                                CGFloat fraction = (self.oldTranslation.y / KEY_WINDOW.bounds.size.height);
                                fraction = fmin(fmaxf(fraction, 0.0), 1.0);
                                self.oldTranslation = CGPointMake(self.oldTranslation.x, self.oldTranslation.y + 0.3);
    
                                [self updateInteractiveTransition:fraction];
                            }
                        }
                    });
                });
                //启动源
                dispatch_resume(timer);
                break;
            }
            default:
                break;
        }
    }
    @end
    展开全文
  • C dismiss回到 A 对数模态弹出的控制器不像push的那样,有系统的导航栏统一管理。为实现这一目的,我仔细研究了官方文档,发现了这么一句话 The presenting view controller is responsible for dismissing the ...

    项目中经常有这样的需求:
    A present弹出 B
    B present弹出 C
    C dismiss回到 A
    对数模态弹出的控制器不像push的那样,有系统的导航栏统一管理。为实现这一目的,我仔细研究了官方文档,发现了这么一句话

    The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.

    也就是说,谁污染谁治理,A present弹出 B,本应该由A dismiss B然后回到再A。
    我们一般都是直接用B dismiss,这是因为系统会自动优化,当B视图控制器没有present弹出过其他视图控制器的时候,dismissViewController方法会自动交给B的presentingViewController执行,也就是A视图。

    If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.

    也就是说,其实在present多个视图控制器的时候,系统也维护了一个栈,以我们现在这个情况为例,从栈底到栈顶依次是A->B->C。当栈中某个位置的视图控制器执行dismissViewController方法的时候,栈中所有在它之上的视图控制器都会被dismiss,不同的是,栈顶的视图控制器将会以动画方式被dismiss,而中间的视图控制器只是简单的remove掉。

    所以想要实现直接从C跳转回A也简单,就是直接在A中执行dismissViewController方法,此时B也会自动销毁,同pop方法类似。但是我们需要在每一个弹出的控制器中都设置监听,来实现谁污染谁治理这个规则。这样就太乱了。

    好在苹果应该是想到这一点,为我们提供了两个属性,分别是presentingViewController和presentedViewController。
    A弹出B 则A是B的presentingViewController,
    B是A的presentedViewController。如果B还弹出了C,那么B同时也是C的presentingViewController。

    说了这么多,也该上代码了,同上pop一样,我也是写了一个Extension,复制粘贴即可轻松实现dismiss多层。不过呢我没有封装完成后的回调,需要的同学可以自己加上。

    .h 文件

    @interface UIViewController (Extension)
    /**  在主线程执行操作*/
    - (void)performSelectorOnMainThread:(void(^)(void))block;
    
    /**  退出 presentViewController  count:次数*/
    - (void)dismissViewControllerWithCount:(NSInteger)count animated:(BOOL)animated;
    
    
    /**  退出 presentViewController 到指定的控制器*/
    - (void)dismissToViewControllerWithClassName:(NSString *)className animated:(BOOL)animated;
    
    @end
    
    
    

    .m 文件

    
    @implementation UIViewController (Extension)
    /**  在主线程执行操作*/
    - (void)performSelectorOnMainThread:(void(^)(void))block{
        if ([[NSThread currentThread] isMainThread]) {
            block();
        }else{
            dispatch_sync(dispatch_get_main_queue(), ^{
                block();
            });
        }
    }
    
    
    /**  退出 presentViewController  count:次数*/
    - (void)dismissViewControllerWithCount:(NSInteger)count animated:(BOOL)animated{
    
        count--;
        // 不是自己,并且自己弹出过VC, 递归交给自己弹出的VC处理
        if (count>0 && self.presentingViewController) {
            [self.presentingViewController dismissViewControllerWithCount:count animated:animated];
        }
        else{
            [self dismissViewControllerAnimated:animated completion:nil];
        }
    }
    
    
    
    /**  退出 presentViewController 到指定的控制器*/
    - (void)dismissToViewControllerWithClassName:(NSString *)className animated:(BOOL)animated{
    
        // 不是自己,并且自己弹出过VC, 递归交给自己弹出的VC处理
        if (![self.class isKindOfClass:NSClassFromString(className)] && self.presentingViewController) {
            [self.presentingViewController dismissToViewControllerWithClassName:className animated:animated];
        }else{
            [self dismissViewControllerAnimated:animated completion:nil];
        }
    }
    
    @end
    
    
    
    
    
    展开全文
  • 当我们自定义继承UIViewController视图控制器的基类视图控制器时,会新增返回到上级的视图控制器方法,于是就要区分,返回时使用 pop 的方法,还是使用dismiss 的方法了。 方法1 - (void)backPreviousController {...

    当我们自定义继承UIViewController视图控制器的基类视图控制器时,会新增返回到上级的视图控制器方法,于是就要区分,返回时使用 pop 的方法,还是使用dismiss 的方法了。

    方法1

    - (void)backPreviousController
    {
        if (self.presentingViewController) 
        {
            [self dismissViewControllerAnimated:YES completion:nil];
        } 
        else 
        {
            [self.navigationController popViewControllerAnimated:YES];
        }
    }

    方法2

    - (void)backPreviousController
    {
        if (self.navigationController.topViewController == self) 
       {
            [self.navigationController popViewControllerAnimated:YES];
        } 
        else 
        {
            [self dismissViewControllerAnimated:YES completion:nil];
        }
    }

    方法3

    - (void)backPreviousController
    {
        if ([self.navigationController.viewControllers.firstObject isEqual:self])
        {
            [self dismissViewControllerAnimated:YES completion:nil];
        }
        else
        {
            [self.navigationController popViewControllerAnimated:YES];
        }
    }

    - (void)backPreviousController
    {
        if ([self.navigationController.viewControllers indexOfObject:self] == 0)
        {
            [self dismissViewControllerAnimated:YES completion:nil];
        }
        else
        {
            [self.navigationController popViewControllerAnimated:YES];
        }
    }


    展开全文
  • iOS dismiss到最底层的控制器方法

    千次阅读 2016-10-12 21:02:14
    iOS开发中,跳转页面最常用的就是push和present这两种方法。在push中有一个方法是popToRootViewControllerAnimated,返回到最底层的控制器。而在present方法中,只有一个 dismissViewControllerAnimated:(BOOL)#> ...

    iOS开发中,跳转页面最常用的就是push和present这两种方法。在push中有一个方法是popToRootViewControllerAnimated,返回到最底层的控制器。而在present方法中,只有一个 dismissViewControllerAnimated:<#(BOOL)#> completion:<#^(void)completion#> 返回上层控制器的方法。我们可以通过以下方法进行让我们的dismiss方法可以返回到最底层的控制器,这里就是用到了UIViewController中的一个属性是presentingViewController,这个是present到当前界面的上一个界面。
    具体代码如下:

    -(void)dismissToRootViewController  {
        UIViewController *vc = self;
        while (vc.presentingViewController) {
          vc = vc.presentingViewController;
        }
        [vc dismissViewControllerAnimated:YES completion:nil];
    }
    展开全文
  • iOS 7 present/dismiss自定义转场动画

    千次阅读 2016-07-13 09:50:49
    前言iOS 7以后提供了自定义转场动画的功能,我们可以通过遵守协议完成自定义转场动画。本篇文章讲解如何实现自定义present、dismiss自定义动画。 关于自定义push/pop转场动画,请阅读iOS 7 push/pop自定义转场动画 ...
  • iOS 模态视图跳转的页面WebView中加载html调用本机相册会dismiss返回上一个页面解决办法
  • 最近项目中用到这样一个逻辑:三个视图控制器:A,B,C,A模态跳转到B,B在模态跳转到C,等C操作完后要直接跳转回A,那么问题就来了,我们平时一般用到的只是从谁跳转过来,就在跳转回去,那怎么实现多级返回呢?...
  • NSArray *viewcontrollers = self.navigationController.viewControllers;    if (viewcontrollers.count > 1) {    if ([viewcontrollers objectAtIndex:viewcontrollers.
  • 如题,苹果自带的是没有直接dismissToRootViewController之类的方法,如果需要直接dismiss到最底层的那个控制器,可以自己写一个dismissToRootViewController方法,代码如下: -(void)dismissToRootViewController ...
  • 转载:http://onevcat.com/2013/10/vc-transition-in-ios7/ 这是我的WWDC2013系列笔记中的一篇,完整的笔记列表请参看这篇总览。本文仅作为个人记录使用,也欢迎在许可协议范围内转载或使用,但是还烦请保留原文...
  • iOS 连续返回两级页面

    2020-03-03 14:05:07
    //当前视图在导航栏控制器子控制器数组的页面索引值 NSInteger index = [self.navigationController....//此处想返回几级就减去几级 [self.navigationController popToViewController:[self.navigationController.vi...
  • iOS -- Present/Dismiss之Animation简谈

    千次阅读 2016-08-10 11:21:34
    需求:需要在当前界面弹出一个视图框(这样的需求很多,采用的方式也比较多),因为弹出的视图控件和操作都比较多,避免当前视图控制器的代码冗余,所以不得寻求其他方法。多番思量还是觉得present当属首选test:1....
  • ios返回上一页面的方法

    千次阅读 2018-03-09 22:33:15
    返回的方法有很多中,现在我却只会一种方法: 1 如果启动页面使用的是presentViewController 启动的 跳回时可以使用以下方法dismissViewControllerAnimated 2 使用UINavigationController 老实说,一开始根本...
  • 然后我尝试present到一个普通的viewcontroller,在里面新建tabbarcontroller的对象,添加view,然后再通过设置代理在viewcontroller中dismiss依然还是无用,求大神指点 而且每次报错都是这边 “0x124909b: movl 8(%...
  • iOS开发中界面跳转是必不可少的一个功能,一个完整的App必然是由多个界面组成的。我在这篇博客中将会较为全面的讲解在是否有导航的情况下进行跳转以及返回,并且对modal、push、custom等视图做一个介绍。代码我已经...
  • 如图所示,当我们点击 消息、好友、我的按钮之后,如果用户...在返回按钮的方法中实现如下代码: self.navigationController.tabBarController.hidesBottomBarWhenPushed=NO; self.navigationController.tabBarC
  • dismiss两级页面

    2019-07-20 13:32:08
    A present B,B present C,直接由C dismiss A UIViewController *presentViewController = [self presentingViewController]; UIViewController *lastViewController = self; while (presentViewCont...
  • 情景:一级页面不显示导航栏 ,二级页面显示导航栏. 方法一 适用于push/pop: 一级页面中 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated];... [self.navigationController ...
  • NSArray *viewControllers=[self.navigationController viewControllers]; UIViewController *controller=[viewControllers objectAtIndex:1]; [self.navigationController popToViewController:controller a
  • iOS 返回父视图之后如何刷新页面

    千次阅读 2015-12-15 10:52:54
    view B tableview中的cell...view A pop回来的时候,我想reload view B中的tableview 在B视图 -(void)viewDidAppear:(BOOL)animated{} 这个方法里面重新[self.tableview reloadData]; 要注意 刷新数据 不然没有效果
1 2 3 4 5 ... 20
收藏数 2,941
精华内容 1,176
关键字:

dismiss返回 ios