简体   繁体   English

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

[英]How to communicate changes in TextField to another Sibling View (Swift UI, Xcode 11.5, macOS Catalina)

I have a parent View which is showing two subView.我有一个显示两个子视图的父视图。 See attached image.见附图。

在此处输入图像描述

I expect user to enter height and weight in View 1. As and when the value of either height or weight changes in View 1, I want the View2 to automatically reflect the value of BMI (body mass index) which is weight / (height * height)我希望用户在视图 1 中输入身高和体重。当视图 1 中的身高或体重值发生变化时,我希望视图 2 自动反映 BMI(体重指数)的值,即体重/(身高 *高度)

For this parent view I have tried multiple option but none working in iOS Simulator对于这个父视图,我尝试了多个选项,但没有在 iOS 模拟器中工作

Option 1选项1

  1. I created two state variable in parent view and passed their binding to both View1 and View2我在父视图中创建了两个 state 变量并将它们的绑定传递给 View1 和 View2
  2. I have then bind-ed two TextField in View1 to them然后我将 View1 中的两个 TextField 绑定到它们
  3. In View2 I set the text to be a function calcBMI which uses height and weight.在 View2 中,我将文本设置为使用身高和体重的 function calcBMI。

In Overall View总览

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

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

In View1在视图1

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

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

In View2在视图2

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

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

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

Only initial value of 68.05 is getting displayed in BMI in Simulator but post this if I change the value of height and/or weight in View1 that doesn't propagate to View2.只有初始值 68.05 会显示在 Simulator 的 BMI 中,但如果我更改 View1 中的身高和/或体重值而不会传播到 View2,请发布此值。 The BMI stays 68.05 no matter what I change in View1无论我在 View1 中进行什么更改,BMI 都保持在 68.05

Question: Isn't the binding from TextField should have send back the state value to Overall View and from there View2 should have picked the changes and reflect the latest value of BMI as and when user typing changes?问题: TextField 的绑定是否应该将 state 值发送回整体视图,并且 View2 应该从那里选择更改并在用户键入更改时反映 BMI 的最新值? ie TextField changes -> affecting Parent's State??即TextField更改->影响Parent的State??

Any clue what is missing here?任何线索这里缺少什么?

Option 2选项 2

I tried encapsulating height and weight in a class and making it ObservableObject and marked @Published to height and weight.我尝试将身高和体重封装在 class 中,并将其设为 ObservableObject 并将@Published 标记为身高和体重。 I then injected same instance of this class into View1 and View2 via Overall View.然后我通过整体视图将这个 class 的相同实例注入到 View1 和 View2 中。 I bind-ed the height and weight in View1 to the instance property of ObservedObject but that also didn't work.我将 View1 中的高度和重量绑定到 ObservedObject 的实例属性,但这也不起作用。 No change is propagated from View1 -> Parent -> View2.没有从 View1 -> Parent -> View2 传播任何更改。

import Foundation
import Combine

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

Overall View总体看法

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

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

View1视图1

@ObserverdObject var bodyST : BodyStats

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

View2视图2

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

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

Overall all I need to capture value from TextField in View1 and then pass down to View 2 for calculation on real time (as user is typing).总的来说,我需要从 View1 中的 TextField 捕获值,然后传递到 View 2 以进行实时计算(当用户输入时)。 I believe we can do this in SwiftUI but not able to get my head around the right communication pattern.我相信我们可以在 SwiftUI 中做到这一点,但无法理解正确的通信模式。

I assume that the reason is in putting calculation result in completely in separated function, and such hiding dependency from renderer.我认为原因是将计算结果完全放在分离的 function 中,并且对渲染器隐藏了依赖关系。

Try something like the following (second option looks more appropriate for your goal).尝试以下类似的方法(第二个选项看起来更适合您的目标)。

 @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)
 }

What I found is that if data is bound to value attribute of TextField then user's typing into TextField is never synced into bounded variable.我发现如果数据绑定到 TextField 的 value 属性,那么用户在 TextField 中的输入永远不会同步到有界变量中。 So below doesn't work.所以下面不起作用。

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

Below works perfectly下面完美运行

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

So in nutshell as long as TextField is bound to a String type variable I got it working both in Option 1 and Option 2. To me it looks like this is a bug in TextField's binding behaviour when variable is bound to value: attribute.因此,简而言之,只要 TextField 绑定到 String 类型变量,我就可以在选项 1 和选项 2 中使用它。对我来说,当变量绑定到 value: 属性时,这看起来像是 TextField 绑定行为中的一个错误。 The compromise is to declare the holding variable as String even though the underlying value is numeric.折衷方案是将持有变量声明为 String,即使基础值是数字。

Overall below is how I was able to get it working总的来说,下面是我如何让它工作

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
    }

Overall View总体看法

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

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

In View 1在视图 1

@ObserverdObject var bodyST : BodyStats

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

In View 2在视图 2

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM