如何在 SwiftUI 中使用 ObservableObject 将值发送到其他类

问题描述

我正在尝试创建一个简单的小费计算器,但是一旦用户在应用程序中添加了信息而不是计算小费,我就会遇到问题,该函数正在返回,好像某处有问题一样。

我试图做到这一点,当用户在文本字段中输入一个数字并选择一个百分比时,总提示显示在下方。

我该如何解决这个问题? 我添加一个打印语句来打印“返回”这个词,并且它一直在打印这个词,所以我认为问题出在calculateTip 函数中:

这是我的 ContentView 类的代码

struct ContentView: View {

//MARK: - PROPERTIES
@Observedobject public var tipVM = Tipviewmodel()

//MARK: - FUNCTIONS
private func endEditing() {
    hideKeyboard()
}
     
//MARK: - BODY
var body: some View {
    Background {
        NavigationView {
            ZStack {
                vstack {
                    HStack(spacing: 10) {
                        Text("Tip Calculator")
                            .font(.system(.largeTitle,design: .rounded))
                            .fontWeight(.heavy)
                            .padding(.leading,4)
                            .foregroundColor(.blue)
                        Spacer()
                        Button(action: {
//                                tipVM.clearFields()
                        },label: {
                            Text("Clear")
                                .font(.system(size: 16,weight: .semibold,design: .rounded))
                                .padding(.horizontal,10)
                                .frame(minWidth: 70,minHeight: 24)
                                .background(
                                    Capsule().stroke(linewidth: 2)
                                )
                                .foregroundColor(.blue)
                        }) //: BUTTON
                    } //: HSTACK
                    .padding()
                    Spacer(minLength: 80)
                    TextField("Enter Amount: ",text: $tipVM.amount)
                        .padding()
                        .background(Color.secondary)
                        .foregroundColor(.white)
                        .font(.system(.title3,design: .rounded))
                    Picker(selection: $tipVM.tipPercentage,label: Text("Picker"),content: {
                        ForEach(0 ..< tipVM.tipChoices.count) { index in
                            Text("\(self.tipVM.tipChoices[index])%").tag(index)
                                .font(.system(.body,design: .rounded)).padding()
                        }.padding()
                        .background(subtitleColor)
                        .foregroundColor(.white)
                    }).onTapGesture(perform: {
                        tipVM.calculateTip()
                    })
                    .pickerStyle(SegmentedPickerStyle())
                    Text(tipVM.tip == nil ? "£0" : "\(tipVM.tip!)")
                        .font(.system(.largeTitle,design: .rounded))
                        .fontWeight(.bold)
                        .foregroundColor(.blue)
                        .padding()
                    
                } //: vstaCK
            } //: ZSTACK
            .navigationBarHidden(true)
        } //: NAVIGATION VIEW
        .navigationViewStyle(StackNavigationViewStyle())
    }.onTapGesture {
        self.endEditing()
    }
    .ignoresSafeArea(.keyboard,edges: .all)
} //: BACKGROUND
}

这是我的 Tipviewmodel 类的代码

import Foundation
import SwiftUI
import Combine

class Tipviewmodel: ObservableObject {

var amount: String = ""
var tipPercentage: Int = 0
var tip: Double?

let tipChoices = [10,15,20,25,30]

let didChange = PassthroughSubject<Tipviewmodel,Never>()

func calculateTip() {

    guard let amount = Double(amount) else {
        print("returning")
        return
    }
    
    self.tip = amount * (Double(tipPercentage)/100)
    self.didChange.send(self)
}
}

如果有任何帮助,我将不胜感激。

解决方法

通常的方法是用 @Published 标记属性,其更改将被监控。不需要额外的 Combine 主题。

并声明 tip 为非可选

import Foundation
import SwiftUI
// import Combine
    
class TipViewModel: ObservableObject {
    
    var amount: String = ""
    var tipPercentage: Int = 0
    @Published var tip = 0.0
    
    let tipChoices = [10,15,20,25,30]
    
    func calculateTip() {
    
        guard let numAmount = Double(amount) else {
            print("returning")
            return
        }
        self.tip = numAmount * (Double(tipPercentage)/100)
    }
}

其次,如果当前视图结构创建(也称为拥有)可观察对象,则始终使用 @StateObject 而不是 @ObservedObject。后者用于在视图层次结构中的更高级别初始化并刚刚通过的对象。

struct ContentView: View {

    //MARK: - PROPERTIES
    @StatedObject private var tipVM = TipViewModel()

    ...

        Text("£\(tipVM.tip)")