首页 > 代码库 > IOS入门之StoryBoard

IOS入门之StoryBoard

概述

在iOS的发展历程中,IOS开发经历了三种主要流派:使用代码手写UI及布局;使用单个xib文件组织viewController或者view;使用StoryBoard来通过单个或很少的几个文件构建全部UI。而在最近几年的开发中,苹果对 Storyboard 的开发力度也不断增强,添加了更多功能和特性,大大方便了界面的开发、适配以及提升代码性能。

我们来看看三种方式的主要区别:

手写页面和逻辑代码

如果你的目的是写一些可以高度重用的控件提供给其他开发者使用,那毫无疑问最好的选择应该是使用代码来完成UIView的子类.这样进一步的修改和其他开发者在使用时,都会方便不少.使用代码也是最为强大的,会有xib或者StoryBoard做不了的事情,但是使用代码最终一定能够完成所要的需求。

XIB

其实IB和xib是从iOS SDK初次面世开始就是捆绑在开发者工具套装内的内容了,而到了Xcode 4之后更被直接集成到了Xcode中成为了IDE的一部分. xib设计的一大目的其实是为了良好的MVC:一般来说,单个的xib文件对应一个ViewController,而对于一些自定义的view,往往也会使用单个xib并从main bundle进行加载的方式来载入.IB帮助完成view的创建,布局和与file owner的关系映射等一些列工作。

但是xib最大的问题在于xib中的设置往往并非最终设置,在代码中你将有机会覆盖你在xib文件中进行的UI设计.在不同的地方对同一个属性进行设置,这在之后的维护中将会是噩梦般的存在.因为其实IB还是有所局限的,它没有逻辑判断,也很难在运行时进行配置。

StoryBoard

简单理解来说,可以把StoryBoard看做是一组viewController对应的xib,以及它们之间的转换方式的集合.在StoryBoard中不仅可以看到每个ViewController的布局样式,也可以明确地知道各个ViewController之间的转换关系。
现在StoryBoard面临的最大问题就是多人协作.因为所有的UI都定义在一个文件中,因此很多开发者个人或企业的技术负责人认为StoryBoard是无法进行协作开发的,其实这更多的是一种对StoryBoard的陌生所造成的误解.虽然Apple并没有在WWDC明确提及,但是没有人规定整个项目只能有一个StoryBoard文件。
现在会有一些对于StoryBoard性能上的担忧. 因为相对于单个xib来说,StoryBoard文件往往更大,加载速度也相应变慢。不过苹果正在这方面做大的改进。

StoryBoard本质

StoryBoard 的本质是一个 XML 文件,描述了若干窗体、组件、Auto Layout 约束等关键信息。示例文件
https://github.com/johnlui/AutoLayout/blob/master/AutoLayout/Base.lproj/Main.storyboard
Storyboard 最大的好处就是把界面和代码分离,如同 Web 开发中 CSS 描述界面,HTML 描述内容,JavaScript 描述行为一样,能让代码变得更清晰易读,界面的行为以及相互的逻辑也变得更直观。

使用 StoryBoard 的 iOS 项目均以初始化 StoryBoard 文件作为整个程序的初始化入口,UIViewController 类是由于被 StoryBoard 绑定而初始化,从而启动app并运行。

StoryBoard使用

1,在ARC下创建StoryBoard文件

技术分享

2,一般情况下, 程序都是写在Xcode默认的Main.storyboard里面,也可以自定义storyboard到相应的模块。
技术分享

3,创建自定义StoryBoard后, 记得要修改Main Interface为自己定义的StoryBoard
技术分享

注:大家记得主界面这里Is Initial View Controller要记得勾选, 这样红圈里面的小箭头才会出现, 表示程序运行从这个界面开始。
技术分享

Storyboard下Xcode右下角, 大家在下方Filter搜索栏可搜索自己想要添加的控件类型, 并拖拽至屏幕中央的View Controller上。这和其他的界面开发软件都差不多。
技术分享

当然也可以对控件的属性进行编辑。
技术分享

我想看到这里大家都不禁想跃跃欲试了吧。入门教程就讲到这里,接下来说说如何写一套代码和页面分离的ios高质量代码规范。

