传递给TextField的Binding存在问题,它不允许状态更改



大家好,提前谢谢。

我在运行应用程序时遇到问题(不在预览中),文本字段没有更新状态。我还没有继续扩展MVVM,因为我遇到了这个UI/Binding问题。

不确定我错过了什么?我将StateObject(视图模型实例)传递到EnvironmentObject列表中,然后从EnvironmentObject访问该列表,并对视图中元素的模型数组进行迭代,然后将数组的迭代元素进一步传递到另一个视图中的Binding,该Binding然后绑定到用户要编辑的文本字段?

具体而言,问题是:当滑动动作>在ContentView中编辑费用以导航到EditExpenseView,文本字段不允许编辑。

注:

  • 如果我将文本字段向上移动到ExpenseList视图,那么要编辑的绑定就可以工作了。我认为List(items)可能是问题所在,因为它在一个不可变的集合上迭代。

  • 我使用索引并通过$expense[index]传递数组绑定,这避免了访问不可变集合,因为它只用于获取用户将编辑的列表项的索引。

如果你还在阅读,谢谢你的精彩请告诉我是否可以添加任何进一步的信息或提供澄清。

费用模型:

struct Expense: Equatable, Identifiable, Codable {

init(date: Date, description: String, amount: Decimal, type: ExpenseType, status: ExpenseStatus, budgetId: UUID?) {
self.date = date
self.description = description
self.amount = amount
self.type = type
self.status = status
self.budgetId = budgetId
}

static func == (lhs: Expense, rhs: Expense) -> Bool {
lhs.id == rhs.id
}

var id: UUID = UUID()
var date: Date
var description: String
var amount: Decimal
var type: ExpenseType
var status: ExpenseStatus
var budgetId: UUID?
}

ExpenseViewModel:

class ExpenseViewModel: ObservableObject, Identifiable {

@Published var expenses: [Expense] = []

func insertExpense(date: Date, description: String, amount: Decimal, type: ExpenseType, status: ExpenseStatus) -> Void {
expenses.insert(Expense(date: date, description: description, amount: amount, type: type, status: status, budgetId: nil), at:0)
}

func remove(_ expense: Expense) {
expenses.removeAll(where: {$0.id == expense.id})
}

}

应用程序入口:

import SwiftUI
@main
struct iBudgeteerApp: App {

@StateObject private var expenses = ExpenseViewModel()

var body: some Scene {
WindowGroup {
ContentView().environmentObject(expenses)
}
}
}

初始视图:

struct ContentView: View {

@EnvironmentObject private var model: ExpenseViewModel

private static let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
return formatter
}()

var body: some View {
NavigationStack {
VStack {
Button("Add Row") {
model.insertExpense(date: Date(), description: "Groceries", amount: 29.94, type: .Expense, status: .Cleared)
}
ExpenseList(expenses: $model.expenses)
}
}
}
}

支出列表视图:

struct ExpenseList: View {

@Binding var expenses: [Expense]

var formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
return formatter
}()

var body: some View {


List (expenses.sorted(by: {$0.date > $1.date}).indices, id: .self) {
index in

HStack {

Text("(index + 1).").padding(.trailing)

VStack(alignment: .leading) {

HStack {
Text(expenses[index].date.formatted(date:.numeric, time: .omitted))
Spacer()
Text(expenses[index].description)
}

HStack {
Text(expenses[index].description)
Spacer()
Text("(expenses[index].amount as NSNumber, formatter: formatter)")
.foregroundColor( expenses[index].type == .Expense ? .red : .green)
Image(systemName: expenses[index].type == .Expense ? "arrow.down" : "arrow.up").foregroundColor( expenses[index].type == .Expense ? .red : .green)
}.padding(.top, 1)
}
.swipeActions(edge: .trailing) {
Button(role: .destructive, action: { expenses.remove(at: index) } ) {
Label("Delete", systemImage: "trash")
}
.tint(.gray)
}
.swipeActions() {
NavigationLink {
EditExpenseView(expense: self.$expenses[index])
} label: {
Label("Edit", systemImage: "slider.horizontal.3")
}
.tint(.yellow)
}
}
}
}

}

编辑支出视图:

struct EditExpenseView: View {

@Binding var expense: Expense

var formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
return formatter
}()

var body: some View {

Form {

Section(header: Text("Editing: (expense.description)")) {
VStack {
DatePicker(
"Date",
selection: $expense.date,
displayedComponents: [.date]
)
HStack {
Text("Name")
Spacer()
TextField("description",text: $expense.description)
.fixedSize().multilineTextAlignment(.trailing)
}
HStack {
Text("Amount")
Spacer()
TextField("0.00", value: $expense.amount, formatter: formatter).fixedSize()
}

Picker("Status", selection: $expense.status) {
ForEach(ExpenseStatus.allCases, id: .self) {
status in
Text("(status.rawValue)")
}
}
Picker("Type", selection: $expense.type) {
ForEach(ExpenseType.allCases, id: .self) {
type in
Text("(type.rawValue)")
}
}
}
}
}

}
}

更新

它适用于:

List ($expenses) { $expense in
NavigationLink(expense.description) {
EditExpenseView(expense: $expense)
}         
}
ForEach($expenses) { $expense in
NavigationLink(expense.description) {
EditExpenseView(expense: $expense)
}
}

但不在:

List($expenses) {
$expense in

VStack(alignment: .leading) {

HStack {
Text(expense.date.formatted(date:.numeric, time: .omitted))
Spacer()                        }

HStack {
Text(expense.description)
Spacer()
Text("(expense.amount as NSNumber, formatter: formatter)")
.foregroundColor( expense.type == .Expense ? .red : .green)
Image(systemName: expense.type == .Expense ? "arrow.down" : "arrow.up").foregroundColor(expense.type == .Expense ? .red : .green)
}.padding(.top, 1)
}
.swipeActions(edge: .trailing) {
Button(role: .destructive, action: { //expenses.remove(expense)
} ) {
Label("Delete", systemImage: "trash")
}
.tint(.gray)
}
.swipeActions() {

NavigationLink {
EditExpenseView(expense: $expense)
} label: {
Label("Edit", systemImage: "slider.horizontal.3")
}
.tint(.yellow)
}


}

免责声明:

我无法正确测试这个答案,因为你的例子缺少信息,无法复制。请考虑发布一个可复制的最小示例。

问题在于以下几行:

List (expenses.sorted(by: {$0.date > $1.date}).indices, id: .self) {

然后做:

EditExpenseView(expense: self.$expenses[index])

您没有将Expense的绑定引用传递给EditExpenseView,而是将绑定传递给它的副本。您正在破坏绑定链。

以下方法应该会产生所需的结果:

List ($expenses) { $expense in
HStack {

Text("(expenses.firstIndex(of: expense) + 1).").padding(.trailing)

VStack(alignment: .leading) {

HStack {
Text(expense.date.formatted(date:.numeric, time: .omitted))
Spacer()
Text(expense.description)
}
.....

并将您的Expense传递到您的子视图:

EditExpenseView(expense: $expense)

相关内容

最新更新