iOS之页面布局

原文请点击

在《iOS 7 UI Transition Guide》中有在《iOS 7 UI Transition Guide》的Bar and Bar Buttons一节中有这么一段话

In iOS 7, the status bar is transparent, and other bars—that is, navigation bars, tab bars, toolbars, search bars, and scope bars—are translucent. As a general rule, you want to make sure that content fills the area behind the bars in your app.

翻译过来:

在iOS7中,状态栏是完全透明的,而其他bar,即navigation bars, tab bars, toolbars, search bars和scope bars都是半透明的。开发者需要保证页面内容能覆盖到这些bar的后面。

事实上,iOS7中的状态栏不仅变完全透明了,而且完全不占空间。
有码有真相 —— 新建一个UIViewController,再viewDidLoad里面输入以下代码,作为rootViewController启动应用:

- (void)viewDidLoad
{  
    [super  viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    UILabel *label = [[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 200, 20)]autorelease];
    label.text = @"I am a label";
    [self.view addSubview:label];
}

应用效果:

Paste_Image.png

可以看到的是label和status bar悲催地重叠了。
我们再套一个UINavigationController,可以看到更悲催的事情:

Paste_Image.png

label活生生地被navigationBar盖住了。

可以说,苹果这次在iOS7上的redesign对开发者来说是惨绝人寰的。
不过苹果还是有节操的,在iOS7上运行iOS7 SDK以下开发的应用时,保留了原先的页面结构布局,并且做了不少向下兼容策略。
而且,iOS7 SDK提供了一系列接口和策略方案,下文将会一一介绍并顺带剖析一下iOS7上的页面结构框架。

Realtime Debug Protal
首先介绍一个小工具,可以方便我们进行学习。它的小名叫RDP,是一个类似Web Inspector的工具,把这个工具引入我们的项目工程,并做一些简单的配置,然后运行真机或者模拟器。应用启动后,在浏览器输入手机的IP地址,就可以看到UIView的树状结构和Log信息,还可以在浏览器中对View进行移动,隐藏,选中高亮等操作。

Paste_Image.png

状态栏
在iOS7中,状态栏是透明的,就是说,状态栏只有文字没有背景。
而变透明之后就很容易和后面的内容混淆,虽说一般应用不会把内容和状态栏叠合在一起,但是至少,现在的情况是,默认是会叠合的,开发需要从20px像素以下开始布局页面元素才能避免。

苹果为了让深色浅色背景均能让状态栏内容清晰显示,提供两种状态栏样式:

UIStatusBarStyleDefault = 0 黑色文字,浅色背景时使用
UIStatusBarStyleLightContent = 1 白色文字,深色背景时使用

而以下两个旧状态栏样式将被废弃:
UIStatusBarStyleBlackTranslucent = 1
UIStatusBarStyleLightContent = 2

还有,iOS7中我们通过ViewController重载方法返回枚举值的方法来控制状态栏的隐藏和样式。
首先,需要在Info.plist配置文件中,增加键:UIViewControllerBasedStatusBarAppearance,并设置为YES;
然后,在UIViewController子类中实现以下两个方法:

- (UIStatusBarStyle)preferredStatusBarStyle
{    
    return UIStatusBarStyleLightContent;
}

- (BOOL)prefersStatusBarHidden
{
    return NO;
}

最后,在需要刷新状态栏样式的时候,调用[self setNeedsStatusBarAppearanceUpdate]方法即可刷新,若果需要以动画形式切换状态栏样式,则用以下方式调用即可:

[UIView animateWithDuration:0. animations:^{
    [self setNeedsStatusBarAppearanceUpdate];
}];

导航栏
在iOS7,由于状态栏背景透明,那么,导航栏背景就可能要兼职充当状态栏背景了。
iOS7默认导航栏样式就是这么做的,见下图:

Paste_Image.png

虽然用户看来,iOS7默认样式的状态栏和导航栏时连在一起的,但是实际上导航栏的位置和大小是和之前系统版本一样的,依然是贴在状态栏下面, 依然是高44px;之所以用户看来它们是连在一起,这是因为UINavigationBar里面的_UINavigationBarBackground 定位在y方向-20px的位置,然后高度增加到64px,这样就可以同时充当了两者的背景。

