Xamarin.iOS 中的堆栈视图
本文介绍如何在 Xamarin.iOS 应用中使用新的 UIStackView 控件来管理水平或垂直排列的堆栈中的一组子视图。
重要
请注意,虽然 iOS 设计器支持 StackView,但在使用稳定通道时可能会遇到可用性 bug。 切换 beta 或 alpha 通道应该会缓解此问题。 我们决定使用 Xcode 演示本演练,直到在稳定通道中实现所需的修补程序为止。
堆栈视图控件 (UIStackView
) 利用自动布局和大小类的强大功能来管理动态响应 iOS 设备的方向和屏幕大小的子视图堆栈(水平或垂直)。
附加到堆栈视图的所有子视图的布局都基于开发人员定义的属性(如轴、分布、对齐和间距)进行管理:
在 Xamarin.iOS 应用中使用 UIStackView
时,开发人员可以在 iOS 设计器的 Storyboard 内定义子视图,也可以在 C# 代码中添加和删除子视图。
本文档由两部分组成:先是一个快速入门来帮助你实现你的第一个堆栈视图,然后是一些关于其工作原理的更多技术详细信息。
UIStackView 视频
UIStackView 快速入门
在对 UIStackView
控件的快速介绍中,我们将创建一个简单的界面,让用户能够输入 1 到 5 的评分。 我们将使用两个堆栈视图:一个用于在设备的屏幕上垂直排列界面,另一个用于跨屏幕水平排列 1-5 评分图标。
定义 UI
启动新的 Xamarin.iOS 项目,并在 Xcode 的 Interface Builder 中编辑 Main.storyboard 文件。 首先,将单个垂直堆栈视图拖动到视图控制器上:
在特性检查器中,设置以下选项:
其中:
- 轴 – 确定堆栈视图是水平还是垂直排列子视图。
- 对齐 - 控制子视图在堆栈视图中的对齐方式。
- 分布 – 控制子视图在堆栈视图中的大小。
- 间距 – 控制堆栈视图中每个子视图之间的最小空间。
- 相对于基线 - 如果选中,每个子视图的垂直间距将从其基线派生。
- 布局边距相对 – 相对于标准布局边距放置子视图。
使用堆栈视图时,可以将对齐视为子视图的 X 和 Y 位置,将分布视为高度和宽度。
重要
UIStackView
设计为非呈现容器视图,因此,它不会像 UIView
的其他子类一样绘制到画布上。 因此,设置属性(例如 BackgroundColor
或重写 DrawRect
)不会产生视觉效果。
通过添加标签、ImageView、两个按钮和一个水平堆栈视图来继续设置应用界面的布局,使其如下所示:
使用以下选项配置水平堆栈视图:
我们不希望表示评分中的每个“点”的图标在添加到水平堆栈视图时被拉伸,所以我们将“对齐”设置为“居中”,将“分布”设置为“均匀填充”。
最后,连接以下输出口和操作:
通过代码填充 UIStackView
返回到 Visual Studio for Mac,编辑 ViewController.cs 文件并添加以下代码:
public int Rating { get; set;} = 0;
...
partial void IncreaseRating (Foundation.NSObject sender) {
// Maximum of 5 "stars"
if (++Rating > 5 ) {
// Abort
Rating = 5;
return;
}
// Create new rating icon and add it to stack
var icon = new UIImageView (new UIImage("icon.png"));
icon.ContentMode = UIViewContentMode.ScaleAspectFit;
RatingView.AddArrangedSubview(icon);
// Animate stack
UIView.Animate(0.25, ()=>{
// Adjust stack view
RatingView.LayoutIfNeeded();
});
}
partial void DecreaseRating (Foundation.NSObject sender) {
// Minimum of zero "stars"
if (--Rating < 0) {
// Abort
Rating =0;
return;
}
// Get the last subview added
var icon = RatingView.ArrangedSubviews[RatingView.ArrangedSubviews.Length-1];
// Remove from stack and screen
RatingView.RemoveArrangedSubview(icon);
icon.RemoveFromSuperview();
// Animate stack
UIView.Animate(0.25, ()=>{
// Adjust stack view
RatingView.LayoutIfNeeded();
});
}
让我们详细查看一下此代码的几个部分。 首先,我们使用 if
语句来检查是否不超过五颗“星”或小于零。
若要添加新的“星形”,请加载其图像并将其内容模式设置为缩放方面拟合:
var icon = new UIImageView (new UIImage("icon.png"));
icon.ContentMode = UIViewContentMode.ScaleAspectFit;
这会使“星形”图标在添加到堆栈视图时被扭曲。
接下来,我们将新的“星形”图标添加到堆栈视图的子视图集合中:
RatingView.AddArrangedSubview(icon);
你会注意到,我们已将 UIImageView
添加到 UIStackView
的 ArrangedSubviews
属性,而不是添加到 SubView
。 希望堆栈视图控制其布局的任何视图都必须添加到 ArrangedSubviews
属性。
若要从堆栈视图中删除子视图,请先获取要删除的子视图:
var icon = RatingView.ArrangedSubviews[RatingView.ArrangedSubviews.Length-1];
然后,我们需要从 ArrangedSubviews
集合和超级视图中删除它:
// Remove from stack and screen
RatingView.RemoveArrangedSubview(icon);
icon.RemoveFromSuperview();
仅从 ArrangedSubviews
集合中删除子视图会将其从堆栈视图的控件中删除,但不会将其从屏幕中删除。
测试 UI
有了所有必需的 UI 元素和代码后,现在可以运行和测试界面。 显示 UI 时,垂直堆栈视图中的所有元素将从上到下等距排列。
当用户点击“增加评级”按钮时,屏幕上会添加另一个“星号”(最多 5 个):
“星号”将自动居中并均匀分布在水平堆栈视图中。 当用户点击“降低评级”按钮时,将删除“星号”(直到无星号显示)。
堆栈视图详细信息
现在我们已大致了解了什么是 UIStackView
控件及其工作原理,接下来让我们更深入地了解它的一些功能和详细信息。
自动布局和大小类
如上所示,当子视图添加到堆栈视图中时,其布局完全由该堆栈视图使用自动布局和大小类来控制排列的视图的位置和大小。
堆栈视图将集合中的第一个和最后一个子视图固定到垂直堆栈视图的上边缘和下边缘或水平堆栈视图的左边缘和右边缘。 如果将属性 true
设置为 LayoutMarginsRelativeArrangement
,则视图会将子视图固定到相关的边距而不是边缘。
堆栈视图在计算所定义的 Axis
(FillEqually Distribution
除外)的子视图大小时使用子视图的 IntrinsicContentSize
属性。 FillEqually Distribution
调整所有子视图的大小,使其大小相同,从而填充沿 Axis
的堆栈视图。
除 Fill Alignment
外,堆栈视图使用子视图的 IntrinsicContentSize
属性来计算与给定 Axis
垂直的视图大小。 对于 Fill Alignment
,所有子视图的大小都大,以便填充垂直于给定 Axis
的堆栈视图。
定位和调整堆栈视图的大小
虽然堆栈视图完全控制任何子视图的布局(基于 Axis
和 Distribution
等属性),但仍需要使用自动布局和大小类将堆栈视图 (UIStackView
) 定位在其父视图中。
通常,这意味着固定堆栈视图的至少两个边缘以展开和收缩,从而定义其位置。 如果没有任何其他约束,堆栈视图将自动调整大小以适应其所有子视图,如下所示:
- 沿其
Axis
的大小将是所有子视图大小的总和以及每个子视图之间定义的任何空间。 - 如果
LayoutMarginsRelativeArrangement
属性为true
,则堆栈视图大小还将包含边距空间。 - 垂直于
Axis
的大小将设置为集合中最大的子视图。
此外,还可以为堆栈视图的高度和宽度指定约束。 在这种情况下,子视图将布局(大小),以填充由堆栈视图指定的空间,由 Distribution
和 Alignment
属性决定。
如果 BaselineRelativeArrangement
属性为 true
,则子视图将基于第一个或最后一个子视图的基线进行布局,而不是使用顶部下方或中心- Y 位置。 这些计算在堆栈视图的内容上,如下所示:
- 垂直堆栈视图将返回第一个基线的第一个子视图,最后一个子视图返回最后一个子视图。 如果其中任一子视图本身是堆栈视图,则将使用其第一个或最后一个基线。
- 水平堆栈视图将对第一个和最后一个基线使用其最高子视图。 如果最高视图也是堆栈视图,它将使用其最高子视图作为基线。
重要
基线对齐不适用于拉伸或压缩的子视图大小,因为基线将计算到错误的位置。 对于基线对齐,请确保子视图的高度与内部内容视图的高度匹配。
常用堆栈视图使用
有几种布局类型适用于堆栈视图控件。 根据 Apple 的说法,以下是一些更常见的用途:
- 定义沿轴的大小 – 通过将两个边缘固定在堆栈视图的
Axis
和一个相邻边缘来设置位置,堆栈视图将沿轴增长,以适应其子视图定义的空间。 - 定义子视图的位置 – 通过将堆栈视图的相邻边缘固定到父视图的相邻边缘,堆栈视图将在两个维度中增长,以适应其包含子视图。
- 定义堆栈的大小和位置 – 通过将堆栈视图的所有四个边缘固定到父视图,堆栈视图将基于堆栈视图中定义的空间排列子视图。
- 定义轴垂直大小 – 通过将两个边缘固定到堆栈视图的
Axis
和沿轴的一个边缘来设置位置,堆栈视图将垂直于轴,以适应其子视图定义的空间。
管理外观
UIStackView
设计为非呈现容器视图,因此,它不会像 UIView
的其他子类一样绘制到画布上。 设置属性(如 BackgroundColor
或重写 DrawRect
)不会产生视觉效果。
有几个属性控制堆栈视图如何排列其子视图集合:
- 轴 – 确定堆栈视图是水平还是垂直排列子视图。
- 对齐 - 控制子视图在堆栈视图中的对齐方式。
- 分布 – 控制子视图在堆栈视图中的大小。
- 间距 – 控制堆栈视图中每个子视图之间的最小空间。
- 基线相对 – 如果
true
,每个子视图的垂直间距将从其基线派生。 - 布局边距相对 – 相对于标准布局边距放置子视图。
通常,你将使用堆栈视图来排列少量的子视图。 可通过相互嵌套一个或多个堆栈视图来创建更复杂的用户界面(如上述 UIStackView 快速入门中所示)。
可以通过向子视图添加其他约束(例如控制高度或宽度)来进一步微调 UI 外观。 但是,应注意不要包括堆栈视图本身引入的冲突约束。
维护已排列的视图和子视图的一致性
堆栈视图将按照以下规则确保其 ArrangedSubviews
属性始终是其 Subviews
属性的子集:
- 如果子视图添加到
ArrangedSubviews
集合,它将自动添加到Subviews
集合(除非它已经是该集合的一部分)。 - 如果从
Subviews
集合中删除子视图(不再显示),则也会从ArrangedSubviews
集合中删除该子视图。 - 如果从
ArrangedSubviews
集合中删除子视图,不会将其从Subviews
集合中删除。 因此,它将不再由堆栈视图布局,但仍会在屏幕上显示。
ArrangedSubviews
集合始终是 Subview
集合的子集,但每个集合中各个子视图的顺序是独立的,并按以下方式控制:
ArrangedSubviews
集合中子视图的顺序决定了它们在堆栈中的显示顺序。Subview
集合中子视图的顺序决定了其在视图中从后到前的 Z 顺序(或分层)。
动态更改内容
每当添加、移除或隐藏子视图时,堆栈视图将自动调整子视图的布局。 如果调整了堆栈视图的任何属性(如其 Axis
),布局也将进行调整。
布局更改可以通过将更改放置在动画块中进行动画处理,例如:
// Animate stack
UIView.Animate(0.25, ()=>{
// Adjust stack view
RatingView.LayoutIfNeeded();
});
可以使用情节提要中的大小类指定堆栈视图的许多属性。 这些属性将自动进行动画处理,以响应大小或方向更改。
总结
本文介绍了新的 UIStackView
控件(面向 iOS 9),它用于在 Xamarin.iOS 应用中管理水平或垂直排列的堆栈中的一组子视图。
它首先介绍一个使用堆栈视图创建 UI 的简单示例,最后更详细地介绍堆栈视图及其属性和功能。