SwiftUI的ViewBuilder进阶
所属分类:ios | 发布于 2024-06-14
目的是要自定义一个结构体ModalPlus,这个结构体的功能是可以自定义Header、Body和Footer,如图是这样的效果
之前学过ViewBuilder,以为实现起来很简单,夸夸一顿写,写完发现不是那么回事。
先来一个自以为的写法
import SwiftUI
struct ModalPlus<Content: View, Header: View, Footer: View>: View {
private let content: Content
private var header: Header? = nil
private var footer: Footer? = nil
init(@ViewBuilder content: () -> Content, @ViewBuilder header: () -> Header, @ViewBuilder footer: () -> Footer) {
self.content = content()
self.header = header()
self.footer = footer()
}
init(@ViewBuilder content: () -> Content, @ViewBuilder header: () -> Header) {
self.content = content()
self.header = header()
}
init(@ViewBuilder content: () -> Content, @ViewBuilder footer: () -> Footer) {
self.content = content()
self.footer = footer()
}
var body: some View {
VStack(spacing: 0) {
if let header = header {
header
}
content
if let footer = footer {
footer
}
}
}
}
来,使用一下
报错了,如果没有header或者没有footer,就会报错,因为没有的那个参数的类型不能确定,泛型这里编译通不过。
想了想SwiftUI的Section就有这样的效果,它为什么可以,研究它去,实际上最后这个ModalPlus没用上,使用Section就能解决问题。
最终实现
看SwiftUI的Section源码有点费劲,貌似只能看到定义,不能看到实现,模仿它,于是就有了下面的实现
1、定义一个ModalPlus
struct ModalPlus<Content: View, Header: View, Footer: View>: View {
private let content: Content
private var header: Header? = nil
private var footer: Footer? = nil
var body: some View {
VStack(spacing: 0) {
if let header = header {
header
.background(Color.green)
}
content
if let footer = footer {
footer
}
}
}
}
上面的定义有一个特点,没有构造方法。
2、扩展ModelPlus(content:, header:, footer:)构造函数
extension ModalPlus where Content: View, Header: View, Footer: View {
init(
@ViewBuilder content: () -> Content,
@ViewBuilder header: () -> Header,
@ViewBuilder footer: () -> Footer
) {
self.content = content()
self.header = header()
self.footer = footer()
}
}
3、扩展ModelPlus(content:, header:)构造函数
extension ModalPlus where Content: View, Header: View, Footer == EmptyView {
init(
@ViewBuilder content: () -> Content,
@ViewBuilder header: () -> Header
) {
self.content = content()
self.header = header()
}
}
4、扩展ModelPlus(content:, footer:)构造函数
extension ModalPlus where Content: View, Header == EmptyView, Footer : View {
init(
@ViewBuilder content: () -> Content,
@ViewBuilder footer: () -> Footer
) {
self.content = content()
self.footer = footer()
}
}
5、大功告成,按需调用
ModalPlus {
Text("Hello Swift")
} header: {
Image(systemName: "heart.fill")
} footer: {
Text("Hello Footer")
}
ModalPlus {
Text("Hello Swift")
} header: {
Image(systemName: "heart.fill")
}