为什么我停止在iOS项目中使用Storyboard和Xib

故事板可帮助我们简化创建iOS应用程序的UI。 拥有一个仅需几次单击和拖动的模拟应用程序,真的很棒,对吗? 我们甚至可以忘记导航,即每个应用程序使用的功能。 仅通过从一个屏幕到另一个屏幕的链接即可完成所有导航,如果转换很复杂,有时还可以使用自定义代码。 苹果正在继续改进Xcode的Interface Builder,以添加越来越多的功能来帮助开发人员简化其UI存根。

例如,我们现在可以预览不同屏幕尺寸的渲染。 我发现此功能非常有用。

当我们由一个小型团队(或一个人)开发一个小型应用程序时,所有这些好东西仍然是好的。 一旦应用程序扩展,团队就会成长,使用情节提要和.xib可能会成为噩梦。 让我们看看为什么。

如果我们没有足够强大的MacBook(Pro),则当我们不得不打开一个大的故事板时,Xcode可能会冻结您的计算机一段时间。 即使使用简单的情节提要板,我也花了大约10秒钟的时间在Xcode中打开它。 对于必须使用旧的MacMini或旧的MacBook开发的人来说,这可能会降低他们的工作性能。

当我们创建具有多个屏幕的大型应用程序时,情节提要文件可能会非常庞大​​。 概述和遵循应用程序流程并非易事。 如果有新的开发人员加入该项目,他将很难理解屏幕之间的逻辑和转换。

当然,我们仍然可以采用一种解决方法来创建使用情节提要面板场景,以将其分隔为多个情节提要面板,并避免使用大型情节提要。 但是,从中创建View / ViewController的实例时,开发人员应该照顾故事板名称或Xib名称。

使用情节提要/ xib时最不方便的是与.storyboard或.xib文件存在某些合并冲突。 当团队的开发人员在不同的存储库分支中同时编辑同一.xib或.storyboard,然后合并到主分支中时,就会发生这种情况。

情节提要和xib文件均为XML格式。 但是这些XML不是用户友好的。 Xml节点是自动生成的,这使我们很难知道xml中带有节点的UIView的引用。 每个节点还具有很多属性(用于指定高度,宽度,位置,颜色等)。 发生合并冲突时,如果合并不正确,将导致错误的结果,有时我们会破坏xml格式,因此无法打开.xib或.storyboard文件。

基本上,我们不会通过Storyboard / xib创建ViewController和View,而是将它们全部写入* ViewController.m / .swift或* View.m / swift。 在这篇文章中,我只会以Swift来编写代码,因为我更喜欢Swift而不是Objective-C,而且我相信你们也喜欢Swift。

假设我们要创建一个包含登录名和电子邮件文本字段以及用于处理登录的按钮的登录屏幕,如下所示:

我们将必须创建两个类: LoginView.swiftLoginViewController.swift

  • LoginView.swift继承了 UIView的子类,并且包含用于构建要在所有自动布局约束条件下显示的视图的代码。
  • LoginViewController.swift继承了 UIViewController的子类,并包含我们需要应用于这些元素的控件处理程序。

首先,让我们看看我们在LoginViewController.swift文件中要做的事情。

它不是那么复杂,对吗? 我们要做的就是重写loadView函数,以告知LoginViewController它需要加载并使用LoginView类中的自定义视图。 然后,ViewController应该控制此View内部的功能元素,以控制用户单击登录按钮关闭按钮时的操作 。 在ViewController类中,我们不实现任何视图布局或视图的自动布局。 所有视图布局必须在View类内部完成。 这样,ViewController将变得更轻,更清洁。

但是,为AutoLayout编写代码很复杂。 我能怎么做?

我确实同意您的观点,通过编写来创建UI非常复杂,至少对于自动布局而言。 因此,我建议使用诸如PureLayout,SnapKit / Masonry等自动布局库,以帮助您节省进行自动布局时的时间。

首先,当我们通过代码创建UI时,一切都是从代码创建的(很明显)。 为了避免重写过多的UI代码,您应该创建一种UI提供程序来重用这些代码。 例如,我创建一个UIView扩展,并在此处编写代码以创建一个简单的按钮和文本字段:

然后,我可以在LoginView中重用这些功能来创建登录按钮和两个文本字段。

 类LoginView:UIView { 
让loginTextField = UIView.createTextField(“输入登录名”)
让passwordTextField = UIView.createTextField(“输入密码”)
让loginButton = UIView.createButton(“ Login”)
让closeButton = UIView.createButton(“ Close”)init(){
super.init(frame:.zero)

setUpView()
layoutView()
}
func setUpView(){
//子视图的附加设置
backgroundColor = .white //不要忘记此行,否则您的LoginView将是透明的
addSubview(loginTextField)
addSubview(passwordTextField)
addSubview(loginButton)
addSubview(closeButton)
}
...
}

对于自动布局,我使用SnapKit布局视图,我仍然必须编写一些代码,但是编写和查看起来要容易得多。

  func layoutView(){ 
//登录文本字段的大小:200 x 33点,距离顶部100点
loginTextField.snp.makeConstraints {
maker.centerX.equalToSuperview()
maker.width.equalTo(200)
maker.height.equalTo(33)
maker.top.equalToSuperview()。offset(100)
}
//密码文字字段的大小:200 x 33点
//从登录文本字段的底部起20点
passwordTextField.snp.makeConstraints {
maker.centerX.equalToSuperview()
maker.width.equalTo(200)
maker.height.equalTo(33)
maker.top.equalTo(loginTextField.snp.bottom).offset(20)
}
// loginButton的宽度:200点
//从顶部算起100分
loginButton.snp.makeConstraints {制造商在
maker.centerX.equalToSuperview()
maker.width.equalTo(200)
maker.top.equalTo(passwordTextField).offset(100)
}
closeButton.snp.makeConstraints {制造商
maker.centerX.equalToSuperview()
maker.width.equalTo(200)
maker.bottom.equalToSuperview()。offset(-30)
}
}

相信我,SnapKit非常易于使用,比通过Apple提供的API编写自动布局约束要容易得多。 请记住,任何UI元素的位置都与父元素或同级元素有关。 位置关系由上,下,左或右指定。 有时,我们可以使用其他关系,例如:edge,centerX,centerY等。它们与您使用Interface Builder时相同。

使用SnapKit或其他自动布局库时,使用自动布局创建UI不会很费力。

通过代码编写UI具有很多优点,也带来一些不便。 就个人而言,我看到的好处多于麻烦。

优点:

  • 使团队合作再一次变得出色,也可以进行代码审查
  • 合并冲突不再令人头疼
  • 不再等待加载情节提要
  • 可以重用代码
  • 使源代码可测试
  • ViewController很干净

不便之处:

  • 不再需要拖放
  • 必须编写更多代码
  • 无法预览我们正在创建的内容

希望这篇文章,您将在开发iOS应用程序时有更多方法来处理创建UI。

可以在https://bitbucket.org/haduyenhoa/userintefaceprogrammatically中找到本文中使用的示例代码源。