07 APRIL 2015onios,objc,uinavigationbar
这是我们最终想要得到的效果:
思路
在UISrollView的delegate方法- (void)scrollViewDidScroll:(UIScrollView *)scrollView中根据当前的contentOffset更新navigationBar的backgroundColor即可,so easy~
开动
那么我们来看看apple为我们提供了哪些API来设置navigationBar的颜色。
首先想到的是最常用的[UINavigationBar appearance],我们一般会在AppDelegate中使用它对navigationBar进行统一的设置。但是如果试一下,会发现在scrollViewDidScrollView中调用它并不能动态地改变navigationBar的颜色,原因可以看一下Apple的doc:
Use the UIAppearance protocol to get the appearance proxy for a class. You can customize the appearance of instances of a class by sending appearance modification messages to the class’s appearance proxy.
但是:
iOS applies appearance changes when a view enters a window, it doesn’t change the appearance of a view that’s already in a window. To change the appearance of a view that’s currently in a window, remove the view from the view hierarchy and then put it back.
所以换一条路,直接修改UINavigationBar的backgroudColor:
- (void)scrollViewDidScroll:(UIScrollView*)scrollView{UIColor*color = [UIColorblueColor];CGFloatoffsetY = scrollView.contentOffset.y;if(offsetY >0) {CGFloatalpha =1- ((64- offsetY) /64);self.navigationController.navigationBar.backgroundColor= [color colorWithAlphaComponent:alpha]; }else{self.navigationController.navigationBar.backgroundColor= [color colorWithAlphaComponent:0]; }}
结果却是。。。
仔细观察,会发现navigationBar的高度是44,它的上方是statusBar,而且,navigationBar的上面还有一个未知的View。。。到底Apple是怎么实现UINavigationBar的呢,让我们一探究竟!
在xcode的顶部菜单栏找到Debug > View Debugging > Capture View Hierarchy:
原来UINavigationBar上有一个_UIBackDropView,正是它决定了navigationBar的背景色。
那么我们是不是可以修改它的颜色呢,赶紧打开UINavigationBar.h,找了一圈,💔
既然没有public的API,我们只能hack了!
Hack
我们的思路很简单,参照Apple的实现,在navigationBar的view hierarchy中插入一个view,通过它来控制在navigationBar的backgroundColor。
考虑到继承UINavigationBar使用起来会非常不便,我们决定用Category来实现,首先定义我们的category:
@interfaceUINavigationBar(BackgroundColor)- (void)lt_setBackgroundColor:(UIColor*)backgroundColor;@end
实现:我们使用associatedObject将overlayView动态地绑定到UINavigationBar的instance上,当调用lt_setBackgroundColor的时候,我们只要更新这个overlayView就行啦~
@implementationUINavigationBar(BackgroundColor)staticcharoverlayKey;- (UIView*)overlay{returnobjc_getAssociatedObject(self, &overlayKey);}- (void)setOverlay:(UIView*)overlay{ objc_setAssociatedObject(self, &overlayKey, overlay, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- (void)lt_setBackgroundColor:(UIColor*)backgroundColor{if(!self.overlay) { [selfsetBackgroundImage:[UIImagenew] forBarMetrics:UIBarMetricsDefault]; [selfsetShadowImage:[UIImagenew]];// insert an overlay into the view hierarchyself.overlay= [[UIViewalloc] initWithFrame:CGRectMake(0, -20, [UIScreen mainScreen].bounds.size.width,64)]; [selfinsertSubview:self.overlayatIndex:0]; }self.overlay.backgroundColor= backgroundColor;}@end
最后在scrollViewDidScroll中,我们就可以动态地修改UINavigationBar的backgroundColor了:
[self.navigationController.navigationBar lt_setBackgroundColor:[color colorWithAlphaComponent:alpha]];
完整的代码在这里:https://github.com/ltebean/LTNavigationBar
写在最后
UINavigationBar是一个比较特殊的view,它被系统高度集成,有时候定制起来并不那么方便。其实这个demo完全可以用另外一种方法实现,就是不用UINavigationBar,自己画一套UI。
很多时候我们都会发现系统原生控件出现一些预料之外的行为,那么打开view debugging,找出原因,然后解决它!
Readmore postsby this author.
Share this post