SwiftUI 6.0(iOS 18)新容器视图修改器漫谈

概览

本届 WWDC 2024 观影正如火如荼的进行中,一片鸟语花香、枝繁叶茂的苹果树上不时结出几颗令人垂涎欲滴的美味苹果让秃头码农们欲罢不能。

如您所愿,在界面布局"利器" SwiftUI 这根蔓藤也长出不少喜人的果实,其中在 iOS 18.0 中新添加的容器视图修改器大家一定不能错过。

在本篇博文中,您将学到如下内容:

  1. 探囊取物:获取容器子视图
  2. 聚沙成塔:重新组织容器子视图

SwiftUI 6.0 新容器视图修改器让我们得以分解原本"铁板一块"的"黑箱"视图,打开无限可能。

无需等待,让我们马上开始探寻之旅吧!

Let's go!!!;)


1. 探囊取物:获取容器子视图

在 SwiftUI 6.0(iOS 18)之前,如果我们希望让自定义容器视图处理布局排版的细节则需要知道数据集,并根据数据集中每个单独元素构建对应的容器子视图:

swift 复制代码
import SwiftUI

struct MyContainerView<Content: View>: View {
    var items: [Int]
    @ViewBuilder var itemView: (Int) -> Content
    
    var body: some View {
        List {
            ForEach(items, id: \.self) { item in
                itemView(item)
            }
        }
    }
}

struct ContentView: View {
    var body: some View {
        MyContainerView(items: Array(1...10)) { item in
            Text("Item \(item)")
                .font(.title)
                .padding()
        }
    }
}

如上代码所示,我们将容器数据集合 items 和单个数据对应的子视图闭包 itemView() 从父视图传入到了 MyContainerView 中。

不过在某些情况下,我们希望在父视图中更灵活的创建容器子视图,比如以静态与动态相结合的方式:

swift 复制代码
struct ContentView: View {
    var body: some View {
        MyContainerView {
            Text("Item Header")
            
            Text("Item Subheader")
            
            ForEach(1...10, id: \.self) { item in
                Text("Item \(item)")
            }
            
            Text("Item Tail")
        }
    }
}

如上代码所示,我们试图将 3 个静态和 10 个动态产生的子视图和睦融洽的一起融入到容器视图 MyContainerView 中。但不幸的是,这在 SwiftUI 6.0 之前几乎是不可能完成的任务。


当然,如果我们巧妙运用一些 Swift Mirror "黑魔法"的话也不是绝对不可能。想要进一步了解 Mirror 黑魔法解决之道的小伙伴们,请移步如下链接观赏精彩的内容:


究其原因则是因为:在 SwiftUI 6.0 之前我们无法探查一个 View 的内部结构,它对我们来说就是一个彻头彻尾的"黑盒"视图。

不过从 SwiftUI 6.0(iOS 18)开始情况有了改观,苹果新增了若干容器视图修改器为我们排愁解忧。其中 ForEach(subviewOf:) 修改器方法就是这里我们所需要的那个"救世主"。

利用 ForEach(subviewOf:) 方法我们可以将 MyContainerView 容器视图修改为如下模样:

swift 复制代码
struct MyContainerView<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        List {
            ForEach(subviewOf: content) {subview in
                                
                subview
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(.green.gradient)
            }
        }
    }
}

从上面修改后的代码中可以看到:现在我们只需专注于子视图本身,而无需再操心对应的子元素 Item 了。

除了 ForEach(subviewOf:) 以外,SwiftUI 6.0 还新增了一个类似的 ForEach(sectionOf: ) 修改器方法,如果小伙伴们希望自己的容器支持 Section 布局则会寻求它的鼎力相助哦。

2. 聚沙成塔:重新组织容器子视图

在聊完了将一整块黑盒视图从布局上分解为各个独立的子视图后,我们再来看看容器视图的另一种操作:将容器子视图重新"恣意"组合在一起。

假如我们希望自己的容器视图能够进一步根据子视图的数量或其它特定条件来布局界面,那么新的 Group(subviewsOf:) 修改器你绝对不能错过:

Group(subviewsOf: content) 方法让我们可以从整个容器的所有子视图而不是单个子视图来考虑布局大计:

