源码分析--KMNavigationBarTransition

最近项目迭代的速度比较慢,准备安心学习一下一些优秀库的源码。记录下来与小伙伴们一同进步。

问题

NavigationBar 是目前市面app几乎都用到的一种控件,很多情况下我们NavigationController栈里面的ViewController对应的NavigationBar样式都是有区别的。然而NavigationBar是由NavigationController统一管理的。
🌰:
A页面 -》 B页面
你在A页面中修改了Bar的样式,随之B页面也会修改。只能A修改了A的样式,B在修改回来。这样咋看是没有问题的,但涉及到右滑返回的时候,就会出现各种问题。

处理

我一开始是在NavigationController的代理中去选择页面对应的样式。但这样,依旧无法避免右滑返回的尴尬。

KMNavigationBarTransition 是新美大的童鞋开源的三方库。我们来分析一下 如何顺滑的处理这方面的动画问题。

先文字说明一下:

隐藏原Bar: 其实不是隐藏Bar 而是隐藏bar上的_backgroundView(我们设置的大部分效果都在这个view上显示)只要我们隐藏了这个view,Bar就变成了一个透明没有效果的Bar。这样相对于完全自定义Bar,可以保留了item切换的动画效果

就是在pop push的时候 隐藏原生的bar(隐藏_backgroundView),在2个VC的view上分别加上替身bar 。

            页面A     --》   页面B
                    push操作
---------------------------------
begin A+替身Bar       原Bar隐藏     B+替身Bar  
---------------------------------
end                  B-替身Bar 原Bar 显示


             页面A     《-   页面B
                    pop操作
