
概述
我们在之前多篇博文中已经介绍过 SwiftUI 6.0(iOS 18)新增的自定义容器布局机制。现在,如何利用它们对容器内容进行"探囊取物"和"聚沙成塔",我们已然胸有成竹了。

然而,除了上述鬼工雷斧般的新技巧之外,SwiftUI 6.0 其实还提供了能更进一步增加容器布局自由度的新利器:自定义容器值(Container Values)。
在本篇博文中,您将学到如下内容:
- 自定义容器值如何让容器布局更加无拘无束?
- 水到渠成:在 Section 上应用自定义容器值
相信 SwiftUI 6.0 中全新的自定义容器值能够让容器布局更加"脱胎换骨"、灵动自由。
那还等什么呢?让我们继续前一篇的容器大冒险吧!Let's go!!!;)
4. 自定义容器值如何让容器布局更加无拘无束?
我们首先需要定义自己的 Container Values。正如上篇博文所说过的,这可以通过 @Entry 宏来轻松完成:
swift
extension ContainerValues {
@Entry var isNeedHighlightPrefix = false
}
为了方便起见,我们顺手在视图扩展中创建一个 highlightPrefix() 辅助方法:
swift
extension View {
func highlightPrefix(_ enable: Bool = true) -> some View {
containerValue(\.isNeedHighlightPrefix, enable)
}
}
接着,我们率先在 NiceListContainer 容器 body 中对醒目显示做出支持:
swift
struct NiceListContainer<Content: View>: View {
@ViewBuilder var content: Content
private var hightlight: some View {
RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.red.gradient)
.frame(width: 5.0)
.padding(.vertical)
.shadow(radius: 3.0)
}
var body: some View {
List {
ForEach(subviews: content) { subview in
HStack {
if subview.containerValues.isNeedHighlightPrefix {
hightlight
}
subview
}
}
}
}
}
可以看到上面代码其实很简单:我们通过容器子视图的 containerValues 访问了它的 isNeedHighlightPrefix 自定义容器值,容易的简直不要不要的。
最后,我们只需在主视图中为所有必要的容器元素应用醒目显示效果即可:
swift
struct ContentView: View {
var body: some View {
NiceListContainer {
Group {
Text("Hello")
.foregroundStyle(.green)
.highlightPrefix()
Text("World")
.foregroundStyle(.red)
.highlightPrefix()
Text("大熊猫侯佩")
.foregroundStyle(.brown)
.font(.system(size: 55, weight: .black))
Image(systemName: "globe.europe.africa")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundStyle(.orange.gradient)
HStack {
Text("战斗力")
Slider(value: .constant(10))
.padding()
}
.tint(.pink)
.highlightPrefix()
}
.font(.title.weight(.heavy))
}
}
}
编译在预览中看一下成果吧:

正是我们想要的!棒棒哒!💯
5. 水到渠成:在 Section 上应用自定义容器值
除了在子视图上应用自定义容器值以外,我们还可以同样在容器的段(Section)上应用它们。
也就是说:除了在 SubView 以外,我们在 SectionConfiguration 中也可以通过 containerValues 属性访问自定义容器值。
假设在容器中有若干个 Section,我们需要将某些 Section 置顶显示,其余的段则顺其自然。和前面类似,我们需要让容器的使用者来决定哪些 Section 放在顶部(需要注意的是:置顶 Section 出现的位置不一定在 @ViewBuilder 闭包代码的前面)。
首先,新建段置顶功能对应的自定义容器值和视图扩展:
swift
extension ContainerValues {
@Entry var isOnTheTop = false
}
extension View {
func onTheTop(_ enable: Bool = true) -> some View {
containerValue(\.isOnTheTop, enable)
}
}
接着,实现我们 NiceListContainer 容器的主体布局:
swift
struct NiceListContainer<Content: View>: View {
@ViewBuilder var content: Content
private var hightlight: some View {
RoundedRectangle(cornerRadius: 8)
.foregroundStyle(.red.gradient)
.frame(width: 5.0)
.padding(.vertical)
.shadow(radius: 3.0)
}
var body: some View {
List {
Group(sections: content) { sections in
let onTheTops = sections.filter(\.containerValues.isOnTheTop)
let others = sections.filter { !$0.containerValues.isOnTheTop }
if !onTheTops.isEmpty {
ForEach(onTheTops) { section in
Section {
HStack {
Image(systemName: "star")
.foregroundStyle(.yellow.gradient)
.font(.largeTitle)
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack {
ForEach(subviews: section.content) { subview in
HStack {
if subview.containerValues.isNeedHighlightPrefix {
hightlight
}
subview
}
}
}
}
}
} header: {
if let header = section.header.first {
HStack {
Image(systemName: "teddybear.fill")
.font(.largeTitle)
.foregroundStyle(.cyan)
header
}
}
}
}
}
if !others.isEmpty {
ForEach(others) { section in
Section {
ForEach(subviews: section.content) { subview in
HStack {
if subview.containerValues.isNeedHighlightPrefix {
hightlight
}
subview
}
}
} header: {
if let header = section.header.first {
header
}
}
}
}
}
}
}
}
在上面的代码中,我们通过检查容器中段的 containerValues.isOnTheTop 容器值,过滤出所有需要显示在顶部和所有其它的 Section。注意,在 NiceListContainer 容器中我们仍然同时支持子视图上的 isNeedHighlightPrefix 容器值。
最后,我们只需在想要置顶的 Section 上应用 onTheTop() 修改器即可:
swift
struct ContentView: View {
var body: some View {
NiceListContainer {
Group {
Section("欢迎语") {
Text("Hello")
.foregroundStyle(.green)
.highlightPrefix()
Text("World")
.foregroundStyle(.red)
.highlightPrefix()
}
Section("主人入住") {
Group {
Text("大熊猫侯佩")
Text("🐼 Hopy")
}
.foregroundStyle(.brown)
.font(.system(size: 55, weight: .black))
.highlightPrefix()
}
.onTheTop()
Section("图片") {
Image(systemName: "globe.europe.africa")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundStyle(.orange.gradient)
}
Section("其它") {
HStack {
Text("战斗力")
Slider(value: .constant(10))
.padding()
}
.tint(.pink)
.highlightPrefix()
}
}
.font(.title.weight(.heavy))
}
}
}
如上代码所示:现在我们在需要顶部显示的 Section 上(可以不止一个)应用了 onTheTop() 置顶修改器,并在 Section 中的某些子视图上应用了之前的醒目显示效果。
运行代码,在预览中见证一下我们美美哒的成果吧:

至此,我们彻底掌握了 SwiftUI 6.0 中定制容器元素的解析与重组,并利用自定义容器值让容器布局更加随心所欲、逍遥自在!棒棒哒!💯
总结
在本篇博文中,我们补全了 SwiftUI 6.0(iOS 18)定制容器的最后一块拼图:自定义容器值(Container Values),并利用它们让容器布局自由度更进一步,小伙伴们值得拥有!
感谢观赏,再会啦!8-)