原文地址:http://www.jianshu.com/p/930643270455
总所周知,苹果从iOS7开始采用扁平化的界面风格,颠覆了果粉们“迷恋”的拟物化风格。对于开发者而言,全新的风格带来新的接口,这些新的接口改动中,有些更加合理了,有些更加方便了,而有些可能让开发者容易迷糊,下面本人就来谈谈iOS7这些新添加“鬼魅”的接口中的经常接触到的一个----UITabBar/UINavigationBar的translucent属性。
新的属性translucent简介
顾名思义,translucent属性能决定UITabBar/UINavigationBar是否为半透明的效果,苹果官方对此的解释如下:
-
UINavigationBar的translucent属性解释
/* New behavior on iOS 7. Default is YES. You may force an opaque background by setting the property to NO. If the navigation bar has a custom background image, the default is inferred from the alpha values of the image—YES if it has any pixel with alpha < 1.0 If you send setTranslucent:YES to a bar with an opaque custom background image it will apply a system opacity less than 1.0 to the image. If you send setTranslucent:NO to a bar with a translucent custom background image it will provide an opaque background for the image using the bar's barTintColor if defined, or black for UIBarStyleBlack or white for UIBarStyleDefault if barTintColor is nil. */ @property(nonatomic,assign,getter=isTranslucent) BOOL translucent NS_AVAILABLE_IOS(3_0) UI_APPEARANCE_SELECTOR; // Default is NO on iOS 6 and earlier. Always YES if barStyle is set to UIBarStyleBlackTranslucent
-
UITabBar的translucent属性解释
/* Default is YES. You may force an opaque background by setting the property to NO. If the tab bar has a custom background image, the default is inferred from the alpha values of the image—YES if it has any pixel with alpha < 1.0 If you send setTranslucent:YES to a tab bar with an opaque custom background image the tab bar will apply a system opacity less than 1.0 to the image. If you send setTranslucent:NO to a tab bar with a translucent custom background image the tab bar will provide an opaque background for the image using the bar's barTintColor if defined, or black for UIBarStyleBlack or white for UIBarStyleDefault if barTintColor is nil. */ @property(nonatomic,getter=isTranslucent) BOOL translucent NS_AVAILABLE_IOS(7_0);
简而言之,这个BOOL属性能控制UITabBar/UINavigationBar的半透明效果,默认为YES,即默认情况下为半透明效果(后面会详细解释这两段官方文字描述的含义)。
translucent缘何“鬼魅”
新属性的出现往往会导致一些“注意事项”,主要体现为被半透明效果处理后产生的色差和添加的类view控件的坐标变动。
默认情况下,如果使用UITabBarController和UINavigationBarController(translucent属性默认为YES),设置一个蓝色的view添加其中并设置距离屏幕边距为(0,0,0,0),展示效果如下:
可以看到,UITabBarController和UINavigationBarController被蓝色的view“穿透”了,此时view的边距也正如设置的一样,零点坐标在(0,0)处。
这时候,如果将view替换成tableView,展示效果如下:
可以看到,tableView的cell并没有因为“穿透”效果而出现被遮挡的情况,这是由于苹果对滚动视图的特殊性进行处理:对于类ScrollView,系统默认默认控制器属性automaticallyAdjustsScrollViewInsets默认为YES。
-
automaticallyAdjustsScrollViewInsets = YES时系统底层所干的事
scrollView的内容原本没有内边距,但是考虑到导航栏(高度44px)、状态栏(高度20px)、TabBar(高度49px)会挡住后面scrollView所展示的内容,系统自动为scrollView增加上下的内边距,这时候我们打印一下tableView的描述(友情提示:tableView继承自scrollView):
可以看出,contentOffset(内容坐标偏移量)和contentSize(内容尺寸大小)都发生了变化,结合tableView自身的frame可以看出,系统自动为scrollView增加了顶部64px的内边距以及底部49px的内边距,正好是导航栏高度+状态栏高度以及TabBar高度。一旦手动在系统布局页面之前设置automaticallyAdjustsScrollViewInsets = NO,将会取消上述操作,届时scrollView内容将会被部分挡住。
请注意:上述的情况仅仅对UIScrollView或者子类(如UITableView)有效。 -
非滚动视图中导致“鬼魅”的原因
从上述内容中,我们可以了解到滚动视图默认情况下系统会给滚动内容增加上下内边距,以防内容被导航条和TabBar遮挡,但是实际上在常用的项目界面结构(TabBarController--NavigationController--ViewController)以及系统默认情况下,viewController中的view是没有内边距的(也就是说view穿透上下两个Bar)。但是我们有时候会看到应用中会有这样的情形:非滚动视图依然有类似增加上下内边距的效果,自己并没有手动更改过视图的frame。这种情况,就要结合其他几个系统的属性来解释了。
针对非滚动视图的需求
由上述内容我们知道,默认情况下,导航条和TabBar都是半透明,添加在上面的控制器的视图会有“穿透”效果。如果现在有这样的需求:对于非滚动视图,从原点(0,0)布局,但是内容不被遮挡,能够正常显示。
这时候我们需要区分两种情况:是否需要导航条/TabBar带有半透明效果。
-
保留半透明效果(导航条/TabBar的translucent == YES)
- 手动修改frame布局,从(0,64)开始布局,底部同理,需要计算坐标尺寸,比较繁琐。
- 修改viewController的edgesForExtendedLayout属性,edgesForExtendedLayout = UIRectEdgeNone
(备注:设置后,控制器的view的frame的坐标Y增加64px紧挨着navigationBar下方,底部同理,该属性支持iOS7及以后的版本。)
注意:这里虽然看着导航条和TabBar还有半透明效果,但是实际上下面的内容已经无法再”穿透“了。
运行效果如下图所示:
-
不保留半透明效果(导航条/TabBar的translucent == NO)
将TabBar和导航条的translucent属性分别设置为NO,不做任何其他修改,此时bar的背景颜色为默认的白色,且不再有色差,运行效果如下图所示:
关于iOS7之后与view全屏相关的知识点
在iOS7之后,默认情况下,控制器的view是”全屏“的,也就是说,即便有TabBar或者NavigationBar,控制器的view也是可以”穿透至全屏“的,针对这一特性,苹果对UIViewController提供了以下几个属性供开发者使用:
// 在iOS7之前,控制器的view默认非全屏,如果想要全屏效果,需要设置为YES,该属性已从iOS7开始过期
@property(nonatomic,assign) BOOL wantsFullScreenLayout NS_DEPRECATED_IOS(3_0, 7_0) __TVOS_PROHIBITED; // Deprecated in 7_0, Replaced by the following: 该属性被以下三个属性代替 // Defaults to UIRectEdgeAll @property(nonatomic,assign) UIRectEdge edgesForExtendedLayout NS_AVAILABLE_IOS(7_0); // Defaults to NO, but bars are translucent by default on 7_0. @property(nonatomic,assign) BOOL extendedLayoutIncludesOpaqueBars NS_AVAILABLE_IOS(7_0); // Defaults to YES @property(nonatomic,assign) BOOL automaticallyAdjustsScrollViewInsets NS_AVAILABLE_IOS(7_0);
下面解释一下这三个属性:
-
edgesForExtendedLayout:意思是view的边缘允许额外布局的情况,默认为UIRectEdgeAll,意味着全屏布局(带穿透效果)。
-
extendedLayoutIncludesOpaqueBars:意思是额外布局是否包括不透明的Bar,默认为NO,意味着如果导航条或者TabBar非透明,view内容不会被他们遮挡,如果该属性设置为YES,那么在导航条或者TabBar非透明的情况下,view的内容将会被他们遮挡(原点为0,0),该属性仅仅对非透明的Bar控件有效。
示例展示如下图所示(代码设置 navigationBar.translucent = NO 并且 extendedLayoutIncludesOpaqueBars = YES)
-
automaticallyAdjustsScrollViewInsets:意思是是否由系统自动调整滚动视图的内边距,默认为YES,意味着系统将会根据导航条和TabBar的情况自动增加上下内边距以防止滚动视图的内容被Bar遮挡。
设置导航条或者TabBar背景图片的注意事项
这里会详细解释官方文档对translucent属性的注释,为什么放到这里才说?因为官方文档对该属性的解释全部跟设置导航条或者TabBar的背景图片有关!说明苹果也知道,这里坑很多,下面就来梳理一下吧。。。
translucent属性的官方解释
- UINavigationBar/UITabBar的translucent属性解释:默认为YES,可以通过设置NO来强制使用非透明背景,如果导航条使用自定义背景图片,那么默认情况该属性的值由图片的alpha(透明度)决定,如果alpha的透明度小于1.0值为YES。如果手动设置translucent为YES并且使用自定义不透明图片,那么会自动设置系统透明度(小于1.0)在这个图片上。如果手动设置translucent为NO并且使用自定义带透明度(透明度小于0)的图片,那么系统会展示这张背景图片,只不过这张图片会使用事先确定的barTintColor进行不透明处理,若barTintColor为空,则会使用UIBarStyleBlack(黑色)或者UIBarStyleDefault(白色)。
设置导航栏背景图片透明度问题
- 如果背景图片没有透明度,系统会自动把导航控制器的栈顶控制器的view的Y值增加64,如果没有透明度,则不会增加。
- 这一情况的图片必须是imageSet格式的,并且图片的透明度为1(不透明),系统才会去调整。