SwiftUI中的@ViewBuilder

什么是@ViewBuilder?

从字面意思去理解 ViewBuilder 就是视图构建,其主要使用场景就是构建视图。
可以通过ViewBuilder把许多具体相同特点的View封装起来,并且分离逻辑代码和视图,提升代码的可复用性,并增强可读性。
看了下面的例子你可能就会对@ViewBuilder的使用有一个更好的理解。

如果我们想要设置一个Text的背景为红色,圆角为5。我们可能这样去写

Text("Liaoworking")
.background(Color.red)
.cornerRadius(5)

后来产品加需求了: 图片也是红色的背景,圆角为5:

Image("Liaoworking")
.background(Color.red)
.cornerRadius(5)

再后来产品又加需求:一些自定义视图也是红色的背景,圆角为5:
这里假设我们的自定义视图类为MyView

MyView()
.background(Color.red)
.cornerRadius(5)

聪明的你肯定想到了用一个View的extension来统一处理。

extension View {
    func addRedBGWithRoundCorner() -> some View {
        self
        .background(Color.red)
        .cornerRadius(5)
    }
}

//调用:
Text("Liaoworking").addRedBGWithRoundCorner()

这样做的确可以达到相同的效果,而且代码也会简洁不少。

这个时候你还可以用@ViewBuilder注解来创建你的自定义视图达到相同的效果:

struct RedBackgroundAndCornerView<Content: View>: View {
    let content: Content

    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }

    var body: some View {
        content
            .background(Color.red)
            .cornerRadius(5)
    }
}

使用方法如下:

RedBackgroundAndCornerView {
    Text("liaoworking")
}

两种封装的最后效果都是一样的,你可能会觉得,View的extension封装要比@ViewBuilder 封装 好太多。代码都少写很多。

但是突然有一天,你的产品经理决定再加一个功能,上面的这些视图点击后都自动隐藏,你会怎么写?

@State var needHideText: Bool = false

Text("Liaoworking")
    .addRedBGWithRoundCorner()
    .opacity(needHideText ? 0.0 : 1.0)
    .onTapGesture {
        self.needHideText = true
    }

然后到了Image,还有MyView, 对于每一个需要隐藏的对象,你都得创建一个类似于needHideText的变量来控制显示隐藏逻辑。 到最后你的重复代码可能会越来越多。

因为extension无法去存储控制隐藏逻辑的变量,这个时候@ViewBuilder的先天优势马上就体现出来了。

我们只需要将逻辑代码在@ViewBuilder中写一次,所有的View就具有了相同的特性。

struct RedBackgroundAndCornerView<Content: View>: View {
    let content: Content
    @State var needHidden: Bool = false

    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }

    var body: some View {
        content
            .background(Color.red)
            .cornerRadius(5)
            .opacity(needHidden ? 0.0 : 1.0)
            .onTapGesture {
                self.needHidden = true
        }
    }
}

// 所有被RedBackgroundAndCornerView包裹的View就都具有了点击后隐藏的功能了。

RedBackgroundAndCornerView {
    Text("liaoworking")
}

RedBackgroundAndCornerView {
    Image("liaoworking")
}

RedBackgroundAndCornerView {
    MyView("liaoworking")
}

总结:

@ViewBuilder是一个封装可复用view逻辑的利器。它最大的好处就是把你逻辑代码和你的视图剥离开。让代码的可维护性和易读性有很大提升。我在之前的项目里一开始写过很多垃圾代码,后来知道了@ViewBuilder,这的确在对相同逻辑View的封装和使用上有了很大的便捷。