目录
1.前言
在 iOS 开发中,布局是一个至关重要的部分,而 AutoLayout 是苹果提供的一种强大且灵活的布局系统。AutoLayout 允许开发者通过设置约束(Constraints)来定义视图之间的关系,从而自动适应不同设备的屏幕大小和方向变化。本文将深入探讨 AutoLayout 的基本概念、常用技巧以及实际开发中的一些最佳实践。
一、AutoLayout 基本概念
1.AutoLayout的概念
自动布局根据对这些视图施加的约束,动态计算视图层次结构中所有视图的大小和位置。例如,您可以约束按钮,使其与图像视图水平居中,并且按钮的顶部边缘始终保持在图像底部下方8 点处。如果图像视图的大小或位置发生变化,按钮的位置会自动调整以匹配。
以下面的图片为例,图一为15p的截图,图二为se的截图,两者布局方式是相同的。但是当屏幕的宽度和高度发生变化的时候,不管文字显示的事几行,文字和图片的距离始终为8.
这种效果就可以通过自动布局设置约束来实现。
图1.iphone 15 pm列表显示效果
图2.iphone se列表显示效果
这种基于约束的设计方法允许您构建动态响应内部和外部变化的用户界面。
Apple设备中,引起设备布局变化的情况有内部变化和外部变化,下面我们详细看一下:
1.外部的变化
当您的父视图的大小或形状发生变化时,就会发生外部变化。每次更改时,您都必须更新视图层次结构的布局,以最好地利用可用空间。
以下是一些常见的外部变化来源:
1.用户调整窗口大小(MacOS)
2.用户在 iPad (iOS) 上进入或离开分割视图。
3.设备旋转 (iOS)。
4.活动通话和录音栏出现或消失 (iOS)。
5.您想要支持不同的尺寸类别
6.您想要支持不同的屏幕尺寸
大多数这些更改都可能在运行时发生,并且需要您的应用做出动态响应。其他更改(例如对不同屏幕尺寸的支持)表示应用适应不同的环境。即使屏幕尺寸通常不会在运行时发生变化,创建自适应界面也能让您的应用在 iPhone 4S、iPhone 6 Plus 甚至 iPad 上同样出色地运行。自动布局也是支持 iPad 上的 Slide Over 和 Split Views 的关键组件。
2.内部的变化
当用户界面中的视图或控件的大小发生变化时,就会发生内部变化。
以下是一些常见的内部变化来源:
1.应用程序显示的内容发生变化
2.该应用程序支持国际化
3.该应用程序支持动态类型(iOS)
当您的应用内容发生变化时,新内容可能需要与旧内容不同的布局。这通常发生在显示文本或图像的应用中。例如,新闻应用需要根据各个新闻文章的大小调整其布局。同样,照片拼贴必须处理各种图像尺寸和宽高比。
国际化是让您的应用能够适应不同语言、地区和文化的过程。国际化应用的布局必须考虑到这些差异,并在应用支持的所有语言和地区中正确显示。
国际化对布局有三个主要影响。首先,当你将用户界面翻译成另一种语言时,标签所需的空间大小会有所不同。例如,德语通常比英语需要更多的空间。日语通常需要的空间要少得多。
其次,即使语言不变,用于表示日期和数字的格式也会因地区而异。虽然这些变化通常比语言变化更微妙,但用户界面仍然需要适应尺寸的细微变化。
第三,更改语言不仅会影响文本的大小,还会影响布局的组织。不同的语言使用不同的布局方向。例如,英语使用从左到右的布局方向,而阿拉伯语和希伯来语使用从右到左的布局方向。通常,用户界面元素的顺序应与布局方向相匹配。如果按钮在英语中位于视图的右下角,则在阿拉伯语中它应该位于左下角。
最后,如果您的 iOS 应用支持动态类型,用户可以更改应用中使用的字体大小。这可以更改用户界面中任何文本元素的高度和宽度。如果用户在应用运行时更改字体大小,则字体和布局都必须适应。
3.AutoLayout和基于frame的布局
在AutoLayout之前,我们使用frame布局。我们使用代码设置UI的坐标,让组件自动调整使用外部更改。
当我们的布局非常复杂的时候,我们则需要花费大量的时间去计算UI的坐标,但是frame布局性能较高,学习曲线较低。
而AutoLayout则高度灵活,能够处理各种复杂的布局需求,特别是在需要支持多设备和屏幕旋转的应用中表现出色。
2.不使用约束进行自动布局
UIStackView提供了一种利用自动布局功能而不需要引入约束的简单方法。单个UIStackView定义一行或一列用户界面元素。UIStackView根据其属性对这些元素进行排列。
序号 | 属性 | 作用 |
1 | axis | 定义 UIStackView 子视图的排列方向 |
2 | distribution |
定义 UIStackView 中子视图的分布方式。 |
3 | alignment |
定义 UIStackView 中所有子视图的对齐方式。 |
4 | spacing |
设置 UIStackView 中子视图之间的间距 |
1.xib使用UIStackView
我们可以使用XIB狂苏的创建横向或者纵向排列的UIStackView。
图1.UIStackView控件
要使用UIStackView,请在 Interface Builder 中将垂直或水平UIStackView拖到画布上。然后将内容拖出并放入堆栈中。
如果对象具有固有内容大小,则它会以该大小出现在堆栈中。如果它没有固有内容大小,Interface Builder 会提供默认大小。您可以调整对象的大小,Interface Builder 会添加约束以保持其大小。
要进一步微调布局,您可以使用属性检查器修改堆栈视图的属性。例如,以下示例使用 8 点间距和均匀填充分布。
图2.使用XIB方式设置UIStackView
2.纯代码方式设置UIStackView
除了上述使用XIB拖拽的方式设置UIStackView之外,我们还可以通过代码设置UIStackView。
这里提供了一个纯代码布局的demo。
#import "UIStackViewCodeDemosVC.h"
@interface UIStackViewCodeDemosVC ()
@end
@implementation UIStackViewCodeDemosVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// 创建三个 UIView,并设置背景颜色
UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
UIView *greenView = [[UIView alloc] init];
greenView.backgroundColor = [UIColor greenColor];
UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
// 创建 UIStackView,将三个 UIView 作为 arrangedSubviews
UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:@[redView, greenView, blueView]];
stackView.axis = UILayoutConstraintAxisHorizontal; // 水平方向排列
stackView.alignment = UIStackViewAlignmentFill; // 子视图高度充满
stackView.distribution = UIStackViewDistributionFillEqually; // 子视图等宽
stackView.spacing = 10; // 子视图之间的间距为 10 点
// 禁用 stackView 的自动布局约束
stackView.translatesAutoresizingMaskIntoConstraints = NO;
// 将 stackView 添加到主视图
[self.view addSubview:stackView];
// 使用 Auto Layout 约束 stackView
[NSLayoutConstraint activateConstraints:@[
[stackView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[stackView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:0],
[stackView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:0],
[stackView.heightAnchor constraintEqualToConstant:40],
[redView.heightAnchor constraintEqualToConstant:40],
]];
}
// 按钮点击事件处理
- (void)buttonPressed {
NSLog(@"Button was pressed!");
}
@end
运行之后,效果图如下:
图3.UIStackView纯代码布局
3.AutoLayout中的约束
视图层次结构的布局定义为一系列线性方程。每个约束代表一个方程。您的目标是声明一系列有且只有一个可能解的方程。
以下面的布局为例:
图4.视图之间的关系描述
我们可以这么描述两个控件之间的关系:redView左边距离blueView的右边8像素。
这个描述可以看成一个一元一次方程式,含义为:第一个视图的属性 = 视图的属性的倍数+常量。
1.Auto Layout中的属性
在自动布局中,属性定义可以约束的特征。一般来说,这包括四个边缘(前边缘、后边缘、顶部边缘和底部边缘)以及高度、宽度、垂直和水平中心。文本项还具有一个或多个基线属性。
图5.AutoLayout中的属性
1.边距约束
边距约束(Edge Constraints):定义视图与其父视图或其他视图边缘的距离。包括顶部(Top)、底部(Bottom)、前导(Leading)、尾随(Trailing)等。
比如说我们要做下图6的一个UI效果。橘色的UIView距离父视图的左右下方的距离均为0,顶部位于安全区域下方。
图6.边缘约束
如果我们使用XIB,我们首先可以拖拽一个UIView,选中UIView的同时按住control键,出现箭头的时候,把我们把箭头指向UIView或者SafeArea然后松开即可。
图6.XIB设置边距
这个时候会有一个弹窗,我们在这里勾选一下左右下面边距即可。
图7.XIB设置约束
写这篇博客的时候,我使用的Xcode版本是 15.4,不同版本的Xcode可能会略有不同,这里弹窗中仅展示的相对SafeArea的约束,当然这里设置之后,我们还可以通过右边的属性检查器去修改。
我们可以选中要修改的约束,然后在右侧的属性检查器修改即可。
图8.修改约束
当然我们还可以通过纯代码的方式这是约束。
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// 创建一个 yellowView
UIView *yellowView = [[UIView alloc] init];
yellowView.backgroundColor = [UIColor orangeColor];
yellowView.translatesAutoresizingMaskIntoConstraints = NO; // 禁用 autoresizing mask
// 将 yellowView 添加到父视图中
[self.view addSubview:yellowView];
// 获取视图的 safeAreaLayoutGuide
UILayoutGuide *safeArea = self.view.safeAreaLayoutGuide;
// 设置 yellowView 的约束
[NSLayoutConstraint activateConstraints:@[
[yellowView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], // 左边距为 0
[yellowView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], // 右边距为 0
[yellowView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor], // 底部距为 0
[yellowView.topAnchor constraintEqualToAnchor:safeArea.topAnchor] // 顶部与安全区域顶部对齐,距为 0
]];
}
2.宽度和高度约束
宽度和高度约束(Width and Height Constraints)定义视图的固定宽度和高度,或相对于其他视图的宽高。
例如我们设置一个宽100,高50的居中显示的红色View。
如果使用XIB设置宽高,点击右侧的按钮。
图9.XIB设置宽高约束
然后在通过跟设置边缘约束相同的方式,设置居中显示,就可以实现居中显示的redView。
纯代码方式核心代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// 创建一个 yellowView
UIView * redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
redView.translatesAutoresizingMaskIntoConstraints = NO; // 禁用 autoresizing mask
// 将 yellowView 添加到父视图中
[self.view addSubview:redView];
// 获取视图的 safeAreaLayoutGuide
UILayoutGuide *safeArea = self.view.safeAreaLayoutGuide;
// 设置 yellowView 的约束
[NSLayoutConstraint activateConstraints:@[
[redView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor], // 左边距为 0
[redView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor], // 右边距为 0
[redView.widthAnchor constraintEqualToConstant:100], // 设置视图宽度 100
[redView.heightAnchor constraintEqualToConstant:50] // 设置视图高度 50
]];
}
3.比例约束
(Aspect Ratio Constraints)维持视图的宽高比,例如将一个视图设置为 1:1 的正方形。
使用XIB设置约束的时候,我们点击右下角的ratio即可设置宽高比例。
图10.XIB设置宽高比例
当然我们还可以通过代码设置UIView宽高比。
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// 创建一个 yellowView
UIView *yellowView = [[UIView alloc] init];
yellowView.backgroundColor = [UIColor redColor];
yellowView.translatesAutoresizingMaskIntoConstraints = NO; // 禁用 autoresizing mask
// 将 yellowView 添加到父视图中
[self.view addSubview:yellowView];
// 设置 yellowView 的约束
[NSLayoutConstraint activateConstraints:@[
// 水平方向居中对齐
[yellowView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
// 垂直方向居中对齐
[yellowView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
// 宽度约束(可以设置为某个具体数值,也可以根据父视图的宽度设置)
[yellowView.widthAnchor constraintEqualToConstant:320],
// 高度与宽度的比例为16:9
[yellowView.heightAnchor constraintEqualToAnchor:yellowView.widthAnchor multiplier:9.0/16.0]
]];
}
4.对齐约束
对齐约束(Align Constraints)用于将一个视图与另一个视图或其父视图对齐。常见的对齐方式包括居中对齐、水平对齐、垂直对齐等。
以下图为例,我们可以按住command键,同时选中三个视图,然后点击下入所示的按钮,设置距离底部对齐。
图11.设置三个组件底部对齐
图12.设置多个控件的对齐方式
当然也可以使用代码来实现,这里就不提供纯代码方式了,建议自己动手实现一下。
5.相对位置约束
通过相对位置约束,可以定义一个视图相对于另一个视图的位置。例如,可以设置一个视图位于另一个视图的下方、右侧等。
例如我们要设置红色视图距离绿色视图底部的距离为20.我们安装control键,选中红色视图之后,会出现一个箭头,当箭头指向绿色视图的时候,我们松开手指,在弹出的窗口选择top,然后在右侧的属性检查器修改相应的参数即可。
图13.设置相对位置布局
图14.设置约束
图15.修改约束
6.基线约束
基线约束用于对齐文本元素的基线,确保不同大小或字体的文本在视觉上对齐。
以下图为例,左边是17号文字,右边是13号文字,我们可以设置其基线对齐。按住control键,拖转到另一个视图上,然后设置baseline,之后我们可以在右侧的面板修改参数。
图16.两种不同文字大小的UILabel
2.Auto Layout中的属性之间的关系
通过上文章的描述,我们理解了AutoLayout的属性。那么描述属性之间的关系有三种。
1.等于(Equal To, =):两个属性相等,如 view1.width = view2.width。
2.大于等于(Greater Than or Equal To, >=):一个属性大于或等于另一个属性,如 view1.height >= view2.height。
3.小于等于(Less Than or Equal To, <=):一个属性小于或等于另一个属性,如 view1.leading <= view2.leading。
在自动布局中,属性定义可以约束的特征。一般来说,这包括四个边缘(前边缘、后边缘、顶部边缘和底部边缘)以及高度、宽度、垂直和水平中心。文本项还具有一个或多个基线属性。
3.Multiplier(乘数)
Multiplier 用于定义两个属性之间的比例关系。例如,view1.width = 0.5 * view2.width 表示 view1 的宽度是 view2 的一半。
4. Constant(常量)
Constant 是指在属性关系中添加或减去的固定值。例如,view1.top = view2.bottom + 20 表示 view1 的顶部距离 view2 的底部 20 点。
5. Priority(优先级)
约束优先级用于解决布局冲突。优先级范围从 1 到 1000,1000 表示最高优先级。可以通过设置优先级来告诉 Auto Layout 哪些约束更重要。
受制于文章篇幅限制,下一篇文章在这里。