隱藏NavigationBar 帶來的坑(iOS)

一、場景介紹

現(xiàn)在大多數(shù)APP 都有一個需求,就是隱藏某一個頁面的NavigationBar。很多開發(fā)者直接 [self.navigationController setNavigationBarHidden:YES] 就萬事大吉了。但是如果開發(fā)者試著將邊緣側滑返回功能加上之后,細心的同學就會發(fā)現(xiàn),如果我們在一個隱藏NavigationBar的頁面和一個顯示NavigationBar 的頁面通過手勢來回切換后,再繼續(xù)push到更深層的頁面,頂部的NavigationBar就會出現(xiàn)錯亂的情況。因此好多APP 將側滑返回直接去掉了,但是這樣的話,整個APP 就會顯得比較粗糙,體驗不佳。其實早已iOS自帶了側滑返回功能,只不過大多數(shù)時候由于我們自定義了NavigationBar的leftBarButtonItem,導致了轉場交互代理interactivePopGestureRecognizer失效了,需要我們重新指定一下代理,在此不再贅述。

二、問題解決

為了更好的描述問題,我特地定義了兩個控制器:1、ViewController1(隱藏了導航欄,導航控制器的根控制器) ;2、ViewController2(需要顯示導航控制器,由ViewController1 push 而來)。 那么怎么解決“側滑返回功能”與“隱藏NavigationBar”共存呢?我的思路是:既然再同一個導航控制器棧內會出問題,那么我將ViewController1和ViewController2放在兩個導航控制器里不就可以了嗎?但是我們還想需要push的效果怎么辦?導航控制器是不允許在push一個新的導航控制器的。只能present出來一個新的導航控制器。因此我想到了自定義一個轉場動畫,讓present出來的效果如同push的效果一樣。

1)設置側滑返回

#import <UIKit/UIKit.h>
 
@protocol ModalViewControllerDelegate <NSObject>
 
-(void) modalViewControllerDidClickedDismissButton:(UIViewController *)viewController;
 
@end
 
@interface BaseNavigationController : UINavigationController<UINavigationControllerDelegate,UIGestureRecognizerDelegate>
//@property(nonatomic,weak)UIPanGestureRecognizer *popPan;
 
//轉場代理
@property (nonatomic, weak) id<ModalViewControllerDelegate> transDelegate;
 
@end
#import "BaseNavigationController.h"

@interface BaseNavigationController ()
 
@property(nonatomic,weak) UIViewController* currentShowVC;
 
@end
 
@implementation BaseNavigationController
 
 
- (void)viewDidLoad {
    [super viewDidLoad];
    //請忽略這段設置UI樣式的代碼,沒什么卵用
    [self.navigationBar setBarTintColor:[Theme colorOne]];
    [self.navigationBar setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
                                                [UIColor whiteColor],NSForegroundColorAttributeName, nil]];
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent animated:YES];
    [self.navigationBar setTranslucent:NO];
    <br>
    self.view.layer.shadowColor = [UIColor blackColor].CGColor;
    self.view.layer.shadowOffset = CGSizeMake(-2, 0);
    self.view.layer.shadowOpacity = 0.3;
}
//導航控制器一被創(chuàng)建則設置交互手勢代理
-(id)initWithRootViewController:(UIViewController *)rootViewController
{
    BaseNavigationController* nvc = [super initWithRootViewController:rootViewController];
    self.interactivePopGestureRecognizer.delegate = self;
    nvc.delegate = self;
    return nvc;
}
 
//這段主要是判斷為了幫助手勢判斷當前響應手勢的控制器是不是根視圖控制器
-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if (navigationController.viewControllers.count == 1)
        self.currentShowVC = Nil;
    else if(animated){
        self.currentShowVC = viewController;
    }else{
        self.currentShowVC = Nil;
    }
}
//交互手勢代理(告訴手勢什么時候需要響應,什么時候不響應)
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer == self.interactivePopGestureRecognizer) {
        return (self.currentShowVC == self.topViewController);
    }
    return YES;
}

設置側滑返回我們需要在base類里重新設置代理,并實現(xiàn)轉場手勢的代理,告訴手勢當頁面為根視圖控制器時候手勢不響應,其實以上代碼就是實現(xiàn)這么點的功能,僅此而已。

2)設置轉場動畫

轉場動畫我是直接修改了大神的代碼 。以下是我修改后的代碼,只是修改了動畫部分,使用方法是一致的。

#import "BouncePresentAnimation.h"

