在@ViewBuilder闭包中更改@State

时间:2019-08-06 10:37:42

标签: ios swift swiftui

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

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:

  

函数声明了不透明的返回类型,但是没有返回语句   从其主体中推断出基础类型

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

错误2:

  

无法将类型为'GeometryReader <_>'的返回表达式转换为return   输入“某些视图”

自从我明确写出return Text("...")以来,类型不应该清晰吗?

这是什么问题?

1 个答案:

答案 0 :(得分:5)

首先,您不能在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到底在寻找什么,它实质上是在寻找每条语句返回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.")
}

这就是为什么出现钝角错误的原因:

  

无法将“ GeometryReader <_>”类型的返回表达式转换为“某些视图”类型

执行时,类型系统不能从ViewView中构造任何Void

  

self.width = geometry.size.width

当您在下面执行连续的View语句时,它仍将转换为新的View类型:

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