迷之“导航栏”

对于iOS 开发者来说,导航栏确实是一个让人困惑的知识点。比如设置导航栏透明效果,透明导航栏与非透明导航栏之间的跳转等等,我开始也是在项目预定的框架 下去设置导航栏的一些属性,直到我负责的模块在IOS10中出现了导航栏的bug的时候才不得不 去好好消化这一块,去理解系统在导航切换时的一些特效,去调研市面上常用APP对于导航栏 的一些处理,这里我就来分解一下导航栏的奥秘。

导航栏基础

iOS 导航栏大致有两部分组成:navigationBar和navigationItem:

  • navigationBar
    navigationBar是navigationController的一部分,它不属于单个的UIViewController。我们在同一个navigationController中push或者pop的时候,看到的bar其实都是一个,也就是说,在一个viewController中修改了bar的属性之后在其他的控制器中也是可见的。

在考虑设置导航栏的时候,我们通常也是一起设置了App的状态栏(也就是电池条),IOS有两个常用的 状态栏(statusBarStyle)属性,分别是:

在考虑设置导航栏的时候,我们通常也是一起设置了App的状态栏(也就是电池条),IOS有两个常用的 状态栏(statusBarStyle)属性,分别是:

// Dark content, for use on light backgrounds
UIStatusBarStyleDefault         = 0,
// Light content, for use on dark backgrounds
UIStatusBarStyleLightContent     NS_ENUM_AVAILABLE_IOS(7_0) = 1,

导航栏始终处于状态栏的下方,我们可以看到几乎所有的App导航栏和状态栏的颜色 都相同,这样的效果无法通过设置导航栏的backgroundColor得到,因为导航栏y坐标为20, 状态栏,设置了导航栏的背景色不会影响到状态栏。但是神奇的是,导航栏上面还有一层 View是navigationBarBackground(设置barTintColor将改变此背景色),其y坐标为-20,刚好把状态栏覆盖,所以我们使用setBackgroundImage就可以保证导航栏与状态栏同色,如下:

UIImage *colorImage = [UIImage imageWithColor:[UIColor clearColor] size:CGSizeMake(1, 1)];
[navc.navigationBar setBackgroundImage:colorImage forBarMetrics:UIBarMetricsDefault];
[navc.navigationBar setShadowImage:colorImage];

navigationItem

我们强调了navigationBar是属于navigationController的一部分,那么navigationItem 却是属于UIViewCOntroller的一部分,这一点在刚开始很容易造成困惑,这些个Item明明 就在Bar上,为什么偏偏属于ViewController,但是事实就是这样。navigationItem由三部分 组成,分别是:
titleView
这又是一个让我们疑惑的属性,按照常理我们认为这是一个UILabel,通过设置navigationItem. titleView就是可以搞定title的一切属性,但是事实是我们想错了,这个属性默认为空。所以 当我们要单独考虑一个viewController的title的时候,就需要为其设置一个UILabel作为 titleView了,如下所示:

- (void)setTitle:(NSString *)title titleColor:(UIColor *)color{
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 44)];
titleLabel.text     = title;
titleLabel.font     = [UIFont boldSystemFontOfSize:20.f];
titleLabel.textAlignment  = NSTextAlignmentCenter;
titleLabel.textColor    = color;
self.navigationItem.titleView = titleLabel;
}

leftNavigationItem

同上,其默认也为空(虽然系统会默认生成“返回”,但这个属性值依然为空)。所以我们一般 会设置可控的leftNavigationItem,如下:

- (void)setNavBarCustomBackButton:(NSString *)title target:(id)target action:(SEL)action {
UIImage *image = [UIImage imageSVGNamed:@"icon_arrow_back_white" size:CGSizeMake(20, 20) cache:YES];

UIButton *buttonItem = [UIButton buttonWithType:UIButtonTypeSystem];
buttonItem.tag = 1002;
buttonItem.titleLabel.font = [UIFont systemFontOfSize:16.0];
buttonItem.imageEdgeInsets = UIEdgeInsetsMake(0, -16, 0, 0);
buttonItem.titleEdgeInsets = UIEdgeInsetsMake(0, -19, 0, 0);
[buttonItem setImage:image forState:UIControlStateNormal];
[buttonItem setTitle:title forState:UIControlStateNormal];
[buttonItem setTitleColor:RGB(45, 45, 45) forState:UIControlStateNormal];
[buttonItem addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[buttonItem sizeToFit];
buttonItem.frame = CGRectMake(0, 0, buttonItem.frame.size.width, 40);

self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:buttonItem];
}

rightNavigationItem

其设置与leftNavigationItem一样,这里不啰嗦了。
对于navigationItem的三个属性来说,其贯穿与整个App的开发过程中,我们为了代码的整洁性, 一般不会在viewController中直接设置,而已通过category来使用。

导航栏切换

