IOS手勢滑動返回總結(jié)(邊緣+全屏)

為了提高用戶體驗(yàn)锯玛,在controller會加上這個操作咐柜,我自己寫了好多次兼蜈,但是沒有系統(tǒng)的整理過,這會兒又做到這個功能了拙友,索性整理一下为狸。

一、邊緣滑動返回

在遠(yuǎn)古時代遗契,大概是ios7之前辐棒,滑動返回這個事兒是不被官方支持的,因?yàn)槭謾C(jī)屏幕沒那么大牍蜂,IOS7以后漾根,蘋果為了提升用戶體驗(yàn),增加了【邊緣返回】的手勢鲫竞,注意是邊緣立叛,不是全屏,并且在特定條件下贡茅,邊緣返回會失效秘蛇,具體是以下幾種情況:

1. 自定義了navigationItem的leftBarButtonItem或leftBarButtonItems

2. self.navigationItem.hidesBackButton = YES

3. self.navigationItem.leftItemsSupplementBackButton = NO


為了解決以上問題,有兩個方案顶考,拿捏:


方案一:

在UINavigationController基類添加以下:

- (void)viewDidLoad

{

? ? [super viewDidLoad];

? ? //設(shè)置右滑返回手勢的代理為自身

? ? __weak typeof(self) weakself = self;

? ? if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {

? ? ? ? self.interactivePopGestureRecognizer.delegate = (id)weakself;

? ? }

}

#pragma mark - UIGestureRecognizerDelegate

//這個方法是在手勢將要激活前調(diào)用:返回YES允許右滑手勢的激活赁还,返回NO不允許右滑手勢的激活

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

{

? ? if (gestureRecognizer == self.interactivePopGestureRecognizer) {

? ? ? ? //屏蔽調(diào)用rootViewController的滑動返回手勢,避免右滑返回手勢引起crash

? ? ? ? if (self.viewControllers.count < 2 ||

self.visibleViewController == [self.viewControllers objectAtIndex:0]) {

? ? ? ? ? ? return NO;

? ? ? ? }

? ? }

? ? //這里就是非右滑手勢調(diào)用的方法啦驹沿,統(tǒng)一允許激活

? ? return YES;

}


那么艘策,在特定場景下,我們不希望用戶輕易返回渊季,比如在直播間內(nèi)朋蔫、在掃碼界面等,拿捏:


創(chuàng)建一個UIViewController 的分類:

+ (void)popGestureClose:(UIViewController *)VC

