SwiftUI的ViewBuilder进阶

所属分类:ios | 发布于 2024-06-14 18:43:43

目的是要自定义一个结构体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")
}

文哥博客(https://wenge365.com)属于文野个人博客,欢迎浏览使用

联系方式:qq:52292959 邮箱:52292959@qq.com

备案号:粤ICP备18108585号 友情链接