我们来看一段简单的SwiftUI声明
var body = List {
Text("1")
Text("2")
}
看到这个代码,我一直有个问题,这个代码怎么通过Swift编译的? 为什么有2个返回值。
查看方法声明可以明白
@MainActor public init(@ViewBuilder content: () -> Content)
是一个ViewBuilder类型的闭包。
{
Text("1")
Text("2")
}
再进一步可以发现ViewBuilder被声明成了resultBuilder
@resultBuilder public struct ViewBuilder
这时候,我们就需要好好好理解什么是resultBuilder
ResultBuilder
查看文档可知,ResultBuilder于Swift5.4推出。
源于Swift5.1 Function Builder。
完全依赖Swift Compiler的功劳,我们上面的代码会在Compiler的作用下,转换为:
var body = List {
let _a = Text("1")
let _b = Text("2")
return ViewBuilder.buildBlock(_a, _b)
}
这样看起来是不是就舒服很多了。
但是一个ViewBuilder肯定不是仅仅支持一个多语句的封装返回,肯定还是需要支持一些逻辑控制语句的,那ViewBuilder里面其他那些功能呢?
再回答这个问题之前,我们要来看下ResultBuilder能处理的事情。
ResultBuilder 功能罗列
resultBuilder支持的所有功能
// 1. buildBlock()
let statementBlock = {
var someVal = 5 // 2. nothing
"expression statement" // 3. buildExpression()
if someVal == 5 { // 4. buildOptional()
}
if someVal == 5 { // 5. buildEither
} else if someVal == 6 {
} else {
}
for i in 0...10 { // 6.buildArray
"\(i)"
}
if #available(macOS 13.0, iOS 16.0, *) { // 7. buildLimitedAvailability
}
// 8. 最终针对BuildBlock可以再次封装 buildFinalResult
}
再理解完ResutBuilder之后,我们来看下ViewBuilder,我们遇到的一些奇怪的问题。
SwiftUI问题分析
Extra argument in call
List {
Text("1")
Text("2")
Text("3")
Text("4")
Text("5")
Text("6")
Text("7")
Text("8")
Text("9")
Text("10")
Text("11") // Extra argument in call
}
简单分析一下 上面的语句在Compiler会被转换成
return ViewBuilder.buildBlock(xxxx)
我们再看一下ViewBuilder的实现。
我们可以发现buildBlock最多接收10个参数,所以....你只能入参这么多。
处理方法也很简单,可以把多个Text封装在一起。
Closure containing control flow statement cannot be used with result builder 'ViewBuilder'
List {
for i in 0...10 {
Text("\(i)")
}
}
我只想说,为什么一个for...in都实现不了?????
for in需要viewBuilder实现buildArray方法,那看这种情况ViewBuilder肯定是没有实现了。我们来扩展一下。
下面的代码就可以正常编译了。
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension ViewBuilder {
public static func buildArray<Content>(_ components: [Content]) -> Content where Content : View {
return components.first!
}
}
List {
for i in 0...10 {
Text("\(i)")
}
}