{

? ? // 禁用側(cè)滑返回手勢

? ? if ([VC.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {

? ? ? ? //這里對添加到右滑視圖上的所有手勢禁用

? ? ? ? for (UIGestureRecognizer *popGesture in VC.navigationController.interactivePopGestureRecognizer.view.gestureRecognizers) {

? ? ? ? ? ? popGesture.enabled = NO;

? ? ? ? }

? ? ? ? //若開啟全屏右滑却汉,不能再使用下面方法驯妄,請對數(shù)組進(jìn)行處理

? ? ? ? //VC.navigationController.interactivePopGestureRecognizer.enabled = NO;

? ? }

}

+ (void)popGestureOpen:(UIViewController *)VC

{

? ? // 啟用側(cè)滑返回手勢

? ? if ([VC.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {

? ? //這里對添加到右滑視圖上的所有手勢啟用

? ? ? ? for (UIGestureRecognizer *popGesture in VC.navigationController.interactivePopGestureRecognizer.view.gestureRecognizers) {

? ? ? ? ? ? popGesture.enabled = YES;

? ? ? ? }

? ? ? ? //若開啟全屏右滑,不能再使用下面方法合砂,請對數(shù)組進(jìn)行處理

? ? ? ? //VC.navigationController.interactivePopGestureRecognizer.enabled = YES;

? ? }

}


使用:

- (void)viewDidAppear:(BOOL)animated

{

? ? [super viewDidAppear:animated];

? ? [UIViewController popGestureClose:self]; //關(guān)閉邊緣返回

}

- (void)viewWillDisappear:(BOOL)animated

{

? ? [super viewWillDisappear:animated];

? ? [UIViewController popGestureOpen:self]; //啟動邊緣返回

}


方案二:

每個UIViewController都有一個backBarButtonItem青扔,這是個特殊屬性,只響應(yīng)頁面的返回和銷毀翩伪,表現(xiàn)為:只能自定義image和title微猖,不能重寫target 或 action。(注意:UINavigationController的左側(cè)是不支持右滑返回手勢的)我們通過自定義backBarButtonItem缘屹,來實(shí)現(xiàn):既實(shí)現(xiàn)“自定義返回按鈕(通常自定義leftBarButtonItem或leftBarButtonItems都是為了實(shí)現(xiàn)自定義返回按鈕)”又保留滑動返回凛剥。

拿捏:

在UIViewController基類:

- (void)viewDidLoad{


? ? [super viewDidLoad];

UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];

? ? //自定義返回按鈕的視圖

? ? [self.navigationController.navigationBar setBackIndicatorImage:[UIImage imageNamed:@"navi_back_icon"]];

? ? [self.navigationController.navigationBar setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"navi_back_icon"]];

? ? //設(shè)置tintColor 改變自定圖片顏色

? ? self.navigationController.navigationBar.tintColor = [UIColor whiteColor];

? ? //設(shè)置自定義的返回按鈕

? ? self.navigationItem.backBarButtonItem = backItem;

}

那么在這種方案下,在特定場景我們不希望用戶輕易返回轻姿,如何做犁珠?

拿捏:

自定義`leftBarButtonItem`或`leftBarButtonItems`傅瞻,并設(shè)置`leftItemsSupplementBackButton = YES`。

- (void)viewDidLoad{


? ? [super viewDidLoad];

? ? //自定義返回按鈕

? ? UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];

? ? [studySearch setImage:[UIImage imageNamed:@"back_icon"] forState:UIControlStateNormal];

? ? [studySearch sizeToFit];

? ? [studySearch addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];

? ? UIBarButtonItem *studySearchItem = [[UIBarButtonItem alloc] initWithCustomView:studySearch];

? ? self.navigationItem.leftBarButtonItems = @[studySearchItem];

? ? //是否支持顯示左滑返回按鈕盲憎,

? ? //NO不顯示:leftBarButtonItems覆蓋backBarButtonItem嗅骄,

? ? //YES顯示:backBarButtonItem 顯示在leftBarButtonItems左側(cè)

? ? //leftItemsSupplementBackButton必須在自定義leftBarButtonItem或leftBarButtonItems后才有效

? ? self.navigationItem.leftItemsSupplementBackButton = YES;

}

以上兩個方案已經(jīng)可以滿足大部分開發(fā)需求,但還有一種情況饼疙,在UIScrollView(UICollectionView)下溺森,返回手勢會失靈。

我們先來看看啥原理:

UIScrollView(包括其子類UITextView窑眯、UITableView屏积、UICollectionView等)的panGestureRecognizer先接收到手勢事件,處理后不再往下傳遞磅甩。即是否讓兩個panGestureRecognizer都起作用的問題炊林,默認(rèn)情況下scrollView的手勢會讓系統(tǒng)的手勢失效。so卷要,顯而易見渣聚,我們需要讓兩個手勢同時啟用。

拿捏:

創(chuàng)建UIScrollView的分類

@implementation UIScrollView (PopGesture)

//此方法返回YES時僧叉,手勢事件會一直往下傳遞奕枝,不論當(dāng)前層次是否對該事件進(jìn)行響應(yīng)。

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{

? ? return YES;

}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{

? ? return YES;

}

@end



二瓶堕、全屏滑動返回

全屏返回這種騷功能隘道,官方從未提供過,可愛的程序員們自己搞出來郎笆,以前的做法(ios7之前)大概就是“手勢+截圖”谭梗,畫風(fēng)是這樣的:

- (void)viewDidLoad{

? ? [super viewDidLoad];

? ? UIImageView *shadowImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"leftside_shadow_bg"]];

? ? shadowImageView.frame = CGRectMake(-10, 0, 10, self.view.frame.size.height);

? ? [self.view addSubview:shadowImageView];


? ? UIPanGestureRecognizer *recognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(paningGestureReceive:)];

? ? [recognizer setDelegate:self];

? ? [recognizer delaysTouchesBegan];

? ? [self.view addGestureRecognizer:recognizer];

}


- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{

? ? if (self.viewControllers.count > 0) {

? ? ? ? [self.screenShotsList addObject:[self capture]]; //截圖,并放入數(shù)組

? ? }

? ? [super pushViewController:viewController animated:animated];

}

- (nullable NSArray<__kindof UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated {

? ? [self.screenShotsList removeAllObjects]; //清空截圖

? ? return [super popToRootViewControllerAnimated:animated];

}

- 感興趣的同學(xué)可以在 這里?看到完整代碼 -


自從ios7支持【邊緣滑動】返回后宛蚓,【全屏返回】的實(shí)現(xiàn)又多了一種思路激捏,江湖人稱:移花接木。同樣有兩個方案苍息。


拿捏:


方案一:

在UINavigationController基類中:


- (void)viewDidLoad{

? ? [super viewDidLoad];

? ? // 獲取系統(tǒng)自帶滑動手勢的target對象

? ? id target = self.interactivePopGestureRecognizer.delegate;

? ? // 創(chuàng)建全屏滑動手勢缩幸,調(diào)用系統(tǒng)自帶滑動手勢的target的action方法

? ? SEL handler = NSSelectorFromString(@"handleNavigationTransition:");

? ? UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:handler];

//設(shè)置手勢代理壹置,攔截手勢觸發(fā)

pan.delegate = self;

//添加全屏滑動手勢

[self.interactivePopGestureRecognizer.view addGestureRecognizer:pan];

// 禁止使用系統(tǒng)自帶的邊緣滑動手勢

self.interactivePopGestureRecognizer.enabled = NO;

? ? //設(shè)置右滑返回手勢的代理為自身

? ? __weak typeof(self) weakself = self;

? ? if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {

? ? ? ? self.interactivePopGestureRecognizer.delegate = (id)weakself;

? ? }

}