Storyboard 使用规范

Storyboard 分离规范

除非是单页面应用,在大多数情况下,使用单一 Storyboard 往往是不可行的,因为每个 App 通常由各种不一样的复杂模块构成,每当里面的一个元素的属性或位置产生变化,都会导致整个 Storyboard 文件产生多处代码的变动。尤其在 Git 代码版本的控制下,处理分支合并产生的冲突会变得非常麻烦。

所以,把 Storyboard 分离有一个 1 - 5 原则:把每一个 Storyboard 尽量分离成一个人处理范围,每一个 Storyboard 尽量控制少于 5 个 View。例如,设置页面、注册页面、信息详情页、发布页面等,基本上只需要一人就能负责,而且页面大多数情况下都会少于 5 个 View (如果超过 5 个的时候,就可以考虑是不是在里面可以考虑把它们再分离)。把该模块的所有 View 都放在同一个的 Storyboard 文件里进行管理。这样就能大大减少相互冲突的情况,并且 Storyboard 的读取和渲染性能也比单独打开一个大型 Storyboard 的要快不少。

比如我们把项目的Storyboard 全部放在一个文件维护。
技术分享

Storyboard 命名规范

一套优秀的代码项目,变量的命名,代码的语言风格都会有一套对应的规范,以便于成员之间进行沟通和开发。在 Storyboard 里面,各种的元素标识同样地也需要一套命名的规范,以便于开发之间的沟通。。例如:albumCollectionViewController -> Album Collection View Controller,passwordLabel -> Password Label。这个可以根据各个团队和行业的规范自己作一个命名规范。

Storyboard 界面规范

Storyboard 除了作为 UI 界面的构建语言之外,还担当着界面预览的角色。界面规范有一个原则,就是当把 Storyboard 展示给其他人的时候,尽量做到能让别人一看就知道这个 Storyboard 是干什么的,界面之间的逻辑是怎样的,界面大致的效果是如何的。

布局规范

Xcode 本身就有就有布局辅助线提示的功能,当进行拖动的时候,可以根据提示的辅助线来进行 View 或控件之间的对齐,合理利用此特性能方便我们调整出整齐的布局。

View 和 View 之间的摆放,可以遵循从左往右,从上到下的原则,进行逻辑排列。因为在 Storyboard 里 View 之间的连线算法都是从 View 的右边缘出发,到另外一个 View 的左边缘结束,如果调转了,就会出现很多交叉的连线,让界面看起来很乱。

在 Xcode 7 里,新增加了 Storyboard Reference 控件(虽然是在 iOS 9 新增加的,但 iOS 8 也可用)。合理使用此控件,能让 Storyboard 描述出 View 与其它 Storyboard 的跳转逻辑。并且可以通过 Segue 来进行 View 之间的数据传递,统一规范 View 之间跳转的代码,让逻辑更清晰。

所以我们在写布局的时候尽量预览下实际效果。

技术分享

初学StoryBoard的一些bug

问题1

初学 iOS 开发的时候,如果使用了 StoryBoard,大家可能遇到过这个奇怪的 bug,比如页面全黑。
APP 在启动以后,会先启动主 StoryBoard,这时候一定要记得主勾选Is Initial View Controller这个选项。

问题2

如果我们在写多个StoryBoard的时候,对于初学者经常回初学下面的情况。
技术分享

这个警告的意思是,这个窗体没有入口,无法被触达。解决方式有两种:
1,设置为该 StoryBoard 的初始窗体:
技术分享

给该窗体设置一个 StoryBoard ID:
技术分享

然后我们就可以在代码中写跳转逻辑了。
主界面

let vc = UIStoryboard(name: "Second", bundle: nil).instantiateInitialViewController() as! UIViewController
self.navigationController?.pushViewController(vc, animated: true)

第二界面

let vc = UIStoryboard(name: "Second", bundle: nil).instantiateViewControllerWithIdentifier("First") as! UIViewController
self.navigationController?.pushViewController(vc, animated: true)

当然,也可以用 self.presentViewController 调用他们。

技术分享

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    IOS入门之StoryBoard