首页 > 解决方案 > SwiftUI 和 Combine - 如何创建一个具有与用户选择对应的字段数的表单

问题描述

我正在创建一个基本的 Rent Splitting 应用程序来了解 Swift UI 和 Combine。我想要一个主屏幕,用户首先选择他们共享的室友数量,然后根据该选择,向他们显示一个表单,其中包含与该数字对应的字段。到目前为止,我有 HomeView:

import SwiftUI
import Combine

struct HomeScreenView: View {
    
    @ObservedObject private var viewModel: HomeScreenViewModel
    
    init(viewModel: HomeScreenViewModel){
        self.viewModel = viewModel
    }
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Welcome to Fair Rent!")
                Text("Please select how many housemates you live with:")
                    .lineLimit(nil)
                    .padding(5.0)
                    
                NavigationLink(destination: FairRentView(viewModel: FairRentViewModel(Amounts())))
                {
                   Text("1")
                }
                NavigationLink(destination: FairRentView(viewModel: FairRentViewModel(Amounts())))
                {
                   Text("2")
                }
                NavigationLink(destination: FairRentView(viewModel: FairRentViewModel(Amounts())))
                {
                   Text("3")
                }
                NavigationLink(destination: FairRentView(viewModel: FairRentViewModel(Amounts())))
                {
                   Text("4")
                }
                NavigationLink(destination: FairRentView(viewModel: FairRentViewModel(Amounts())))
                {
                   Text("5")
                }
                NavigationLink(destination: FairRentView(viewModel: FairRentViewModel(Amounts())))
                {
                   Text("6")
                }
                NavigationLink(destination: FairRentView(viewModel: FairRentViewModel(Amounts())))
                {
                   Text("7")
                }
            }
        }
    }
}

struct HomeScreenView_Previews: PreviewProvider {
    static var previews: some View {
        HomeScreenView(viewModel: HomeScreenViewModel(Variables()))
    }
}

根据他们点击的数字,他们将被带到 FairRentView:

import SwiftUI
import Combine

struct FairRentView: View {
    
    @ObservedObject private var viewModel: FairRentViewModel
    
    init(viewModel: FairRentViewModel){
        self.viewModel = viewModel
    }
    
    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Enter the total monthly rent:")) {
                    TextField("Total rent", text: $viewModel.amount.totalRent)
                        .keyboardType(.decimalPad)
                }
                Section(header: Text("Enter your monthly income:")) {
                    TextField("Your monthly wage", text: $viewModel.amount.myMonthlyIncome)
                        .keyboardType(.decimalPad)
                }
                Section(header: Text("Enter your housemate's monthly income:")) {
                    TextField("Housemate's monthly income", text: $viewModel.amount.housemateMonthlyIncome)
                        .keyboardType(.decimalPad)
                }
                Section {
                    Text("Your share: £\(viewModel.yourShare, specifier: "%.2f")")
                }
            }
            .navigationBarTitle("FairRent")
        }
    }
}

struct FairRentView_Previews: PreviewProvider {
    static var previews: some View {
        FairRentView(viewModel: FairRentViewModel(Amounts()))
    }
}

我希望租金是基于工资的。目前,您可以看到只有一个表单字段可以输入室友的工资,而不考虑是否有多个室友。我希望这些字段的数量与用户在 HomeScreen 上选择的数量相对应。我对 SwiftUI 很陌生,不确定最好的方法。谁能指出我正确的方向?我正在尽力使用组合并确认 MVVM 模式。

谢谢

标签: swiftswiftui

解决方案


由于 SwiftUI 是数据驱动的,其中视图是基于数据生成的,因此您需要一个地方来将每个室友的金额存储在某个地方,比如在视图模型中的一个数组中。然后,您将遍历数组以生成TextFields.

这是一个概念性示例:

class FairRentViewModel: ObservableObject {
   @Published var incomes: [String]

   init(for housemates: Int) {
      self.incomes = Array(repeating: "", count: housemates)
   }
}

(我使用 aString作为简化,所以以后更容易绑定到 a TextField

然后视图将遍历数组:

struct FairRentView: View {
   @ObservedObject var vm: FairRentViewModel

   var body: some View {
      ForEach(vm.incomes.indices, id: \.self) { idx in
          TextField("Housemate \(idx + 1)", text: self.$vm.incomes[idx])
      }
   }
}

然后在父视图中,您可以FairRentView使用正确数量的家庭进行实例化。同样,这是一种简化,因为您可能也希望在这里有一个循环。

struct HomeScreenView: View {
   var body: some View {
      NavigationView {
         VStack {
            NavigationLink(destination: FairRentView(vm: FairRentViewModel(for: 2)) {
               Text(2)
            }
            NavigationLink(destination: FairRentView(vm: FairRentViewModel(for: 3)) {
               Text(3)
            }
            // .. etc
         }
      }
   }
}

推荐阅读