文章

创建高性能的可滚动 Stack

通过滚动视图、Stack 视图和惰性 Stack 高效地显示大量重复的视图。

概览

你的 App 需要在容器视图中显示的数据经常会超过设备屏幕上容许的空间。对于重复视图或视图组,水平 Stack 和垂直 Stack 是很好的解决方案,但它们没有内建的滚动机制。你可以通过将 Stack 包装在 ScrollView (英文) 中来添加滚动,并在出现性能问题时切换到惰性 Stack。

在可滚动的容器中显示视图组

实施重复视图或视图组很简单,只需将它们包装在 ScrollView (英文) 内的 HStack (英文)VStack (英文) 中即可。


ScrollView(.horizontal) {
    HStack {
        ProfileView()
        ProfileView()
        ProfileView()
        ProfileView()
        ProfileView()
    }
}
.frame(maxWidth: 500)

如果上述示例代码中的 ProfileView 具有 200 x 200 点的固有内容大小,则 frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:) (英文) 视图修饰符将 500 点的最大宽度应用于 ScrollView (英文) 会导致 Stack 在其内部滚动。

五个个人资料视图成一排显示在 Stack 视图中。滚动视图按照最大宽度修剪其内容,从而导致 Stack 中的最后两个半个人资料视图超出了视窗范围。

有关如何使用 Stack 将视图组合在一起的介绍,请参阅“使用 Stack 视图构建布局”。

为你的数据创建重复视图

使用 ForEach (英文) 来为你 App 中的数据创建重复视图。从 profiles 数组中的一个个人资料数据列表中,在 HStack (英文) 内使用 ForEach (英文) 为数组中的每个元素创建一个 ProfileView


ScrollView(.horizontal) {
    HStack {
        ForEach(profiles) { profile in
            ProfileView(profile: profile)
        }
    }
}
.frame(maxWidth: 500)

考虑对大量视图使用惰性堆叠

HStack (英文)VStack (英文)ZStack (英文) 三个标准 Stack 视图在显示时都会载入它们的所含视图层次结构,而一次性载入大量视图会导致运行时性能变慢。

在以上示例中,ProfileView 是一个包含嵌套 Stack 视图、文本标签和图像视图的复合视图。一次性载入大量个人资料会导致明显的速度变慢。

随着 Stack 中的视图数量增加,请考虑使用 LazyHStack (英文)LazyVStack (英文) 代替 HStack (英文)VStack (英文)。惰性 Stack 按需载入和渲染子视图,从而在载入大量子视图时可提供显著的性能提升。

示意图显示了一个滚动视图容器内的惰性 Stack 视图。载入的视图在中间的视口中可见,尚未载入的视图在右侧等待处理。

Stack 视图和惰性 Stack 具有相似的功能,可能给人感觉它们能够互换,但是,它们在不同情况下有各自的优势。Stack 视图会一次性载入所有子视图,布局性能快且可靠,因为系统在载入每个子视图时就知道它们的大小和形状。惰性 Stack 会为了性能而损失一定程度的布局正确性,原因是系统仅在子视图可见时才会计算它们的几何结构。

在选择要使用的 Stack 视图类型时,始终先选择标准 Stack 视图,并且仅在对代码进行性能分析后证明值得提升性能时才切换到惰性 Stack。

进行性能分析以发现性能问题

考虑要使用哪种 Stack 时,请使用 Instruments 工具对你的 App进行性能分析,以确定用户界面代码中将有大量视图载入 Stack 的区域。

若要对 SwiftUI 视图载入进行性能分析,请打开 Instruments 工具,方法是在 Xcode“Product”(产品) 菜单中选择“Profile”(性能分析),然后选取 SwiftUI 性能分析模板。该模板将载入四个 instrument:View Body、View Properties、Core Animation Commits 和 Time Profiler。这些 instrument 的组合为你寻找加快 App 性能的机会提供了一个不错的起点。

来自 Instruments 工具的截屏,显示了大量视图一次性载入。

对上述代码进行性能分析时,View Body instrument 显示 1,000 个 ProfileView 实例同时作为 HStack (英文) 载入内存。随着系统载入每个个人资料,你还会看到相同数量的 Image (英文) 视图载入。

在此情况下,解决方案是将 HStack (英文) 替换为 LazyHStack (英文),如以下代码所示:


ScrollView(.horizontal) {
    LazyHStack {
        ForEach(profiles) { profile in
            ProfileView(profile: profile)
        }
    }
}
.frame(maxWidth: 500)

运行另一次跟踪会显示最初载入的视图数量急剧下降,因为只有四个启动的 ProfileView 实例可见。你还可以在“Total Duration”(总时间长度) 列中看到相应的下降。

来自 Instruments 工具的截屏,显示了少量视图载入。

有关使用 Instruments 工具的更多信息,请参阅提升 App 的性能

另请参阅

惰性 Stack