UINavigationController的全屏pop之runtime探究

對之前一直寫一直用的功能,來做個(gè)總結(jié)。
系統(tǒng)自帶的pop效果是輕掃左邊邊緣pop返回,要實(shí)現(xiàn)的效果是輕掃全屏pop返回。

思路

要改變系統(tǒng)的效果,1.重寫,2.設(shè)置系統(tǒng)提供的相關(guān)屬性(直接設(shè)置/通過KVC)

探究

首先,我們先去UINavigationController.h源文件中找系統(tǒng)提供的方法或者屬性。@property(nullable, nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;,interactivePopGestureRecognizer是只讀屬性,屬于UIGestureRecognizer這個(gè)類。因?yàn)槭莚eadonly的,所以我們無法重寫和自定義,繼續(xù)點(diǎn)進(jìn)去UIGestureRecognizer.h查看,有一個(gè)enabled屬性,所以可以將enabled設(shè)置為NO,或者猜測是否有私有屬性可以通過KVC搞定的。
于是,log一下interactivePopGestureRecognizer一看究竟

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

打印結(jié)果

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

通過log,interactivePopGestureRecognizer屬性其實(shí)是UIScreenEdgePanGestureRecognizer類,查看UIScreenEdgePanGestureRecognizer.h源文件,繼承自UIPanGestureRecognizer,包含一個(gè)屬性edges,也就是所有邊緣,從這個(gè)枚舉看出都是設(shè)置邊緣的,無法修改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);

所以,我們要做的是,創(chuàng)建一個(gè)UIPanGestureRecognizer手勢,讓它的target和action執(zhí)行系統(tǒng)響應(yīng)的方法,所以可以用runtime獲取系統(tǒng)手勢的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));
}
打印結(jié)果

接下來,可以通過KVC獲取_targets了

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

_targets數(shù)組中就一個(gè)元素,雖然不知道什么類型,可以選擇用 id 接收。如果想繼續(xù)探究,那就打斷點(diǎn)看下控制臺,isa指向UIGestureRecognizerTarget私有類。


斷點(diǎn)調(diào)試
代碼

我們可以在自定義的NavigationController中添加以下代碼

@interface SSNavigationController ()<UIGestureRecognizerDelegate>

@end

@implementation SSNavigationController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initGlobalPan];
}

-(void)initGlobalPan
{
    //取消系統(tǒng)自帶手勢
    self.interactivePopGestureRecognizer.enabled = NO;
    
    //獲取系統(tǒng)的手勢的target數(shù)組
    NSMutableArray *_targets = [self.interactivePopGestureRecognizer valueForKeyPath:@"_targets"];
    //獲取target
    id target = [[_targets firstObject] valueForKeyPath:@"_target"];
    //獲取action
    SEL action = NSSelectorFromString(@"handleNavigationTransition:");
    
    //創(chuàng)建一個(gè)與系統(tǒng)一樣的手勢 只把它的類改為UIPanGestureRecognizer
    UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget: target action: action];
    popRecognizer.delegate = self;
    //添加到系統(tǒng)手勢作用的view上
    UIView *gestureView = self.interactivePopGestureRecognizer.view;
    [gestureView addGestureRecognizer:popRecognizer];
}

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    //當(dāng)前控制器為根控制器,pop動(dòng)畫正在執(zhí)行的時(shí)候不允許手勢
    return self.viewControllers.count != 1 && ![[self valueForKeyPath:@"_isTransitioning"] boolValue];
}

@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容