swift 复制代码
struct HyListView<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        VStack {
            Group(subviewsOf: content) { subviews in
                                
                if subviews.isEmpty {
                    Text("🐼No\nContent")
                        .font(.system(size: 100))
                        .padding()
                } else {
                    if let first = subviews.first {
                        first
                            .font(.largeTitle.weight(.heavy))
                            .background(
                                Circle()
                                    .foregroundStyle(.blue.gradient)
                                    .frame(width: 150, height: 150))
                    }
                    
                    if subviews.count >= 3 {
                       HStack {
                           subviews[1]
                           subviews[2]
                       }
                       .font(.title)
                       .foregroundStyle(.white)
                       .padding()
                       .background(.green)
                       
                        
                       if subviews.count > 3 {
                           
                           List {
                               subviews[3...]
                                   .frame(maxWidth: .infinity)
                                   .padding()
                                   .background(.yellow.gradient)
                           }
                           .listStyle(.plain)
                           .font(.title3.weight(.black))
                           .foregroundStyle(.red)
                           .padding()
                           
                       }
                        
                   }
                }
            }
            .transition(.slide)
        }
    }
}

struct ContentView: View {
    
    @State var count = 10.0
    
    var items: [Int] {
        if count == 0 {
            []
        } else {
            Array(0...Int(count))
        }
    }
    
    var body: some View {
        VStack {
            HyListView {
                ForEach(items, id: \.self) { i in
                    Text("Item \(i)")
                }
            }
            .animation(.bouncy, value: count)
            
            Spacer()
            
            HStack {
                Text("\(Int(count))")
                    .fontWeight(.heavy)
                Slider(value: $count, in: 0...20.0, label: {}, minimumValueLabel: {
                    Text("0")
                }, maximumValueLabel: {
                    Text("20")
                })
                .foregroundStyle(.gray)
            }
            .padding()
        }
    }
}

在上面代码中,我们创建了自己的 HyListView 容器视图,并且根据实际子视图的数量利用 Group(subviewsOf: content) 修改器方法来决定到底如何将它们"浑然天成"的组合在一起。

从实际运行效果可以看到,我们动态的根据容器内部的子视图来决定到底如何布局容器本身,再辅以动画整个效果简直 Nice 的不要不要的:

现在小伙伴们手握 SwiftUI 6.0 中新的容器视图修改"利器",在 App 的开发中是不是愈发感觉神采奕奕、容光焕发了呢?棒棒哒!

总结

在本篇博文中,我们讨论了 WWDC24 里 SwiftUI 6.0(iOS 18)中最新的容器视图修改器,并用简单的示例代码让小伙伴们豁然开朗!

感谢观赏,再会!8-)

相关推荐
游戏开发爱好者89 分钟前
TCP 抓包分析:tcp抓包工具、 iOS/HTTPS 流量解析全流程
网络协议·tcp/ip·ios·小程序·https·uni-app·iphone
天堂罗恋歌1 小时前
CocoaPods 安装 Masonry 库时出现的网络连接问题处理
ios·iphone·xcode·cocoapods·app store
2501_916008891 小时前
iOS 26 软件性能测试全流程,启动渲染资源压力对比与优化策略
android·macos·ios·小程序·uni-app·cocoa·iphone
00后程序员张2 小时前
iOS 26 兼容测试实战,机型兼容、SwiftUI 兼容性改动
android·ios·小程序·uni-app·swiftui·cocoa·iphone
2501_915106323 小时前
iOS 可分发是已经上架了吗?深入解析应用分发状态、ipa 文件上传、TestFlight 测试与 App Store 审核流程
android·ios·小程序·https·uni-app·iphone·webview
2501_9160074711 小时前
HTTPS 抓包乱码怎么办?原因剖析、排查步骤与实战工具对策(HTTPS 抓包乱码、gzipbrotli、TLS 解密、iOS 抓包)
android·ios·小程序·https·uni-app·iphone·webview
2501_9160088917 小时前
HTTPS 双向认证抓包实战,原理、难点、工具与可操作的排查流程
网络协议·http·ios·小程序·https·uni-app·iphone
2501_9151063217 小时前
HTTPS 能抓包吗?实战答案与逐步可行方案(HTTPS 抓包原理、证书Pinning双向认证应对、工具对比)
网络协议·http·ios·小程序·https·uni-app·iphone
游戏开发爱好者817 小时前
App HTTPS 抓包实战,原理、常见问题与可行工具路线(开发 测试 安全 角度)
网络协议·安全·ios·小程序·https·uni-app·iphone
2501_9151063217 小时前
App HTTPS 抓包实战指南,原理、常见阻碍、逐步排查与工具组合
网络协议·http·ios·小程序·https·uni-app·iphone