---------------------------------
begin                       B+替身Bar  原生Bar隐藏
---------------------------------
end A-替身Bar 原生Bar显示
```

看代码分析
#####push
1 push 开始 这时候页面A 就拥有一个和原本Bar相同的替身Bar
```
- (void)km_pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    // 1 获取页面A 的实例
    UIViewController *disappearingViewController = self.viewControllers.lastObject;
    if (!disappearingViewController) {
        return [self km_pushViewController:viewController animated:animated];
    }
    // 2 判断页面A 上不存在替身Bar
    if (!self.km_transitionContextToViewController || !disappearingViewController.km_transitionNavigationBar) {
        // 3 页面 A.view add 一个和原Bar相同属性的Bar
    [disappearingViewController km_addTransitionNavigationBarIfNeeded];
    }

    if (animated) {
        self.km_transitionContextToViewController = viewController;
        if (disappearingViewController.km_transitionNavigationBar) {
            // 4 隐藏原生Bar
            disappearingViewController.km_prefersNavigationBarBackgroundViewHidden = YES;
        }
    }
    return [self km_pushViewController:viewController animated:animated];
}
```
2 Push 到 页面B 加载过程中 这时候页面B 也拥有了替身Bar
```
- (void)km_viewWillLayoutSubviews {
    id<UIViewControllerTransitionCoordinator> tc = self.transitionCoordinator;
    //1. 获取push 过程中的页面A 和 页面B 实例
    UIViewController *fromViewController = [tc viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toViewController = [tc viewControllerForKey:UITransitionContextToViewControllerKey];
    //2 判断 当前页面加载是否是页面B
    if ([self isEqual:self.navigationController.viewControllers.lastObject] && [toViewController isEqual:self] && self.navigationController.km_transitionContextToViewController) {
        if (self.navigationController.navigationBar.translucent) {
            [tc containerView].backgroundColor = [self.navigationController km_containerViewBackgroundColor];
        }
        fromViewController.view.clipsToBounds = NO;
        toViewController.view.clipsToBounds = NO;
        //3 判断页面B 上是否有替身Bar 没有add 替身Bar
        if (!self.km_transitionNavigationBar) {
            [self km_addTransitionNavigationBarIfNeeded];
            //4 隐藏 原生Bar
            self.km_prefersNavigationBarBackgroundViewHidden = YES;
        }
        [self km_resizeTransitionNavigationBarFrame];
    }
    if (self.km_transitionNavigationBar) {
        [self.view bringSubviewToFront:self.km_transitionNavigationBar];
    }
    [self km_viewWillLayoutSubviews];
}
```
3 push 完成 页面B 完全加载。这时候B 的替身Bar,任务完成。
```
- (void)km_viewDidAppear:(BOOL)animated {
    //1 将 替身Bar 的属性 还给 原生Bar
    if (self.km_transitionNavigationBar) {
        self.navigationController.navigationBar.barTintColor = self.km_transitionNavigationBar.barTintColor;
        [self.navigationController.navigationBar setBackgroundImage:[self.km_transitionNavigationBar backgroundImageForBarMetrics:UIBarMetricsDefault] forBarMetrics:UIBarMetricsDefault];
        [self.navigationController.navigationBar setShadowImage:self.km_transitionNavigationBar.shadowImage];
        
        UIViewController *transitionViewController = self.navigationController.km_transitionContextToViewController;
        if (!transitionViewController || [transitionViewController isEqual:self]) {
            [self.km_transitionNavigationBar removeFromSuperview];
            self.km_transitionNavigationBar = nil;
            self.navigationController.km_transitionContextToViewController = nil;
        }
    }
    //2 显示 原生Bar
    self.km_prefersNavigationBarBackgroundViewHidden = NO;
    [self km_viewDidAppear:animated];
}
```
#####pop
pop 才是关键 因为pop是可以 右滑返回的
1 pop 开始 加上替身Bar 隐藏原生Bar
```
- (UIViewController *)km_popViewControllerAnimated:(BOOL)animated {
    if (self.viewControllers.count < 2) {
        return [self km_popViewControllerAnimated:animated];
    }
    UIViewController *disappearingViewController = self.viewControllers.lastObject;
    //1 页面B 重新加上 替身Bar
    [disappearingViewController km_addTransitionNavigationBarIfNeeded];
    UIViewController *appearingViewController = self.viewControllers[self.viewControllers.count - 2];
    if (appearingViewController.km_transitionNavigationBar) {
        UINavigationBar *appearingNavigationBar = appearingViewController.km_transitionNavigationBar;
        self.navigationBar.barTintColor = appearingNavigationBar.barTintColor;
        [self.navigationBar setBackgroundImage:[appearingNavigationBar backgroundImageForBarMetrics:UIBarMetricsDefault] forBarMetrics:UIBarMetricsDefault];
        self.navigationBar.shadowImage = appearingNavigationBar.shadowImage;
    }
    if (animated) {
        // 原生Bar 隐藏
        disappearingViewController.km_prefersNavigationBarBackgroundViewHidden = YES;
    }
    return [self km_popViewControllerAnimated:animated];
}
``` 
2 因为 push 操作 已经给页面A加上了替身Bar
直接pop结束, 去掉A 的替身Bar,显示原生Bar
```
- (void)km_viewDidAppear:(BOOL)animated {
    //1 将 替身Bar 的属性 还给 原生Bar
    if (self.km_transitionNavigationBar) {
        self.navigationController.navigationBar.barTintColor = self.km_transitionNavigationBar.barTintColor;
        [self.navigationController.navigationBar setBackgroundImage:[self.km_transitionNavigationBar backgroundImageForBarMetrics:UIBarMetricsDefault] forBarMetrics:UIBarMetricsDefault];
        [self.navigationController.navigationBar setShadowImage:self.km_transitionNavigationBar.shadowImage];
        
        UIViewController *transitionViewController = self.navigationController.km_transitionContextToViewController;
        if (!transitionViewController || [transitionViewController isEqual:self]) {
            [self.km_transitionNavigationBar removeFromSuperview];
            self.km_transitionNavigationBar = nil;
            self.navigationController.km_transitionContextToViewController = nil;
        }
    }
    //2 显示 原生Bar
    self.km_prefersNavigationBarBackgroundViewHidden = NO;
    [self km_viewDidAppear:animated];
}
```
效果

![Now1.gif](http://upload-images.jianshu.io/upload_images/1106106-c5fd7b3f93ee5b9b.gif?imageMogr2/auto-orient/strip)

大家如果想看源码,可以去github上 搜索新美大同学的这个三方库。大家一起学习进步~
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • Swift版本点击这里欢迎加入QQ群交流: 594119878最新更新日期:18-09-17 About A cu...
    ylgwhyh阅读 25,281评论 7 249
  • 本文参加#未完待续,就要表白#活动,本人承诺,文章内容为原创,且未在其他平台发表过。 不知不觉中 你们要离开 内心...
    07f089d95f8e阅读 348评论 1 4
  • 是不是人在心情不好的时候,总喜欢单首循环呢!今天晚上被一通电话给搞的有点小不开心,就沿着城市的街道漫无目的的瞎走,...
    伊予yisheng阅读 136评论 0 0
  • 她还是个孩子, 牙还没长,眼还没睁。 她还是个孩子, 发还没长,脸还没脏。 她还是个孩子, 泪还没流,话还没说。 ...
    滩涂阅读 294评论 2 3