@implementation BouncePresentAnimation
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
   return 0.25f;
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
   // 1. Get controllers from transition context
   UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
   // 2. Set init frame for toVC
   CGRect screenBounds = [[UIScreen mainScreen] bounds];
   CGRect finalFrame = [transitionContext finalFrameForViewController:toVC];
   toVC.view.frame = CGRectOffset(finalFrame, screenBounds.size.width, 0);
    
   // 3. Add toVC's view to containerView
   UIView *containerView = [transitionContext containerView];
   [containerView addSubview:toVC.view];
    
   // 4. Do animate now
   NSTimeInterval duration = [self transitionDuration:transitionContext];
   [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
       toVC.view.frame = finalFrame;
   } completion:^(BOOL finished) {
       [transitionContext completeTransition:YES];
   }];
}




#import "NormalDismissAnimation.h"

@implementation NormalDismissAnimation
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
   return 0.25f;
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
   // 1. Get controllers from transition context
   UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
   UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
   // 2. Set init frame for fromVC
   CGRect screenBounds = [[UIScreen mainScreen] bounds];
   CGRect initFrame = [transitionContext initialFrameForViewController:fromVC];
   CGRect finalFrame = CGRectOffset(initFrame, screenBounds.size.width,0 );
    
   // 3. Add target view to the container, and move it to back.
   UIView *containerView = [transitionContext containerView];
   [containerView addSubview:toVC.view];
   [containerView sendSubviewToBack:toVC.view];

   // 4. Do animate now
   NSTimeInterval duration = [self transitionDuration:transitionContext];
    
   [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
       fromVC.view.frame = finalFrame;
   } completion:^(BOOL finished) {
       [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
   }];
}

3)又一個坑

大家將轉場動畫和側滑功能加入后會發(fā)現(xiàn),當我在ViewController2中再繼續(xù)push時,如果側滑則直接返回到了我們的ViewController1控制器,而不是我們想要的次棧頂控制器。這涉及到了手勢之間競爭的問題,也就是說轉場的手勢取代我們邊緣側滑手勢去響應了,因此我們要告訴系統(tǒng),如果當前手勢所在的控制器不是present出來的導航控制器的根控制器的話,轉場手勢就不需要響應。因此需要修改大神的一部分代碼

#import "SwipeUpInteractiveTransition.h"
 
@interface SwipeUpInteractiveTransition()<UIGestureRecognizerDelegate>
@property (nonatomic, assign) BOOL shouldComplete;
@property (nonatomic, strong) UIViewController *presentingVC;
@end
 
@implementation SwipeUpInteractiveTransition
-(void)wireToViewController:(UIViewController *)viewController
{
    self.presentingVC = viewController;
    [self prepareGestureRecognizerInView:viewController.view];
}
 
- (void)prepareGestureRecognizerInView:(UIView*)view {
    UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
    gesture.delegate = self;
    [view addGestureRecognizer:gesture];
}
 
-(CGFloat)completionSpeed
{
    return 1 - self.percentComplete;
}
 
- (void)handleGesture:(UIPanGestureRecognizer *)gestureRecognizer {
    CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view.superview];
       switch (gestureRecognizer.state) {
        case UIGestureRecognizerStateBegan:
            // 1. Mark the interacting flag. Used when supplying it in delegate.
            self.interacting = YES;
            [self.presentingVC dismissViewControllerAnimated:YES completion:nil];
            break;
        case UIGestureRecognizerStateChanged: {
            // 2. Calculate the percentage of guesture
            CGSize screenSize = [UIScreen mainScreen].bounds.size;
            CGFloat fraction = translation.x / screenSize.width;
            //Limit it between 0 and 1
            fraction = fminf(fmaxf(fraction, 0.0), 1.0);
            self.shouldComplete = (fraction > 0.5);
 
            [self updateInteractiveTransition:fraction];
            break;
        }
        case UIGestureRecognizerStateEnded:
        case UIGestureRecognizerStateCancelled: {
            // 3. Gesture over. Check if the transition should happen or not
            self.interacting = NO;
            if (!self.shouldComplete || gestureRecognizer.state == UIGestureRecognizerStateCancelled) {
                [self cancelInteractiveTransition];
            } else {
                [self finishInteractiveTransition];
            }
            break;
        }
        default:
            break;
    }
}
 
//如果不是根視圖控制器則不響應
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    if ([self.presentingVC isKindOfClass:[BaseNavigationController class]]) {
        BaseNavigationController *nav = (BaseNavigationController*)self.presentingVC;
        if (nav.viewControllers.count >=2) {
            return NO;
        }
    }
    return YES;
}

至此側滑和導航欄的隱藏則完美兼容,希望這篇文章對你有用~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容

  • 1. conform an appointment確認預約 2. Details of your incompet...
    Maei閱讀 918評論 0 1
  • 一下校車就跟我說昨天的考試成績出來了,她總分198!我一猜就是語文98數(shù)學100分!她說:媽媽你怎么知道呢?我說猜...
    倆千金的媽閱讀 174評論 0 0