原文出处: 霖溦(@羽霖溦 )php
前言html
本文整理了一些关于navigationBar的很是规的可是较为实用的操做,包括利用毛玻璃、动态透明、动态item颜色、动态隐藏,以及头视图的动态缩放,并同时涉及了statusBar的动态设置(换色)。ios
先预览下总体效果:git
Demo详见GitHub:Demo_AboutNavigationBar
为了便于展现,类没有复用,也没有继承共有的父类,全部存在大量“有丝分裂”的重复代码。。。说白了就是懒。。。千万别学我就是了。github
这里所谓的不要浪费,只是我的的偏好,固然也是顺遂了苹果的UI特点之一:毛玻璃穿透效果。web
通常界面上是有两块毛玻璃的:navigationBar和tabBar,不少APP都会自定义这两块Bar,不只是颜色,甚至是控件自己。单考虑利用系统的Bar的话,若是不自定义颜色,就是利用毛玻璃效果自己了,然而不少APP,也并未利用起这个毛玻璃效果,简书的就是这样,不过简书应该是自定义了navigationBar?或许。编程
(这里插一句,就是QQ的某次更新,更新以后,navigationBar的蓝色变成了毛玻璃,以后的一次更新又换了回来……多是被吐槽太多?这里应该是用户习惯先入为主的问题了;还有就是微信,微信应该算得上是个十分纯粹的iOS风格的APP,有留意过的话,也会发现其navigationBar是黑色的毛玻璃,或许是barStyle = UIBarStyleBlack
,由于微信也是有不少非原生的处理的。)浏览器
下面结合tableView介绍下其与navigationBar的毛玻璃的做用效果。知道我可能要啰嗦什么的朋友可自行跳过这部份内容。微信
先对比下这两种效果(图不太清晰,仔细看navigationBar,仍是很容易对比出来差异的):架构
有毛玻璃穿透效果的效果
没有毛玻璃穿透效果
1 2 3 4 5 6 7 8 9 10 11 |
- (UITableView *)tableView { if (!_tableView) { _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, ScreenWidth, ScreenHeight - 64)]; _tableView.delegate = self; _tableView.dataSource = self; _tableView.backgroundColor = [UIColor whiteColor]; _tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; } return _tableView; } |
相信有不少人大概是以与这样相似的方式去初始化tableView的,origin.y=64,总高度-64,都是为了防止navigationBar的遮挡,然而这样的处理会产生一个bug,就是tableView总体竟然下移了64……,而后就有了这段解决代码:
1 2 3 |
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) { self.automaticallyAdjustsScrollViewInsets = NO; } |
这是iOS7的特性之一,navigationBar自适应scrollView滑动视图,然而由于咱们的+64的处理,致使系统的这个默认开启的功能就成了自做多情。若是
1 |
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight)]; |
这样作,以前的“bug”天然就会没有,并且咱们想要的毛玻璃效果就会天然的展示出来(如上图一),可是由于系统的自适应,在某些特殊状况下,依旧会产生不可控的bug,因此刚才处理bug的代码,仍是有必要的。
配合:
1 2 3 |
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) { self.automaticallyAdjustsScrollViewInsets = NO; } |
这样初始化tableView:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- (UITableView *)tableView { if (!_tableView) { _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight)]; _tableView.delegate = self; _tableView.dataSource = self; _tableView.backgroundColor = [UIColor whiteColor]; _tableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0); _tableView.scrollIndicatorInsets = UIEdgeInsetsMake(64, 0, 0, 0); _tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; } return _tableView; } |
这里利用了edgeInset
内嵌边距,tableView的frame依旧是全屏尺寸的,只是设置了下contentInset
使得其内容视图的尺寸改变了,scrollIndicatorInsets
是右侧的滑动指示器,要跟着一块儿才天然。若是当前页还有tabBar的话,只须要设置UIEdgeInsetsMake
的bottom
值就行。这样,上图一的毛玻璃穿透效果就天然的释放了出来。
contentInset
带来的小问题1.webView初始加载时底部的黑条
若是使用上述方式初始化webView(一样是滑动视图,有scrollView属性,可设置scrollView.contentInset
),至于所谓的“黑条”,大体相似于下面的效果:
黑条出如今视图底部,通常高度为64,也就是contentInset
的top
值,上图的由于设置关系要大不少。相信不少人都在浏览APP的webView时,在web加载完以前的空白页时,见到过这一现象。
关于这以现象的解决方法网上也有不少,我通常是在初始化时,将contentOffset.y
设置一个(-64)的偏移量,这样,初始进入webView时,在不滑动页面的状况下,是不可见的,这算是较为温和的解决方法吧,还有一种就是,初始化时设置opaque=NO
,也就是使得view透明,可是在加载以后,要设置opaque=YES
,也就是默认不透明,这样,黑条是完全不可见的。
2.滑动偏移量改变
在实现一些滑动视图的动态变化的处理中,例如稍后提到的动态缩放,通常会用代理检测contentOffset.y
值,可是由于设置了contentInset.top
,此时contentOffset.y
值也就发生了变化:
1 2 3 4 |
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat offsetY = scrollView.contentOffset.y + self.tableView.contentInset.top;//注意 } |
初始再也不是0,而是-64,也就是-top
。
费了一些功夫,都只是为了实现毛玻璃效果,至于为何要这个毛玻璃穿透效果,我也说很差,喜爱也罢,设计风格也罢,至少,感受这样作出的APP,不用专门考虑各类Bar的配色什么的,动态模糊穿透的彩色,也的确很漂亮,至少,iOS的UI风格有这样的趋势。也不要小瞧这个毛玻璃效果,真正实现一个高效的毛玻璃,也就是动态模糊渲染,也是很麻烦的,一些三方库,为了版本适配(适配iOS7),自定义的毛玻璃,也是利用了系统的toolBar。看过一些文章,介绍来介绍去,仍是说,用系统在iOS8以后提供的API实现毛玻璃是最好的方式。
下图(渣图……)是我最近封装了有一阵子的一个自定义控件,一样是为了效果,用toolBar自定义了一块毛玻璃,这个控件还未完全完善,稍后会开源,下图只是其中一种样式,这个控件的自定义程度仍是很是高的。
有时候,咱们须要将navigationBar设置透明,但不是隐藏,由于还须要其item控件(返回键什么的),虽然navigationBar是继承于UIView的,可是直接设置其alpha
是无效的,应该是由于navigationBar复合的视图层级:
根据视图层级关系,咱们用这个十分简单的方法来设置navigationBar的透明:
1 |
[[[self.navigationController.navigationBar subviews] objectAtIndex:0] setAlpha:0]; |
关于navigationBar设置透明的方法,不知上述一种,还有许多,详见此文,很是具备参考价值:
若是想像QQ空间或者微博那样动态的改变透明度,只须要在scrollViewDidScroll
方法中,动态去设置alpha值就行,什么时候开始改变、变化率全凭自定义的参数控制,具体详见Demo中的代码。
改变item的颜色很简单,只需设置:
1 |
self.navigationController.navigationBar.tintColor = [UIColor blackColor]; |
动态设置的方式同刚才的alpha。这里主要说的是statusBar的颜色或者说样式的改变。
iOS7以后,statusBar再也不是全局属性,每一个VC均可自行控制statusBar的样式,虽然样式只有简单的字体黑或白两种,可是在不少状况下都是颇有用的,尤为是上面的navigationBar的alpha动态改变,在QQ空间中就有这个效果。
在设置statusBar样式以前,须要作一个处理,并且是针对navigationBar的处理,在使用了navigationController以后,直接设置某一个VC的statusBar的样式是无效的,由于navigationBar是惟一的,全部压栈推出的VC,都是navigationController的子控制器,这就须要指定statusBar样式改变的VC为当前的topVC,具体方式网上也有不少,这里只介绍我的使用的一种。
首先建立一个继承于UINavigationController的子类,在这个类中实现下面的方法:
1 2 3 4 |
- (UIViewController *)childViewControllerForStatusBarStyle { return self.topViewController; } |
或者是一样效果的这个方法:
1 2 3 4 5 |
- (UIStatusBarStyle)preferredStatusBarStyle { UIViewController * topVC = self.topViewController; return [topVC preferredStatusBarStyle]; } |
以后,只需在要改变statusBar样式的VC类中实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#ifdef __IPHONE_7_0 - (UIStatusBarStyle)preferredStatusBarStyle { if (_statusBarStyleControl) { return UIStatusBarStyleDefault; } else { return UIStatusBarStyleLightContent; } } - (BOOL)prefersStatusBarHidden { return NO; } #endif |
__IPHONE_7_0
是系统的宏,这里用来版本适配,这个不写貌似没有关系?由于以前试了iOS7如下的系统没有崩溃,iOS7以前没有这个方法,应该是不会执行的,也就不会崩溃。
preferredStatusBarStyle
就是控制用来控制statusBar颜色或者说样式的,_statusBarStyleControl
是自定义的一个用来动态控制的BOOL属性。
prefersStatusBarHidden
这个控制statusBar的显示隐藏,建议NO或直接默认不写,若是设置隐藏,视图会总体上移20,效果不太好,看具体需求。
至于控制statusBar的改变,也是在scrollViewDidScroll
代理中动态实现,例如某一状况下触发以下:
1 2 3 4 |
_statusBarStyleControl = YES; if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) { [self setNeedsStatusBarAppearanceUpdate]; } |
第一步是设置以前提到的_statusBarStyleControl
标志位的值的切换,第二部是最重要的setNeedsStatusBarAppearanceUpdate
,这个系统的方法是在改变statusBar显示样式以前必须执行的,不然preferredStatusBarStyle
不会再当前视图加载完成后再次执行。
navigationBar的隐藏很简单:
1 |
[self.navigationController setNavigationBarHidden:YES animated:YES]; |
这个方法可使动态隐藏时有动画效果,不会显得突兀。
动态隐藏的效果有两个场景:一个就是例如简书这样的,在浏览时,上滑,navigationBar隐藏,下滑navigationBar显示,在这期间,手指是不松开的,这须要实时检测当前是上滑仍是下滑;第二个场景是Safari浏览器那样的,滑动后松手,根据上滑仍是下滑设置隐藏(Safari的navigationBar不是隐藏,只是变化)。这样的两种场景虽然很类似,但就是松不松手的问题,处理方式和体验也是彻底不一样的。
1.第二场景,松手
这个处理十分简单:
1 2 3 4 5 6 7 8 9 |
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{ NSLog(@"======== %lf", velocity.y); if(velocity.y > 0) { [self.navigationController setNavigationBarHidden:YES animated:YES]; } else { [self.navigationController setNavigationBarHidden:NO animated:YES]; } } |
velocity.y这个量,在上滑和下滑时,变化极小(小数),可是由于方向不一样,有正负之分,这就很好处理了。
2.第一场景,不松手
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat offsetY = scrollView.contentOffset.y + _tableView.contentInset.top;//注意 CGFloat panTranslationY = [scrollView.panGestureRecognizer translationInView:self.tableView].y; if (offsetY > 64) { if (panTranslationY > 0) { //下滑趋势,显示 [self.navigationController setNavigationBarHidden:NO animated:YES]; } else { //上滑趋势,隐藏 [self.navigationController setNavigationBarHidden:YES animated:YES]; } } else { [self.navigationController setNavigationBarHidden:NO animated:YES]; } } |
这里的offsetY > 64
只是为了在视图滑过navigationBar的高度以后才开始处理,防止影响展现效果。
panTranslationY
是scrollView的pan手势的手指位置的y值,这个方法是我的本身想到的,可能不是太好,由于panTranslationY
这个值在较小幅度上下滑动时,可能都为正或都为负,这就使得这一方式不太灵敏,若是有更好的方法,欢迎留言交流。
这个效果纯粹是为了仿简书的我的信息的头像的缩放效果。
头像视图的布局直接利用了navigationBar的titleView,代码仍是很好理解的,视图层级关系以下:
头像的一半是“悬空”的。
缩放只是滑动代理监听时的一个动态缩放的过程,缩放率同以前说的alpha的动态变化同样,自行设置常量,这里注意缩放过程的分段执行,为了效果,这个有下拉放大回弹的效果,这部分不是简书的效果:
1 |
_topImageView.transform = CGAffineTransformMakeScale(1 - offsetY/300, 1 - offsetY/300); |
若是只是设置缩放,效果以下:
为了使缩放过程当中位置相对保持,以前想到了改变锚点的方式,然而效果不好,最终仍是以简单的方式实现:
1 2 3 |
CGRect frame = _topImageView.frame; frame.origin.y = 5; _topImageView.frame = frame; |
就是保持y的坐标为初始值。
下面的这个仿微博的效果,也是基于一样的原理,只是要注意图层关系,详细参考代码,再也不赘述。
这算是个比较大的话题了,前面提到的那篇文章中有关于此的详细论述,这里只是简单说起,稍后有机会,会专门写一篇关于侧滑返回的自定义实现。
iOS7以后,navigationController推出的视图,只要返回按钮不自定义覆盖,或者相关属性默认,右滑屏幕左边缘,能够直接pop返回,这是个十分方便的功能。关于实现和自定义实现,网上也有太多的文章。这里只是说明一点,就是在设置navigationBar透明以后,使用侧滑返回的过程当中,navigationBar会忽然出现,显得十分突兀,这也是目前微博APP的效果,QQ和微信都针对此作了不一样的处理,本文的Demo中也尝试作了必定的处理,可是效果并非十分理想的,就是中断侧滑返回动做时,navigationBar闪的问题。这个问题已经超出本文内容的范围,有机会会在从此的文章中介绍。
参考文章:
1.iOS开发的一些奇巧淫技
2.自定义iOS7导航栏背景,标题和返回按钮文字颜色
3.Removing the title text of an iOS 7 UIBarButtonItem
4.iOS 实现ScrollView 上滑隐藏Navigationbar,下滑显示
5.iOS 7 改变 app 的外观(NavigationBar,TabBar,StatusBar)
6.iOS不一样版本适配问题(#ifdef __IPHONE_7_0)
7. __IPHONE_OS_VERSION_MIN_REQUIRED
8.IOS开发之不一样版本适配问题3(#ifdef __IPHONE_7_0 BaseSDK Development Target)
9.IOS开发之不一样版本适配问题2(#ifdef __IPHONE_7_0)
10.ios7 statusBar的字体颜色怎么设置为白色的呢
11.IOS上 关于状态栏的相关设置(UIStatusBar)
12.IOS7怎么修改Navigation Bar上的返回按钮文本颜色,箭头颜色以及导航栏按钮的颜色
13.怎么把顶部这个navigationbar设置为透明呢,可以让下面的图片显示出来,可是返回按钮不透明?
14.iOS navigationbar全透明的方法
15.IOS中设置UINavigationBar的各类样式(图片/透明效果/下方内容显示状况)
16.iOS开发UI篇—CAlayer层的属性
17.iOS – UINavigationController简介
问啊-定制化IT教育平台,牛人一对一服务,有问必答,开发编程社交头条 官方网站:www.wenaaa.com 下载问啊APP,参与官方悬赏,赚百元现金。
QQ群290551701 汇集不少互联网精英,技术总监,架构师,项目经理!开源技术研究,欢迎业内人士,大牛及新手有志于从事IT行业人员进入!