原文:IOS状态栏和导航栏的控制问题html
IOS的项目多数会遇到控制状态栏和导航栏的问题,好比隐藏状态栏、控制状态栏的文字颜色等,导航栏也有一样需求。本文总结一下操做方法。ios
首先一点,IOS的界面分为状态栏和导航栏,状态栏是指显示电池、时间的最顶部的一个窄条,高度为20个点;而导航栏是紧接着状态栏的44个点高度的横条,通常用于显示app标题,返回按钮等操做按钮。app
在ios7以前,状态栏和导航栏是分开的,而从ios7开始状态栏和导航栏交织在一块儿了,状态栏变为透明,导航栏的高度变为44+20=64:布局
对状态栏的控制分两种状况:全局设置和分页面设置。控制这两种模式的开关是info.plist
文件的View controller-based status bar appearance
配置项。字体
将info.plist
文件的View controller-based status bar appearance
设置为NO
,便可开启全局设置,也就是说你在VC中对状态栏的控制都将无效,相比之下,是经过下面的代码来全局控制:ui
//设置状态栏的字体颜色模式 [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; //设置状态栏是否隐藏 [[UIApplication sharedApplication] setStatusBarHidden:YES];
注意,咱们并不能对状态栏的字体颜色作任意的控制,只有两种选择UIStatusBarStyleDefault
和UIStatusBarStyleLightContent
,前者是默认的黑色,然后者是白色。也就是说若是你的背景色是偏深色,那么设置状态栏的字体颜色为白色。另外,咱们能够全局设置状态栏是否显示,可是通常而言app不会对全部界面都不显示状态栏,而是只在特定的页面须要隐藏状态栏,好比对于视频播放界面不但愿显示状态栏。atom
对于状态栏的背景色设置,上面提到从ios7开始状态栏自己其实是透明的,它的背景色其实取决于导航栏的背景色。下面会讲导航栏的设置。spa
将info.plist
文件的View controller-based status bar appearance
设置为YES
,便可开启由VC来控制状态栏的功能,在这种模式下,全局的设置将无效!!因此咱们必须逐个页面对状态栏进行设置,不然状态栏将维持默认的黑色字体和默认为显示状态。3d
对于设置状态栏字体颜色,分两种状况:VC是否属于UINavigationController
中:code
1) 当VC不在UINavigationController
中时,在VC中添加一个方法
- (UIStatusBarStyle)preferredStatusBarStyle { //返回白色 return UIStatusBarStyleLightContent; //返回黑色 //return UIStatusBarStyleDefault; }
保险起见,在view的某个加载阶段好比viewWillAppear
中,执行:
[self setNeedsStatusBarAppearanceUpdate];
2) 当VC在UINavigationController
中时,VC并不能经过1)的方式控制状态栏的颜色,详见本文后面的参考资料,那么这个时候,有一个trick的方法能够在VC中间接的控制:
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
对于控制状态栏的隐藏一样存在VC是不是根控制器的问题,也就说只有根控制器才能直接控制状态栏的显示与否。
1) 若是是VC自己就是根控制器,那么在VC中添加以下代码:
- (BOOL)prefersStatusBarHidden { return YES; }
固然,保险起见,在适当的时候调用
[self setNeedsStatusBarAppearanceUpdate];
2) 若是VC不是根控制器,那么不像控制字体颜色那样有trick,咱们只能间接的经过在子VC中控制根VC,从而间接控制根控制器。那么这个方法就不少了,好比个人根VC是个tab的VC,首先如今tab的VC中,实现1):
@interface YYCTabBarController : RDVTabBarController //定义一个变量来控制状态栏显示,子VC经过修改这个值来间接控制 @property (nonatomic,assign)BOOL statusBarHidden; @end @implementation YYCTabBarController - (BOOL)prefersStatusBarHidden { return _statusBarHidden; } @end
在子VC中:
- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; //rdv_tabBarController指向YYCTabBarController if([self.rdv_tabBarController respondsToSelector:@selector(setStatusBarHidden:)]){ [self.rdv_tabBarController performSelector:@selector(setStatusBarHidden:) withObject:@(YES)]; [self setNeedsStatusBarAppearanceUpdate]; } } - (void)viewWillDisappear:(BOOL)animated{ [super viewWillDisappear:animated]; if([self.rdv_tabBarController respondsToSelector:@selector(setStatusBarHidden:)]){ //注意对NO的状况,不能传@NO,只能传nil才能被当成NO [self.rdv_tabBarController performSelector:@selector(setStatusBarHidden:) withObject:nil]; [self setNeedsStatusBarAppearanceUpdate]; } }
能够看到在子VC中经过设置根VC的属性,并调用setNeedsStatusBarAppearanceUpdate
后,根VC的prefersStatusBarHidden
就会被调用,从而隐藏或显示状态栏。
在IOS7中使用barTintColor
来控制导航栏的背景色:
[[UINavigationBar appearance] setBarTintColor:[UIColor yellowColor]];
这个设置方法能够在AppDelegate
中设置,全局能够生效。
若是但愿使用图片来做为导航的背景,那么须要注意的是ios7中图片的高度问题。上面提到过了,ios7导航栏的高度实际上是算上状态栏的,即44+20=64个点的高度。能够经过setBackgroundImage
来设置:
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@ "nav_bg.png" ] forBarMetrics:UIBarMetricsDefault];
前景控制分为标题控制和返回按钮(等系统按钮)的控制
标题须要经过setTitleTextAttributes
来设置,相对比较复杂一些,例如:
NSShadow *shadow = [[NSShadow alloc] init]; shadow.shadowColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8]; shadow.shadowOffset = CGSizeMake(0, 1); [[UINavigationBar appearance] setTitleTextAttributes: [NSDictionary dictionaryWithObjectsAndKeys: [UIColor colorWithRed:245.0/255.0 green:245.0/255.0 blue:245.0/255.0 alpha:1.0], NSForegroundColorAttributeName, shadow, NSShadowAttributeName, [UIFont fontWithName:@ "HelveticaNeue-CondensedBlack" size:21.0], NSFontAttributeName, nil]];
设置返回按钮(等系统按钮)能够经过TintColor
,直接来设置颜色
[[UINavigationBar appearance] setTintColor:[UIColor whiteColor]];
有时咱们但愿导航栏不显示,而有时又但愿显示,那么最好经过每一个个体的VC来控制,若是某个VC须要与其余VC有所区别,那么最好是“负责到底”,即在进入VC时改变导航栏的显示状态,而退出时还原:
- (void) viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.navigationController.navigationBar setHidden:YES]; [self.rdv_tabBarController setTabBarHidden:YES animated:NO]; } - (void) viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self.navigationController.navigationBar setHidden:NO]; [self.rdv_tabBarController setTabBarHidden:NO animated:NO]; }
可能初学ios的同窗(尤为是经过手写代码布局的同窗)都会有这么个感觉,为何个人控件有的时候明明定位在VC上,但会被导航栏遮住,那么你可能会得出结论原点(0,0)是在屏幕的左上角被导航栏遮住的;而对于像UITableView
这样的,设置了全屏铺满,怎么就没有被导航栏遮住呢?原点难道不在左上角?
笔者被这个问题困扰了好久,这里谈一下最近的一个理解。咱们拿UITextView
来看
当咱们把一个UITextView
放到一个没有导航的VC中时:
UITextView *textView = [[UITextView alloc] init]; textView.frame = CGRectMake(10, 200, 300, 120); textView.backgroundColor = [UIColor redColor]; textView.text = @"游戏分两种,一种是在生活中玩的,另外一种是生活在其中的。这两个世界相互矛盾,而两位约翰就分别属于这不一样的世界。"; textView.font = [UIFont boldSystemFontOfSize:40]; textView.editable = NO; [self.view addSubview:textView]
效果是这样的,看起来并无什么问题
然而若是咱们把这个VC放到一个导航控制器中,一样的代码倒是这样结果
首先,看起来UITextView
距离设备顶部的绝对距离彷佛并无变化,可是请注意UITextView
的滚动条,滚动条居然没有顶部对齐,并且文字也向下移位了,看起来空出一大块。仔细看空出的这段高度其实恰好是导航栏的高度64个点!!通过搜索,我发现只要设置以下代码便可恢复这种异常的情况:
self.automaticallyAdjustsScrollViewInsets = NO;
这下明白了,原来VC会对其内部的UIScrollView
的内容部分进行一个Inset
,这个Inset
在上半部分恰好就对应导航栏的高度,而UIScrollView
包括UITableView
和UITextView
等。到这里,彷佛有些问题明朗了:
VC中的view默认会对UIScrollView
作一个适应导航栏的处理,由此推测,其实只要是VC中的控件,都是从设备左上角的(0,0)开始算的,只是对于UIScrollView
,VC会自动调整一下内容的位置而已。
在有导航的状况下,可视范围的Y坐标就是从64开始的,除了UIScrollView
的控件,定位的时候,都应当以(0,64)为原点;而UIScrollView
若是是全屏的,那么无所谓,若是不是全屏的,请注意是否须要设置VC的automaticallyAdjustsScrollViewInsets
。
参考资料