最近做了个关于UINavigationController的demo,遇到一个问题,又引出更多问题,记录一下。 在ios7中,UINavigationController发生了很大的变化,特别是导航栏。
先用一些代码,描述问题:
1、添加导航控制器和视图控制器:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
MainViewController* mainController = [[MainViewController alloc] init];
UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:mainController];
self.window.rootViewController = nav;
return YES;
<span style="font-family: Arial, Helvetica, sans-serif;">}</span>
2、MainViewController添加UILabel
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(0,0, 50, 50)];
[label setText:@"test"];
[label setBackgroundColor:[UIColor redColor]];
[self.view addSubview:label];
}
代码在ios6和ios7运行的效果如下:
ios6:
ios7:
描述:self.view代表maincontroller的视图
问题出现了,很明显可以看出来,在ios7里面,self.view的内容延伸到了导航栏和状态栏的下面,并且可以模糊的看到。
首先我们来大概了解一下导航器的视图结构。用NSLog打印出状态栏、导航条和self.view以及self.view.superview的frame可以看出一些来。这里以3.5屏幕为例:
在ios6中: 状态栏(0,0,320,20) 导航条(0,20,320,44) self.view(0,0,320,416) self.view.superview(0,64,320,416)
ios6隐藏状态栏: 状态栏(0,0,0,0) 导航条(0,0,320,44) self.view(0,0,320,436) self.view.superview(0,44,320,436)
ios6隐藏导航栏: 状态栏(0,0,320,20) 导航条(0,-24,320,44) self.view(0,0,320,460) self.view.superview(0,20,320,460)
可以看出,状态栏、导航栏、self.view.superview在同一个坐标系内。状态栏和导航栏的改变,会影响self.view.superview的变化。但是要注意的是,self.view的坐标始终不变,只是高度会随着superview而变化。至于隐藏导航栏以后,为什么y坐标会变为-24,是因为作为状态栏的背景吗?
在ios7中,状态栏背景透明,导航栏半透明,导航栏的背景高度为64,坐标(0,-20,320,44),Y方向多出的20点可以作为状态栏的背景。默认self.view延伸到屏幕顶部,被状态栏和导航栏覆盖。默认导航栏的 translucent = YES 此时各个元素的frame如下:
状态栏(0,0,320,20) 导航条(0,20,320,44) self.view(0,0,320,480) self.view.superview(0,0,320,480)
ios7隐藏状态栏: 状态栏(0,0,0,0) 导航条(0,0,320,44) self.view(0,0,320,480) self.view.superview(0,0,320,480)
ios7隐藏导航栏: 状态栏(0,0,320,20) 导航条(0,-44,320,44) self.view(0,0,320,480) self.view.superview(0,0,320,480)
可以看出,self.view.superview不会因为状态栏和导航栏的改变而发生变化,始终铺满整个屏幕,当然self.view也是铺满整个屏幕的。
现在设置translucent = NO
状态栏(0,0,320,20) 导航条(0,20,320,44) self.view(0,64,320,416) self.view.superview(0,0,320,480)
ios7隐藏状态栏: 状态栏(0,0,0,0) 导航条(0,0,320,44) self.view(0,44,320,436) self.view.superview(0,0,320,480)
ios7隐藏导航栏: 状态栏(0,0,320,20) 导航条(0,-44,320,44) self.view(0,0,320,480) self.view.superview(0,0,320,480)
设置translucent=NO之后,self.view.superview依然铺满屏幕,而self.view在状态栏和导航栏都可见的情况下,Y方向坐标是64;当状态栏隐藏,self.viewY坐标上移20点,高度增加20点。这是因为设置了导航栏的属性translucent=NO,所以self.view从导航栏下面开始平铺。
重点是,隐藏了导航栏之后,translucent属性失去作用,self.view从屏幕左上角开始平铺,被状态栏覆盖了20点的高度。
总结:
一、ios6中
1、状态栏20高度导航栏44高度;
2、self.view.superview的Y坐标和高度会随着状态栏和导航栏高度变化;
3、self.view坐标和宽度不变,高度会随着self.view.superview变化。self.view的子视图不会被状态条或者导航条遮盖。
二、ios7中
1、状态栏背景透明,高度20;
2、导航栏背景半透明,坐标(0,-20,320,64),Y轴方向多出的20点可以作为状态栏的背景,所以设置导航栏背景色,也能作为状态栏背景色;
3、因为状态栏和导航栏所在区域提供给开发者操作,所以self.view.superview从屏幕顶部开始平铺整个屏幕并且固定并不变;
4、当导航栏显示且透明时,self.view的坐标(0,0),当然也是平铺整个屏幕,延伸到导航栏和状态栏下面;
5、当导航栏显示但是不透明,self.view刚好在导航栏的下边界开始平铺,此时状态栏的隐藏会影响到导航栏和self.view的坐标和高度。
6、导航栏隐藏,则透明属性不起作用,self.view坐标(0,0),平铺整个屏幕,延伸到状态栏的下面。
三、对于ios7中设置self.view从导航栏下面显示,有三种方式
1、[self setEdgesForExtendedLayout:UIRectEdgeNone]; 设置本控制器的视图延伸范围。对本控制器视图有效果。
在ios7以下ios3以上的系统,可以使用wantsFullScreenLayout属性代替。
2、[navigationBar setTranslucent:NO]; 设置导航栏不透明,对所有控制器视图有效果。导航栏不透明了,不会再让你延伸view了。
3、[navigationBar setBackgroundImage:[UIImage imageNamed:@"image_background.png"] forBarMetrics:UIBarMetricsDefault];自定义导航栏背景图片,对所有控制器视图有效果。自定义背景,等于不透明,延伸了也看不到哇。
iOS7 SDK中新增了一个设置背景图片的方法(setBackgroundImage:forBarPosition:barMetrics:),比原有的方法多了一个UIBarPosition枚举参数,用于设置背景图片拉伸的策略
值UIBarPositionTopAttached表明操作栏贴近屏幕顶部,背景向上延伸入系统状态栏区域。相对的,值UIBarPositionTop表示操作栏位于当前局部内容的顶部(比如,在弹出式气泡的顶部)它不为状态栏提供背景
所以可以判断ios版本,用不同API给导航栏设置背景图片,来达到适配ios7和ios6的目的。
在2和3的case,可以单独设置某个控制器view延伸到导航栏和状态栏下面:extendedLayoutIncludesOpaqueBars属性,默认是NO,设置为YES时,可以延伸。
知晓了以上的情况,在设置self.view的布局及其子视图的时候,就可以根据需求来设置了。
理解不够深刻和准确,非常欢迎指正!
后面转载一篇更加专业的博文。
//6月11日更新
总结中case3里面,背景图片如果是半透明的,self.view还是会有延伸效果的;所以如果想阻挡它的延伸,一定要是不透明的背景图片。