iOS10中navigationController的push和pop,前后两个viewController如果导航栏颜色(或者透明度)不一致, 就会出现导航栏的切换动画,处理这种情况时应该特别小心,如果处理不当就会造成bug或者 App体验上的损失。
系统默认在导航栏切换的时候会有动画和毛玻璃效果,其实这个动画就是前后Controller的 导航栏颜色的渐变动画,而且title也会随着动画产生“隐去”和“隐现”的效果,这种效果在 一般情况下没有问题,而且看起来还很不错,但是下列两种情况下你应该单独考虑。

一侧透明一侧不透明

这种情况下就会产生奇怪的效果,一侧根本看不见导航栏,而系统给我们的是一个渐变的动画, 然而这个动画仿佛在iOS10上有bug,会发生跳转闪烁的情况(在viewWillAppear中设置导航栏 背景Image的效果会推迟显示)。如果我们可以接受这个动画,但是绝不能接受这个闪烁发生, 可以尝试着修复这个bug;但是一些情况下我们并不期望这个动画产生,做法很简单把透明一侧 的导航栏直接隐藏掉,这样前后viewController的导航栏渐进动画就会消失,很多blog上面 说的就是这种方法。
如果在透明一侧的导航栏的透明度是随着scrollView的contentOffset来变化的,那么隐藏导航栏这种 解决办法就会失效,因为我们不能鲁莽地去隐藏掉一个透明度不为0的导航栏。这种情况下是 逼着我们去实现自定义的转场动画,或者修复ios10上的bug,这两种解决办法都不会太简单, 我会接下来讲解。

两侧主题相差巨大

如果两侧导航栏的主题风格相差巨大,也就是说前后两个viewController根本不像是同一个 navigationController中的子congtoller,那么系统中的渐变动画就会显得很不协调,想想 在渐变动画中出现一个其它不相干的颜色是多么的抓狂。这里的解决办法也有两种,一种原理很简单 ,就是在一侧放弃使用系统的导航栏,而伪造一个假的导航栏,这样就不会产生渐变动画。另一种 方法还是自己实现转场动画,又回到了这个问题。
市面上一些大的App,在处理上面两种情况的时候都放弃了系统的渐变动画,而是采用一种泾渭分明的 转场方式,比如说今日头条、支付宝等,具体是使用伪导航栏还是自己实现的转场动画就不得而知了。 特别说明:以上情况可能不适用于ios10之前,小心调试吧。

Appearance主题

上面已经讨论过,为navigationBar上面的item设置属性往往会造成困扰,我们有时候无法直接 改变这些item的属性,所以需要设置自定义item。Appearance主题是为了设置整个App的默认属性, 特别是对于UINavigation有用,能够设置App导航栏的色彩基调。如下:

[[UINavigationBar appearance] setTintColor:RGB(0x51, 0x4e, 0x4e)];
[[UINavigationBar appearance] setBarTintColor:RGB(0, 191, 143)];
[[UINavigationBar appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIFont systemFontOfSize:15], NSFontAttributeName, [UIColor whiteColor], NSForegroundColorAttributeName, nil]];
[[UIBarButtonItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
                                                              [UIFont systemFontOfSize:15.0], NSFontAttributeName,
                                                              RGB(0x51, 0x4e, 0x4e), NSForegroundColorAttributeName,
                                                              nil]
                                                    forState:UIControlStateNormal];
[[UINavigationBar appearance] setBackIndicatorImage:[UIImage imageSVGNamed:@"icon_arrow_back_white" size:CGSizeMake(20, 20) cache:YES]];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:[UIImage imageSVGNamed:@"icon_arrow_back_white" size:CGSizeMake(20, 20) cache:YES]];

上述代码最好在App启动的时候就设置好,作为导航栏的默认设置,而且在项目的进行过程中不要轻易地再去更改。 如果确实有需求更改navigationBar上的属性,记得在viewWillAppear或者viewWillDisappear中修改 回去,或者直接使用自定义的item。

总结

这里我不打算去讲解IOS的转场动画了,因为这个题目还是很大的,我怕说不清楚;再者除非对细节有着很高的要求,一般APP也不会去做 导航栏的转场动画。针对,IOS10中出现的导航栏闪烁问题,我们组的大牛的解决办法是,放弃NavigationBar上面的一层backgroundView, 统一设置成clearColor,然后在navigationBar上自己加一层layer,来模拟渐变动画,这样就通过扩展NavigationBar,避开了系统导航栏 的一些默认处理效果。

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

推荐阅读更多精彩内容

  • 对于初学者来说,IOS中的导航栏确实是一个让人困惑的知识点,我开始也是在项目预定的框架下去设置导航栏的一些属性,直...
    myzhing阅读 382评论 0 1
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,945评论 4 60
  • { 11、核心动画 需要签协议,但是系统帮签好 一、CABasicAnimation 1、创建基础动画对象 CAB...
    CYC666阅读 1,519评论 2 4
  • 妈妈: 您还好吗? 转眼间您已经离开我一百多天了,我非常想念您,您可知道? 您离世后我总梦...
    杨静相伴要你好看阅读 359评论 4 2
  • 1、 去太太家吃饭,开空调很喛和 2、回来时跟老公闹别扭了,生气中…我在想我是否真的所有的事都依着他来? 3、去超...
    宁静的流星阅读 58评论 0 0