自动布局与框架尺寸
Auto Layout vs Frame Sizes
所以我有点不好意思问这个问题,因为它看起来很简陋,但自从开始 iOS 开发(大约一年的 self/internet 教学)以来,当不使用故事板时,我一直依赖严重依赖于框架尺寸并且很少使用自动布局。在 Objective C 中,我发现自己处于需要更频繁地使用自动布局的情况,但是使用 Swift 我可以为框架的 x 和 y 原点、它的中心等设置动画……我从来没有 运行 进入这样一种情况,我无法单独使用帧大小和像素位置来完成我想要的,并且我已经完成了一些相当复杂的视图工作。我的问题是,使用自动布局而不是框架大小和像素位置有什么好处?我不想因为无知和习惯而错过一些很棒的性能优势。非常感谢!
Auto Layout 的开发允许程序员在 iOS 上为任意大小的屏幕和视图编写程序。以前,对于 iPhone 的前四代,iOS 开发人员可以将他们的设计定位于一种屏幕尺寸,但最近的历史表明,Apple 对制造不同形状和尺寸的硬件持开放态度。自动布局提供了一种创建可适应不同屏幕和视图尺寸的 UI 的便捷方式。是否可以在没有自动布局的情况下开发 UI 以利用不同的视图尺寸?是的。但是要制作具有多个 UI 元素的真正可调整大小的视图,通常程序员必须求助于调整相对于其他 UI 元素定位的元素的大小,这些元素本身必须已经调整大小和定位。例如:
NSString *nameString = [NSString stringWithFormat:@"%@", self.name];
CGSize nameSize = [nameString sizeWithAttributes:attribs];
[nameString drawAtPoint:CGPointMake(self.bounds.size.width*0.5-nameSize.width*0.5, IMAGE_INSET+4*CONTENT_BUFFER(self.bounds.size.height)+facePicHeight) withAttributes:attribs];
questionFont = [UIFont systemFontOfSize:self.bounds.size.height*0.025];
attribs = @{NSFontAttributeName: questionFont, NSParagraphStyleAttributeName:paragraphStyle};
NSString *maleString = [NSString stringWithFormat:@"%@", self.male];
CGSize maleSize = [maleString sizeWithAttributes:attribs];
[maleString drawAtPoint:CGPointMake(self.bounds.size.width*0.5-maleSize.width*0.5,
IMAGE_INSET+5*CONTENT_BUFFER(self.bounds.size.height)+facePicHeight+nameSize.height) withAttributes:attribs];
NSString *femaleString = [NSString stringWithFormat:@"%@", self.female];
CGSize femaleSize = [femaleString sizeWithAttributes:attribs];
[femaleString drawAtPoint:CGPointMake(self.bounds.size.width*0.5-femaleSize.width*0.5,
IMAGE_INSET+6*CONTENT_BUFFER(self.bounds.size.height)+facePicHeight+nameSize.height+maleSize.height) withAttributes:attribs];
NSString *babyString = [NSString stringWithFormat:@"%@", self.baby];
CGSize babySize = [babyString sizeWithAttributes:attribs];
[babyString drawInRect:CGRectMake(IMAGE_INSET+CONTENT_BUFFER(self.bounds.size.height), IMAGE_INSET+7*CONTENT_BUFFER(self.bounds.size.height)+facePicHeight+nameSize.height+maleSize.height+femaleSize.height, self.bounds.size.width-2.0*(IMAGE_INSET+CONTENT_BUFFER(self.bounds.size.height)), babySize.height) withAttributes:attribs];
整个事情可能会变得有点笨拙。显然有些视图比其他视图简单,但如果您有复杂的视图,您希望支持多任务处理和本地化以及其他会影响布局的东西 UI 使用自动布局通常要容易得多。
依靠帧大小等于计算大小。当使用百分比或比例时,它可以在不同的 phone 模型上工作,但在少数分辨率下有时看起来仍然很奇怪。然后你会添加另一个条件语句,因为例如在 iPhone 4s 上,您可能需要稍微不同的布局。您的代码越来越复杂,可能导致任何更改都存在风险。这是可行的,但为什么要重新发明轮子呢?
像基本约束这样的工具有时会取代非常大的布局逻辑块。当第二个标签扩展时压缩一个标签的逻辑意味着很少的 ifs。考虑 10 个标签。添加大小 类、enabling/disabling 约束、压缩和拥抱,自动布局对我来说已经变得不可替代,我建议看看它,因为大多数公司都在使用它,如果不是在情节提要中,然后在 xibs 或代码中,但使用自动布局。
tl;博士
了解如何使用自动布局 – 它可以为实际应用程序节省大量时间!
长答案:
最后,是否要使用 Apple 提供的自动布局或大小 类 等功能,由您决定。就最终产品的 UI 渲染而言,这些的主要好处是 而不是 真正 性能 。如果您计划开发适用于不同屏幕尺寸和方向的应用程序,现在是您需要考虑所有边缘情况的时候了。
假设您想完成支持 iPhone 4/4s 屏幕尺寸,iPhone 5/5s 屏幕尺寸,iPhone 6/6s 所需的所有工作(加上)屏幕尺寸和 iPad 屏幕尺寸以及适用于上述所有屏幕尺寸的纵向和横向模式(here 是一个很好的概述):好吧,这没什么问题,就去做吧。当然,您随后需要非常仔细地进行测试,并始终保持此列表为最新——在每个应用程序中,您都在使用自己的解决方案。如果那是给你的,那就去做吧。
但大多数人都希望表达自己的想法,而不用纠结于不同屏幕尺寸等问题。您当然可以从一个应用程序中提取您自己的逻辑以在您的所有应用程序中使用它,而这正是自动布局和屏幕尺寸的意义所在。 简而言之,Apple 已经完成了让您的框架设置在不同屏幕上工作的所有工作。你只需要使用他们的新词汇(Constraints)就可以让它发挥作用。
换句话说:它是一个 直接处理屏幕渲染逻辑的抽象层 。如果你不喜欢它,就放手吧。但是,如果您打算开发一些应该适用于不同 iPhone/iPad 代的重要应用程序,并且还计划维护这些应用程序,那么,请学习如何使用自动布局和大小 类。它将为您和所有未来的维护者节省大量的开发时间。
一个好的起点是 raywenderlich.com 上的 docs. Or a tutorial like this one。
Apple 自己对这个困难说了以下内容(在上面链接的文档中):
In many ways, programmatically defining a view’s frame provides the most flexibility and power. When a change occurs, you can literally make any change you want. Yet because you must also manage all the changes yourself, laying out a simple user interface requires a considerable amount of effort to design, debug, and maintain. Creating a truly adaptive user interface increases the difficulty by an order of magnitude.
顺便说一句:
在编程语言方面使用自动布局或框架没有任何区别:Swift 和 Objective-C 都很好地支持这两种方式。看来您还没有找到如何在 Obj-C 中更改框架。 ;)
还有一点:
您还不需要使用 Storyboards 来使用自动布局。您可以从代码中使用自动布局。请参阅文档 here. There's even plenty of frameworks trying to make this easier (the original APIs from Apple tend to be not very pretty in many terms), I can recommend SnapKit.
iOS 基于框架与自动布局
Frame-based
- 通过 (x, y, with, height) 定位。多屏情况下的计算和变体很多
Autoresizing Masks
是基于父视图的视图调整大小行为。增加一些活力
view.autoresizingMask = [.flexibleRightMargin]
Auto Layout
- 使用约束来设置视图之间的关系。视图的框架是根据约束动态计算的
view.translatesAutoresizingMaskIntoConstraints = false // false for autolayout
view.topAnchor.constraint(equalTo: parentView.topAnchor, constant: 16).isActive = true
AutoLayout 在 UITableView
中使用,在 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
中使用 UITableView.automaticDimension
,这让您不必关心视图高度。
当视图已添加到打印视图时也设置约束
parentView.addSubview(view)
view.topAnchor.constraint(equalTo: self.topAnchor, constant: 16).isActive = true
或者你得到
Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors <> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.'
所以我有点不好意思问这个问题,因为它看起来很简陋,但自从开始 iOS 开发(大约一年的 self/internet 教学)以来,当不使用故事板时,我一直依赖严重依赖于框架尺寸并且很少使用自动布局。在 Objective C 中,我发现自己处于需要更频繁地使用自动布局的情况,但是使用 Swift 我可以为框架的 x 和 y 原点、它的中心等设置动画……我从来没有 运行 进入这样一种情况,我无法单独使用帧大小和像素位置来完成我想要的,并且我已经完成了一些相当复杂的视图工作。我的问题是,使用自动布局而不是框架大小和像素位置有什么好处?我不想因为无知和习惯而错过一些很棒的性能优势。非常感谢!
Auto Layout 的开发允许程序员在 iOS 上为任意大小的屏幕和视图编写程序。以前,对于 iPhone 的前四代,iOS 开发人员可以将他们的设计定位于一种屏幕尺寸,但最近的历史表明,Apple 对制造不同形状和尺寸的硬件持开放态度。自动布局提供了一种创建可适应不同屏幕和视图尺寸的 UI 的便捷方式。是否可以在没有自动布局的情况下开发 UI 以利用不同的视图尺寸?是的。但是要制作具有多个 UI 元素的真正可调整大小的视图,通常程序员必须求助于调整相对于其他 UI 元素定位的元素的大小,这些元素本身必须已经调整大小和定位。例如:
NSString *nameString = [NSString stringWithFormat:@"%@", self.name];
CGSize nameSize = [nameString sizeWithAttributes:attribs];
[nameString drawAtPoint:CGPointMake(self.bounds.size.width*0.5-nameSize.width*0.5, IMAGE_INSET+4*CONTENT_BUFFER(self.bounds.size.height)+facePicHeight) withAttributes:attribs];
questionFont = [UIFont systemFontOfSize:self.bounds.size.height*0.025];
attribs = @{NSFontAttributeName: questionFont, NSParagraphStyleAttributeName:paragraphStyle};
NSString *maleString = [NSString stringWithFormat:@"%@", self.male];
CGSize maleSize = [maleString sizeWithAttributes:attribs];
[maleString drawAtPoint:CGPointMake(self.bounds.size.width*0.5-maleSize.width*0.5,
IMAGE_INSET+5*CONTENT_BUFFER(self.bounds.size.height)+facePicHeight+nameSize.height) withAttributes:attribs];
NSString *femaleString = [NSString stringWithFormat:@"%@", self.female];
CGSize femaleSize = [femaleString sizeWithAttributes:attribs];
[femaleString drawAtPoint:CGPointMake(self.bounds.size.width*0.5-femaleSize.width*0.5,
IMAGE_INSET+6*CONTENT_BUFFER(self.bounds.size.height)+facePicHeight+nameSize.height+maleSize.height) withAttributes:attribs];
NSString *babyString = [NSString stringWithFormat:@"%@", self.baby];
CGSize babySize = [babyString sizeWithAttributes:attribs];
[babyString drawInRect:CGRectMake(IMAGE_INSET+CONTENT_BUFFER(self.bounds.size.height), IMAGE_INSET+7*CONTENT_BUFFER(self.bounds.size.height)+facePicHeight+nameSize.height+maleSize.height+femaleSize.height, self.bounds.size.width-2.0*(IMAGE_INSET+CONTENT_BUFFER(self.bounds.size.height)), babySize.height) withAttributes:attribs];
整个事情可能会变得有点笨拙。显然有些视图比其他视图简单,但如果您有复杂的视图,您希望支持多任务处理和本地化以及其他会影响布局的东西 UI 使用自动布局通常要容易得多。
依靠帧大小等于计算大小。当使用百分比或比例时,它可以在不同的 phone 模型上工作,但在少数分辨率下有时看起来仍然很奇怪。然后你会添加另一个条件语句,因为例如在 iPhone 4s 上,您可能需要稍微不同的布局。您的代码越来越复杂,可能导致任何更改都存在风险。这是可行的,但为什么要重新发明轮子呢?
像基本约束这样的工具有时会取代非常大的布局逻辑块。当第二个标签扩展时压缩一个标签的逻辑意味着很少的 ifs。考虑 10 个标签。添加大小 类、enabling/disabling 约束、压缩和拥抱,自动布局对我来说已经变得不可替代,我建议看看它,因为大多数公司都在使用它,如果不是在情节提要中,然后在 xibs 或代码中,但使用自动布局。
tl;博士 了解如何使用自动布局 – 它可以为实际应用程序节省大量时间!
长答案:
最后,是否要使用 Apple 提供的自动布局或大小 类 等功能,由您决定。就最终产品的 UI 渲染而言,这些的主要好处是 而不是 真正 性能 。如果您计划开发适用于不同屏幕尺寸和方向的应用程序,现在是您需要考虑所有边缘情况的时候了。
假设您想完成支持 iPhone 4/4s 屏幕尺寸,iPhone 5/5s 屏幕尺寸,iPhone 6/6s 所需的所有工作(加上)屏幕尺寸和 iPad 屏幕尺寸以及适用于上述所有屏幕尺寸的纵向和横向模式(here 是一个很好的概述):好吧,这没什么问题,就去做吧。当然,您随后需要非常仔细地进行测试,并始终保持此列表为最新——在每个应用程序中,您都在使用自己的解决方案。如果那是给你的,那就去做吧。
但大多数人都希望表达自己的想法,而不用纠结于不同屏幕尺寸等问题。您当然可以从一个应用程序中提取您自己的逻辑以在您的所有应用程序中使用它,而这正是自动布局和屏幕尺寸的意义所在。 简而言之,Apple 已经完成了让您的框架设置在不同屏幕上工作的所有工作。你只需要使用他们的新词汇(Constraints)就可以让它发挥作用。
换句话说:它是一个 直接处理屏幕渲染逻辑的抽象层 。如果你不喜欢它,就放手吧。但是,如果您打算开发一些应该适用于不同 iPhone/iPad 代的重要应用程序,并且还计划维护这些应用程序,那么,请学习如何使用自动布局和大小 类。它将为您和所有未来的维护者节省大量的开发时间。
一个好的起点是 raywenderlich.com 上的 docs. Or a tutorial like this one。
Apple 自己对这个困难说了以下内容(在上面链接的文档中):
In many ways, programmatically defining a view’s frame provides the most flexibility and power. When a change occurs, you can literally make any change you want. Yet because you must also manage all the changes yourself, laying out a simple user interface requires a considerable amount of effort to design, debug, and maintain. Creating a truly adaptive user interface increases the difficulty by an order of magnitude.
顺便说一句: 在编程语言方面使用自动布局或框架没有任何区别:Swift 和 Objective-C 都很好地支持这两种方式。看来您还没有找到如何在 Obj-C 中更改框架。 ;)
还有一点: 您还不需要使用 Storyboards 来使用自动布局。您可以从代码中使用自动布局。请参阅文档 here. There's even plenty of frameworks trying to make this easier (the original APIs from Apple tend to be not very pretty in many terms), I can recommend SnapKit.
iOS 基于框架与自动布局
Frame-based
- 通过 (x, y, with, height) 定位。多屏情况下的计算和变体很多
Autoresizing Masks
是基于父视图的视图调整大小行为。增加一些活力
view.autoresizingMask = [.flexibleRightMargin]
Auto Layout
- 使用约束来设置视图之间的关系。视图的框架是根据约束动态计算的
view.translatesAutoresizingMaskIntoConstraints = false // false for autolayout
view.topAnchor.constraint(equalTo: parentView.topAnchor, constant: 16).isActive = true
AutoLayout 在 UITableView
中使用,在 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
中使用 UITableView.automaticDimension
,这让您不必关心视图高度。
当视图已添加到打印视图时也设置约束
parentView.addSubview(view)
view.topAnchor.constraint(equalTo: self.topAnchor, constant: 16).isActive = true
或者你得到
Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors <> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.'