在代码中修改故事板自动布局约束的最佳实践是什么?

4
我一直以来都是用代码做我的用户界面,但我决定在当前项目中使用storyboard和自动布局。一切都进行得很顺利,直到我构建了一个包含约50个视图以及一些网格视图的复杂场景。问题是,我的自动布局在某些设备和方向上变得混乱。我发现使用IB尝试修复数十(数百?)个约束很具有挑战性,或者跟踪问题并解决它们。情况是这样的,我没有收到错误或警告,只是有时会出现不愉快的布局。而且,IB可能需要您点击和更改设置来跟踪约束信息,更不用说获得有关它们在场景中如何相互关联的完整想法了。
我刚刚花了一天时间阅读有关自动布局和约束的文档和背景材料,似乎使用可视化格式在代码中指定约束并创建一些自定义代码是最好的解决方案。然而,我似乎找不到关于如何从IB过渡到代码的任何信息。
具体地说,我应该清除所有IB约束并全部手动完成,还是可以有选择地执行?我之所以问这个问题,是因为我有一些包含视图组的容器视图,其中内容视图具有完美的布局。
其次,我应该将代码放在哪里?我想共存storyboard,并只想有选择地修改一些复杂场景。ViewWillAppear:是否是控制其视图的视图控制器修改或删除/添加约束的正确位置?

只是一个快速的更新,我还没有解决这个问题。我做了很多自由职业工作,不得不把这个放在一边一段时间,但希望在一两天内回来并尝试不同的建议。感谢Ron和Eagle11的建议,两者都看起来非常好。 - Fran K.
3个回答

9

将要在Storyboard/XIB文件中修改的NSLayoutConstraint的IBOutlet连接到您的控制器/视图类。

一旦连接了布局对象,就可以修改.constant属性并对视图进行动画处理:

[self.containerView layoutIfNeeded]; //make sure all layout operations are done
self.containerViewBottomLayoutConstraint.constant = 200.0; //change the layout
[UIView animateWithDuration:duration animations:^{
    [self.containerView layoutIfNeeded]; //animate the changes
}];

更新:你可以将修改代码放在viewDidLoad、awakeFromNib、viewDidAppear或基于事件的方法中。这取决于你的意图。


我认为我有太多的限制条件来连接到IBOutlets,但如果我可以将我的更改减少到一个小集合,这可能会起作用。我需要研究一下这个问题来做出决定,但看起来这是一项很大的工作,如果所有的限制条件都是从代码开始的话,可能会更容易些。 - Fran K.
我的意图是修复约束,使其在所有设备配置中都能正常工作。如果可能的话,我不打算在运行时对它们进行动画或更改。我假设viewWillAppear可能是设置它们的地方,但是您的评论让我想知道那是否正确。当使用Storyboards时,awakeFromNib是否会触发,并且它是否是删除/添加约束的正确位置?viewDidLoad是否太晚了?这会导致用户可以看到场景的变化吗? - Fran K.
awakeFromNib函数在从storyboard反序列化视图(控制器)时确实会被调用:更多信息请参见:https://developer.apple.com/library/ios/documentation/uikit/reference/NSObject_UIKitAdditions/Introduction/Introduction.html#//apple_ref/occ/instm/NSObject/awakeFromNib 我会在viewDidLoad或awakeFromNib中设置约束,具体取决于你是否打算在didLoad中进行额外的视图层次结构修改。在storyboard中设置所有约束有什么问题吗?尝试使用新的beta版xcode,它有一些改进。还可以查看autolayout WWDC13会议。 - Ron
谢谢,Ron。我会查看文档并重新观看这些课程。我在使用IB时遇到的问题是,一旦你有了100多个约束条件,点击和跳转的次数就会变得很痛苦,并且会分散注意力。而且不幸的是,IB似乎并不总是保持UI的状态,因此当您返回某个内容时,菜单或编辑器会恢复到默认状态。IB已经取得了长足的进步,我欣赏它试图解决的复杂问题,但是当我有这么多实体时,我发现在代码中命名对象并对它们进行分组、排序和组织要容易得多。 - Fran K.
此外,当你选择像“添加缺失的约束”或“应用约束更改”这样的选项时,你永远不知道IB是否会按照你的意愿执行。它擅长指出错误,但它不像编码那样具有表达能力和特定性。 - Fran K.

5
抱歉耽搁了这么久才回复,期间有其他项目需要处理。
我需要对场景进行大量重构,以便自动布局能够正常工作,但是我对结果并不完全满意。问题似乎在于IB在处理大量元素时并不容易使用,而自动布局则必须复杂。
话虽如此,到目前为止,我看到的最好的结果来自Justin Driscoll的这篇文章:http://themainthread.com/blog/2014/02/building-a-universal-app.html 他主张构建自定义视图来封装可重用的UI组件。我采取了这种方法,但将其扩展到了打包一些相关组件,这些组件在布局发生变化时不会有太大差异。例如,我有一个带有按钮和两个标签的进度条,即使我不将它们作为一组重用,但它们需要相邻且在概念上相关,因此我为它们制作了一个自定义视图,按照Justin的建议处理自动布局。
现在,我认为每个自动布局级别只应具有少量元素。如果一个级别变得过于复杂,我将把一些相关项捆绑在一个自定义视图中,并将一些自动布局推入该新视图中。到目前为止,情况还不算太糟糕。

我将选择这个作为最佳答案,因为Justin的解决方案给了我最大的控制,并且带来了最少的问题。Ron和Eagle11都提供了极好的建议,使我得出了这个答案,我希望我能够检查所有三个答案,但这是不可能的。谢谢大家。 - Fran K.

2

当使用大量视图时,自动布局可能会非常棘手。我曾经使用过类似复杂的视图结构,我发现最好尝试在代码或IB中保留所有约束条件。目前我们将它们保留在IB中。唯一需要将约束移入代码的时间是当我们支持不同的屏幕大小,并且需要修改单个约束以使视图正常工作时。我总是在viewDidLoad中自己修改约束。

当某些东西出了问题时,我几乎总是不得不清除该视图上的所有约束并重新开始。这很糟糕,但通常比追踪问题更快。我们做的一件事情使处理这种情况变得更容易,那就是与你的故事板一起使用.xibs。这样,每个视图都可以处理自己的布局,并且您可以将其拉入坐落在storyboard中的视图中。


我可以相信“要么全是,要么全不是”是正确的选择,因为错误的潜力似乎很高。 你可以解释一下如何使用xib吗?你是否制作一个来替换整个场景,并从代码中加载它? - Fran K.
嗯,我确定我们并不总是以最佳方式进行,但我们已经找到了一种适合我们的方法。我们尝试在storyboard中创建所有层次结构,并根据情况在storyboard中创建内容。在我们的一些项目中,由于项目非常大,我们有几个storyboard。因此,我们将许多视图分解为xib文件。我们在代码中加载xib并将其设置为视图控制器中的视图。 - Eagle11
顺便说一句,自动布局在处理所有奇怪的问题后,一个星期左右就会变得更容易使用。一旦你真正弄清楚了它,它可以相当容易地用于大型项目中。 - Eagle11

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接