尽管现在已经是Apple将Storyboard整合进Xcode中的第四个年头,大家对于Storyboard的评价仍然褒贬不一。 有早期就选择转向Storyboard 用于UI开发的国内业界领头人物,也有 创建项目就立马删除Storyboard 的大牛。我经历过纯代码布局,同时也在多个多人合作项目中使用Storyboard开发界面。在初期绕过各种坑后,Storyboard将会是快速构建UI界面的好帮手,特别是在现如今设备分辨率与尺寸日益增加的情况下,它可以帮助工程师们节约大量的界面代码书写时间。Storyboard存在的一大意义在于为UI提供了可视化开发方式,另一方面提供了一种更好的MVC的View层实现方式,让你的ViewController代码更简洁。当然,Storyboard的不足仍然不可忽视,错误的难以定位经常让刚上手的开发者们手足无措,相比于代码更不容易阅读的XML源文件所导致多人合作中的冲突不易解决等问题仍然有待完善。本文从各个方面介绍一下Storyboard,分享一下Storyboard的一些使用心得。
1986年Jean-Marie Hullot发明了IB(Interface Build--Storyboard的前身),并且和Macintosh的工具箱无缝融合,这一工具被Denison Bollay发现了。第二年, Denison Bollay带着Hullot和他的IB到NeXT,将IB演示给Steve Jobs看。老乔立意识到了IB的价值,并将其纳入到了NeXTSTEP中。之后Steve 带着NeXT的技术结晶(当然也包括IB)重新回归Apple,并将之整合到了Apple的体系中。2008年第一代iPhone SDK发布的时候,IB就已经捆绑在其中。到了Xcode4,Apple更是直接将其集成进IDE里。随后随着不断地改进,更新,演变,最终变成了我们今天所看到的Storyboard。从某种角度来说,Storyboard也是老乔留给我们的众多礼物之一。
故事版主要为我们提供了以下的功能:(这些功能都是 可视化
的)
自动布局颠覆了之前直接操作Frame的布局方式,从思考 View应该在哪个位置
,变成了考虑 在特定条件下,View的所处的位置需要满足哪些条件
。通过这些条件来确定View的Frame。自动布局在实际应用中大体上可以将分为三组:
加入我们需要在代码中使用自动布局可以使用 Visual Format Language 或者 NSLayoutConstraint
的简单工厂方法来生成约束,然后添加到View上。我们来看一个例子:
//用代码来实现上图中View与Super View的约束 UIView *superView = self.view; UIView *subView = [[UIView alloc] init]; NSLayoutConstraint *leadingConstraint = [NSLayoutConstraint constraintWithItem:superView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:subView attribute:NSLayoutAttributeLeading multiplier:1 constant:15]; NSLayoutConstraint *TrailingConstraint = [NSLayoutConstraint constraintWithItem:superView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:subView attribute:NSLayoutAttributeTrailing multiplier:1 constant:15]; //topConstraint init... //bottomConstraint init... [superView addConstraint:leadingConstraint]; [superView addConstraint:TrailingConstraint]; [superView addConstraint:topConstraint]; [superView addConstraint:bottomConstraint]; // 如果是iOS8+ 则使用下面的方式来激活Constraint // leadingConstraint.active = YES; // leadingConstraint.active = YES; // leadingConstraint.active = YES; // leadingConstraint.active = YES;
是不是一大团乱糟糟的代码?Visual Format Language用起来更加令人崩溃。好在业界已经有比较好的代码自动布局的 第三方解决方案 。但是仍然会有大堆的简单界面布局代码残留在你的代码中。
为了让你的生活更轻松(也为了让代码更清爽),Storyboard就包含了非常优雅的可视化自动布局解决方案。以上一切,在Storyboard中都被浓缩成了两个按钮(下图红圈中的椭圆按钮)。
只需要点击几下鼠标,Storyboard就可以帮你轻松完成视图布局。
使用代码来对Auto Layout布局的另一个缺点在于debug的困难。当添加了多余的约束,往往只能在运行时才能发现错误。同时,要寻找出是哪一行代码添加了错误的约束也比较费力(往往连控制台都没有错误输出)。
而Storyboard却为此提供了非常友好的静态检查。主要针对View的约束、布局提供警告和Error,甚至是解决方案。
上图的例子是:我们为Label添加了多余的约束,Storyboard用红色标记出冲突的约束,并给出修改建议: 删除其中一个约束以保证约束的正确性。 是不是很友好? :)
Apple 与iOS 8推出了Size Classes的概念。意在解决因设备尺寸造成的适配问题。Size Classes通过将界面的宽度和高度抽象为 正常
和 紧凑
两种概念,通过合理的组合,可以将现有设备(以及未来将要出现的设备)划分到不同的Size中。因此,无论是代码还是界面布局,只需要针对Size进行,而不用再拘泥于分辨是iPhone还是iPad,是横屏还是竖屏的问题了。Size Classes的推出是具有前瞻性的,无论是Apple Watch还是iOS 9推出的的iPad 分屏模式,都可以用Size Classes完美解决适配的问题。
Size Classes和现有设备的对照表如下:
在之前,我们要对横屏竖屏的界面进行区分,代码一般是这样的:
if (IPAD_PORTRAIT) { //TODO:modify something portrait } else { //TODO:modify something landscape }
在Size Classes时代,Apple引入了一个新的类 UITraitCollection
来封装水平和垂直方向的Size信息。现在我们通过代码来改变界面是这样的:
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator { [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator]; [coordinator animateAlongsideTransition:^(id <UIViewControllerTransitionCoordinatorContext> context) { if (newCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact) { //To Do: modify something for compact vertical size } else { //To Do: modify something for other vertical size } [self.view setNeedsLayout]; } completion:nil]; }
在TODO中做相对应Size的事。
可以想见的是,仍然会有非常多的布局代码占据着你的源文件。但在Storyboard中,一切变得异常简单。
使用Size Classes,我们只需要选择相对应的size,在那个Size下进行布局。运行时,就会根据设备的尺寸,自动地展示相对应Size的布局。比如iPhone竖屏就展示 width Compact height Regular
Size下的信息。当手机横屏,系统会自动添加一个过渡动画(虽然有点生硬),并转到 width Regular height Compact
的Size。这一切不需要一行代码。
能不能再给力点?
Sure.有这么一种情景: iPhone横屏下,拥有一个 avatarView
,竖屏下拥有一个相同的 avatar View
。这种情况下我们只需要在一个Size中完成这个View,然后在Storyboard的 attributed inspector
中做一些勾选,将其"install"进相对应的Size中,就可以达到复用的目的。如果有差异,则在对应的Size中定制即可。(如下图)
能不能再给力点儿?
Of Course!除了View,约束也可以不同Size配置不同。最厉害的是,图片文件也可以根据Size来区分。我们只需要对 .xcassets
文件勾选Size Classes,就可以为不同Size配置不同图片.这意味着,在同一个安装包下,通过Size Classes,我们甚至可以为横屏iPhone和竖屏iPhone做出完全不同的App!
Storyboard的爱与恨(下) ----未完成