注意竞思,這里的 ?pan.delegate = self; ?可能系統(tǒng)會打警告??,因?yàn)闆]有申明和實(shí)現(xiàn)代理 ?UIGestureRecognizerDelegate ?钞护,不實(shí)現(xiàn)也木有關(guān)系的盖喷,不過實(shí)現(xiàn)的話,可以再加一些判斷难咕,出于安全和為了去掉警告课梳,我們來加一下距辆。

拿捏:

@interface UIViewController()<UIGestureRecognizerDelegate>

@end

... ...

- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer{

? ? //控制器棧里只有一個,不響應(yīng)

? ? if (self.navigationController.viewControllers.count <= 1) {

? ? ? ? return NO;

? ? }

? ? // 當(dāng)控制器正在返回的時候暮刃,不響應(yīng)

? ? if ([[self.navigationController valueForKey:@"_isTransitioning"] boolValue]) {

? ? ? ? return NO;

? ? }

? ? //只能響應(yīng) 從左到右的滑動

? ? CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];

? ? if (translation.x <= 0) {

? ? ? ? return NO;

? ? }

? ? return YES;

}

還有一處細(xì)節(jié)跨算,每個UIViewController都會默認(rèn)添加 navigationController.interactivePopGestureRecognizer手勢,而我們再基類又給加了一次椭懊,這不是變成兩個interactivePopGestureRecognizer了嗎诸蚕,既然如此,我們禁用掉一個氧猬!

拿捏:

在UIViewController基類中:

- (void)viewDidLoad{

? ? [super viewDidLoad];

? if(self.navigationController.interactivePopGestureRecognizer.view.gestureRecognizers.count == 2 ){

? ? ? ? for (UIGestureRecognizer *popGesture in self.navigationController.interactivePopGestureRecognizer.view.gestureRecognizers) {

? ? ? ? ? ? popGesture.enabled = NO;

? ? ? ? ? ? break;

? ? ? ? }

? ? }

}


方案二

此方案最方便快捷背犯。

pod 'FDFullscreenPopGesture'

pod 'TZScrollViewPopGesture'

- FDFullscreenPopGesture 為每個UIViewController添加【全屏滑動返回】,但遇到UIScrollView就無效了

- TZScrollViewPopGesture 主要是實(shí)現(xiàn)【邊緣滑動返回】功能盅抚,不過這個是次要漠魏,主要是,它提供了給UIScrollView添加【邊緣滑動返回】的功能妄均。

這倆不會互相影響柱锹,功能互補(bǔ),詳細(xì)原理可以去看看源碼丰包,這兒就不細(xì)寫了奕纫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市烫沙,隨后出現(xiàn)的幾起案子匹层,更是在濱河造成了極大的恐慌,老刑警劉巖锌蓄,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件升筏,死亡現(xiàn)場離奇詭異,居然都是意外死亡瘸爽,警方通過查閱死者的電腦和手機(jī)您访,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剪决,“玉大人灵汪,你說我怎么就攤上這事「塘剩” “怎么了享言?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長渗鬼。 經(jīng)常有香客問我览露,道長,這世上最難降的妖魔是什么譬胎? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任差牛,我火速辦了婚禮命锄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘偏化。我一直安慰自己脐恩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布侦讨。 她就那樣靜靜地躺著被盈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搭伤。 梳的紋絲不亂的頭發(fā)上只怎,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天,我揣著相機(jī)與錄音怜俐,去河邊找鬼身堡。 笑死,一個胖子當(dāng)著我的面吹牛拍鲤,可吹牛的內(nèi)容都是我干的贴谎。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼季稳,長吁一口氣:“原來是場噩夢啊……” “哼擅这!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起景鼠,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤仲翎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后铛漓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體溯香,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年浓恶,在試婚紗的時候發(fā)現(xiàn)自己被綠了玫坛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡包晰,死狀恐怖湿镀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伐憾,我是刑警寧澤勉痴,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站塞耕,受9級特大地震影響蚀腿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扫外,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一莉钙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧筛谚,春花似錦磁玉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吮铭,卻和暖如春时迫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谓晌。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工掠拳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纸肉。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓溺欧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親柏肪。 傳聞我的和親對象是個殘疾皇子姐刁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容