在@ViewBuilder 闭包中更改@State

Change @State in a @ViewBuilder closure

我正在尝试了解如何在 @ViewBuilder 闭包中更改 @State 变量。以下只是一个简单的例子:

struct ContentView: View {
    @State var width = CGFloat(0)

    var body: some View { //Error 1
        GeometryReader { geometry in //Error 2
            self.width = geometry.size.width
            return Text("Hello world!")
        }
    }
}

我遇到了一些错误:

错误 1:

Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type

但是 return 是多余的,因为 View 中只有一行代码计算 属性。

错误 2:

Cannot convert return expression of type 'GeometryReader<_>' to return type 'some View'

既然我明确写了return Text("...")类型不应该清楚吗?

这里有什么问题?

首先,您不能在 functionBuilder 中任意 swift 陈述。允许使用特定语法。在 SwiftUI 的情况下,它是一个 ViewBuilder,它在幕后构成您的类型。当您在 SwiftUI 中进行连续语句时,您实际上是在依赖编译器根据该 DSL 的规则在其下组合一个新类型。

2、SwiftUI是配方,不是事件系统。您 更改 body 函数中的状态变量,您设置当状态变量在外部发生变化时事物应该如何反应。如果您希望另一个视图以某种方式对该宽度做出反应,则需要根据您想要的组件的宽度来定义该内容。在不知道您的最终目标是什么的情况下,很难回答如何将内容相互联系起来。

编辑:

我被要求详细说明究竟允许什么。每个 functionBuilder 都有不同的允许语法,这些语法在 functionBuilder 本身中定义。这对 Swift 5.1 中的函数构建器有很好的概述:https://www.swiftbysundell.com/posts/the-swift-51-features-that-power-swiftuis-api

至于SwiftUI具体找的是什么,本质上就是找return一个View.

实例的每个语句
// works fine!
VStack {
  Text("Does this")
  Text("Work?")
}

// doesn't work!
VStack {
  Text("Updating work status...")
  self.workStatus = .ok // this doesn't return an instance of `View`!
}

// roundabout, but ok...
VStack {
  Text("Being clever")
  gimmeView()
}

// fine to execute arbitrary code now, as long as we return a `View`
private func gimmeView() -> some View {
    self.workingStatus = .roundabout 
    return Text("Yes, but it does work.")
}

这就是为什么您会遇到钝错误的原因:

Cannot convert return expression of type 'GeometryReader<_>' to return type 'some View'

类型系统无法从 View 中构造出任何 View,并且在您执行时基本上 Void

self.width = geometry.size.width

当你执行连续的 View 语句时,在下面,它仍然被转换成一种新的类型 View:

// the result of this is TupleView<Text, Text>
Text("left")
Text("right")