美文网首页
UINavigationController的全屏pop之run

UINavigationController的全屏pop之run

作者: GemShi | 来源:发表于2018-10-25 17:00 被阅读76次

    对之前一直写一直用的功能,来做个总结。
    系统自带的pop效果是轻扫左边边缘pop返回,要实现的效果是轻扫全屏pop返回。

    思路

    要改变系统的效果,1.重写,2.设置系统提供的相关属性(直接设置/通过KVC)

    探究

    首先,我们先去UINavigationController.h源文件中找系统提供的方法或者属性。@property(nullable, nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;,interactivePopGestureRecognizer是只读属性,属于UIGestureRecognizer这个类。因为是readonly的,所以我们无法重写和自定义,继续点进去UIGestureRecognizer.h查看,有一个enabled属性,所以可以将enabled设置为NO,或者猜测是否有私有属性可以通过KVC搞定的。
    于是,log一下interactivePopGestureRecognizer一看究竟

    NSLog(@"%@",self.navigationController.interactivePopGestureRecognizer);
    

    打印结果

    <UIScreenEdgePanGestureRecognizer: 0x7fd9a160cb30; state = Possible; enabled = NO; delaysTouchesBegan = YES; view = <UILayoutContainerView 0x7fd9a1415480>; target= <(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7fd9a16003c0>)>>
    

    通过log,interactivePopGestureRecognizer属性其实是UIScreenEdgePanGestureRecognizer类,查看UIScreenEdgePanGestureRecognizer.h源文件,继承自UIPanGestureRecognizer,包含一个属性edges,也就是所有边缘,从这个枚举看出都是设置边缘的,无法修改edges为全部

    typedef NS_OPTIONS(NSUInteger, UIRectEdge) {
        UIRectEdgeNone   = 0,
        UIRectEdgeTop    = 1 << 0,
        UIRectEdgeLeft   = 1 << 1,
        UIRectEdgeBottom = 1 << 2,
        UIRectEdgeRight  = 1 << 3,
        UIRectEdgeAll    = UIRectEdgeTop | UIRectEdgeLeft | UIRectEdgeBottom | UIRectEdgeRight
    } NS_ENUM_AVAILABLE_IOS(7_0);
    

    所以,我们要做的是,创建一个UIPanGestureRecognizer手势,让它的target和action执行系统响应的方法,所以可以用runtime获取系统手势的target和action

    unsigned int count = 0;
    Ivar *var = class_copyIvarList([UIGestureRecognizer class], &count);
    for (int i = 0; i < count; i++) {
        Ivar _var = *(var + i);
        NSLog(@"%s ------ %s",ivar_getName(_var),ivar_getTypeEncoding(_var));
    }
    
    打印结果

    接下来,可以通过KVC获取_targets了

    NSMutableArray *targets = [self.navigationController.interactivePopGestureRecognizer valueForKeyPath:@"_targets"];
    NSLog(@"%@",targets);
    /*
    (
        "(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7fdbf2e02110>)"
    )
    */
    

    _targets数组中就一个元素,虽然不知道什么类型,可以选择用 id 接收。如果想继续探究,那就打断点看下控制台,isa指向UIGestureRecognizerTarget私有类。


    断点调试
    代码

    我们可以在自定义的NavigationController中添加以下代码

    @interface SSNavigationController ()<UIGestureRecognizerDelegate>
    
    @end
    
    @implementation SSNavigationController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self initGlobalPan];
    }
    
    -(void)initGlobalPan
    {
        //取消系统自带手势
        self.interactivePopGestureRecognizer.enabled = NO;
        
        //获取系统的手势的target数组
        NSMutableArray *_targets = [self.interactivePopGestureRecognizer valueForKeyPath:@"_targets"];
        //获取target
        id target = [[_targets firstObject] valueForKeyPath:@"_target"];
        //获取action
        SEL action = NSSelectorFromString(@"handleNavigationTransition:");
        
        //创建一个与系统一样的手势 只把它的类改为UIPanGestureRecognizer
        UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget: target action: action];
        popRecognizer.delegate = self;
        //添加到系统手势作用的view上
        UIView *gestureView = self.interactivePopGestureRecognizer.view;
        [gestureView addGestureRecognizer:popRecognizer];
    }
    
    -(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
    {
        //当前控制器为根控制器,pop动画正在执行的时候不允许手势
        return self.viewControllers.count != 1 && ![[self valueForKeyPath:@"_isTransitioning"] boolValue];
    }
    
    @end
    

    相关文章

      网友评论

          本文标题:UINavigationController的全屏pop之run

          本文链接:https://www.haomeiwen.com/subject/byxctqtx.html