关于这些定位,苹果做了很多工作,后面也会谈到不少。不关心的同学可以略过,其实这些细节,个人觉得,即使对于开发者来说,也不是必需知道的,我们只需要知道怎么调用相关API就足够了。
实际情况下,我们会自定义导航栏背景,过去,我们也许会使用如下代码把一张高44像素(retina/88像素)的图片来平铺作为导航栏背景。

[navCtrl.navigationBar setBackgroundImage:[UIImage imageNamed:@"nav_background"] forBarMetrics:UIBarMetricsDefault];

启动应用,出现了意想不到的效果和久违的界面 —— 黑底白字的状态栏,不再被navigationBar盖住的label。

Paste_Image.png

这里两个点需要解释一下:

  1. 若我们使用自定义图片作为导航栏的背景,那么UIViewController的view(下面称为视图)就不会延伸到navigationBar的顶部,而是从它的底部开始——正如往常一样。
  2. 若我们使用一张高44像素(retina/88像素)的图片作为导航栏背景,那么状态栏就会保持黑色,图片只会在导航栏区域平铺。

另外,iOS7 SDK中新增了一个设置背景图片的方法(setBackgroundImage:forBarPosition:barMetrics:),比原有的方法多了一个UIBarPosition枚举参数,用于设置背景图片拉伸的策略。
针对不同的拉伸设置和背景图片尺寸,在《iOS 7 UI Transition Guide》的Bar and Bar Buttons一节中
中有详细说明:

Paste_Image.png

页面布局
在 《iOS 7 UI Transition Guide》的Layout and Appearance 一节中也提到 —— 在iOS7中,view controllers使用全屏布局 (In iOS 7, view controllers use full-screen layout)。

通过上面的讨论我们也知道,除非导航栏设置了自定义的背景图片,否则每个视图都会延伸到屏幕一样大小的。
所以,像上面第二张图片中出现导航栏遮盖label的情况也是正常的现象。

如果我们要让label从导航栏以下位置显示,可以通过修改UIViewController的edgesForExtendedLayout这个属性来实现。
edgesForExtendedLayout是一个类型为UIExtendedEdge的属性,指定边缘要延伸的方向。
因为iOS7鼓励全屏布局,它的默认值很自然地是UIRectEdgeAll,四周边缘均延伸,就是说,如果即使视图中上有navigationBar,下有tabBar,那么视图仍会延伸覆盖到四周的区域。

如果把视图做如下设置,那么视图就不会延伸到这些bar的后面了,于是label又出来了。

self.edgesForExtendedLayout = UIExtendedEdgeNone;
Paste_Image.png

也许,这时候你会想,那为什么不把UIExtendedEdgeNone作为默认态呢?
iOS7以后鼓励全屏,它希望用户在使用可滚动视图的时候可以透过半透明的bar还可以看到一些模模糊糊的内容。

为了保持设计的优雅,同时避免给开发者太多的困扰,iOS7在Conttoller中新增了这个属性:automaticallyAdjustsScrollViewInsets,当设置为YES时(默认YES),如果视图里面存在唯一一个UIScrollView或其子类View,那么它会自动设置相应的内边距,这样可以让scroll占据整个视图,又不会让导航栏遮盖,如以下例子:

Paste_Image.png

要注意的是,这个例子中我们没有设置edgesForExtendedLayout,即视图是延伸至全屏的。
我们可以从UIView树状图看到,tableview的bounds值中有64像素的偏移值,它作为一个内边距来保持内容显示在导航栏以下,而滚动时仍可以透过半透明的导航栏看到模糊的内容。

最后一个介绍的新属性是extendedLayoutIncludesOpaqueBars,这个属性指定了当Bar使用了不透明图片时,视图是否延伸至Bar所在区域,默认值时NO。
所以我们如果自定义了导航栏的背景图片,那么视图会从导航栏以下开始,不会延伸到导航栏区域。
如果把这个属性设置为YES,那么视图将会延伸至导航栏区域,即使我们把导航栏设置成了自定义背景,如下图:

Paste_Image.png

视图延伸之后,label又被导航栏覆盖住了,正如我们意料。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容