如何将TextField中的更改传达给另一个兄弟视图(Swift UI,Xcode 11.5,macOS Catalina)

时间:2020-06-14 15:25:01

标签: ios swift swiftui textfield

我有一个父视图,其中显示了两个子视图。请参阅附件图片。

enter image description here

我希望用户在View 1中输入身高和体重。当View 1中身高或体重的值发生变化时,我希望View2自动反映BMI(体重指数)的值,即体重/ (高度*高度)

对于此父视图,我尝试了多种选择,但在iOS Simulator中均无法使用

选项1

  1. 我在父视图中创建了两个状态变量,并将它们的绑定传递给View1和View2
  2. 然后我将View1中的两个TextField绑定到它们上
  3. 在View2中,我将文本设置为使用身高和体重的calcBMI函数。

总览

@State var height: Float = 1.2
@State var weight: Float = 98.0

VStack{
View1(height : $height, weight: $weight)
View2(height : $height, weight: $weight)
}

在View1中

@Binding var height: Float
@Binding var weight: Float

TextField("Height", value: $height, formatter: NumberFormatter())
TextField("Weight", value: $weight, formatter: NumberFormatter())

在View2中

 @Binding var height: Float
 @Binding var weight: Float

 Text("Your BMI \(calcBMI())")

 private fund calcBMI(){
  return height <= 0 ? 0.0 : weight / (height * height)
 }

在Simulator的BMI中仅显示68.05的初始值,但是如果我更改了不会传播到View2的View1中的身高和/或体重的值,则发布此值。无论我在View1中进行什么更改,BMI都保持在68.05

问题:不是TextField的绑定应该将状态值发送回总体视图,并且View2应该从那里选择更改并在用户键入时反映BMI的最新值变化?即TextField发生了变化->影响了家长的状态?

任何线索,这里缺少什么?

选项2

我尝试将高度和重量封装在一个类中,并使其设置为ObservableObject,然后将@Published标记为高度和重量。然后,我通过总体视图将此类的相同实例注入到View1和View2中。我将View1中的高度和重量绑定到ObservedObject的instance属性,但这也没有用。从View1->父级-> View2不会传播任何更改。

import Foundation
import Combine

class BodyStats: ObservableObject{
   @Published var height: Float
   @Published var weight : Float
}

整体视图

@State var bodySt = BodyStats(height: 1.2, weight: 98.0)

View1(bodyST : $self.bodySt)
View2(bodyST : $self.bodySt)

View1

@ObserverdObject var bodyST : BodyStats

TextField("Height", value: $bodyST.height, formatter: NumberFormatter())
TextField("Weight", value: $bodyST.weight, formatter: NumberFormatter())

View2

@ObserverdObject var bodyST : BodyStat
 Text("Your BMI \(calcBMI())")

 private fund calcBMI(){
  return bodyST.height <= 0 ? 0.0 : bodyST.weight / (bodyST.height * bodyST.height)
 }

总体而言,我需要从View1的TextField中捕获值,然后向下传递到View 2进行实时计算(随着用户的输入)。我相信我们可以在SwiftUI中做到这一点,但无法让我了解正确的沟通方式。

2 个答案:

答案 0 :(得分:0)

我认为原因是将计算结果完全放在单独的函数中,并且这种对渲染器的隐藏依赖性。

尝试类似以下操作(第二个选项看起来更适合您的目标)。

 @ObserverdObject var bodyST : BodyStat

 var body: some View {
    // leave dependency on ObservedObject in body
    Text("Your BMI \(calcBMI(bodyST.weight, bodyST.height))")
 }

 private fund calcBMI(_ weight: Float, _ height: Float) {
  return height <= 0 ? 0.0 : weight / (height * height)
 }

答案 1 :(得分:0)

我发现的是,如果将数据绑定到TextField的value属性,则用户在TextField中键入的内容永远不会同步到有界变量中。所以下面的行不通。

@Binding var height: Float
TextField("Height", value: @height, formatter: NumberFormatter())

下面的作品很完美

@Binding var height: String
TextField("Height", text: @height)

因此,简而言之,只要将TextField绑定到String类型的变量,我就可以在Option 1和Option 2中使用它。对我来说,这似乎是变量绑定到value:attribute时TextField绑定行为的错误。 。折衷方案是将保持变量声明为String,即使基础值是数字。

下面的总体说明是我如何使它工作

import Foundation
import Combine

class BodyStats: ObservableObject{
   @Published var height: String
   @Published var weight : String

   func getBMI() -> Float{
        if let height = Float(self.height), let weight = Float(self.weight){
            return height <= 0.0 ? 0.0 : weight / (height * height)
        }

        return 0.0
    }

整体视图

@ObservedObject var bodySt = BodyStats(height: "1.2", weight: "98.0")

View1(bodyST : bodySt)
View2(bodyST : bodySt)

在视图1中

@ObserverdObject var bodyST : BodyStats

TextField("Height", text: $bodyST.height)
TextField("Weight", text: $bodyST.weight)

在View 2中

@ObserverdObject var bodyST : BodyStat
Text("Your BMI \(bodyST.getBMI())")