【问题标题】:Why does the TextField in the List stop updating?为什么 List 中的 TextField 停止更新?
【发布时间】:2022-01-05 08:08:00
【问题描述】:

我开始学习 SwiftUI,并想制作一个标准提醒的原型,就像在 iPhone 中一样。看起来没什么复杂的,有一个List,每个单元格都有一个TextField。

但我遇到了一个问题:当我们使用 onChange 更改 TextField 中的文本时,我们会相应地告诉视图模型更新我们的对象。

并且当对象被更新时,整个 List 被重绘并且当前 TextField 的编辑被重置(你不能删除多个字符,也不能添加)。您必须再次单击文本才能继续编辑。

有谁知道如何处理这个问题?

这是我的代码:

import SwiftUI

struct Fruit: Identifiable {
    let id = UUID()
    let name: String
    
    func updateName(newName: String) -> Fruit {
        return Fruit(name: newName)
    }
}

class ViewModel: ObservableObject {
    @Published var fruits: [Fruit] = [Fruit(name: "apple"), Fruit(name: "banana"), Fruit(name: "orange")]
    
    func updateName(newName: String, fruit: Fruit) {
        if let index = fruits.firstIndex(where: { $0.id == fruit.id }) {
            fruits[index] = fruit.updateName(newName: newName)
        }
    }
}

struct ListView: View {
    @StateObject var viewModel = ViewModel()
    var body: some View {
        List {
            ForEach(viewModel.fruits) { fruit in
                ListViewRow(fruit: fruit)
            }
        }
        .environmentObject(viewModel)
    }
}

struct ListViewRow: View {
    @EnvironmentObject var viewModel: ViewModel
    @State var fruitTextField: String
    let fruit: Fruit
    
    init(fruit: Fruit) {
        self.fruit = fruit
        _fruitTextField = State(initialValue: fruit.name)
    }
    
    var body: some View {
        TextField("", text: $fruitTextField)
            .onChange(of: fruitTextField) { newValue in
                viewModel.updateName(newName: newValue, fruit: fruit)
            }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ListView()
    }
}

【问题讨论】:

  • 使用绑定而不是状态
  • @loremipsum 我真的不太明白我应该与对方绑定什么,请你解释一下吗?
  • A @Binding 保留对父视图中绑定变量的更新值的引用并传递。 `@State` 只保留它自己的内部值。当您更新父级的@StateObject 时,您将创建一个全新的ListViewRow。由于您从未保留输入到 TextField 中的数据,因此它会以您在第一个实例中传递给视图的水果名称重新开始。
  • State 和 StateObject 是事实的来源。绑定是一种双向连接。观看 WWDC21 揭开 SwiftUI 的神秘面纱

标签: swiftui swiftui-list


【解决方案1】:

您可以通过以下方式进一步简化它:

struct Fruit: Identifiable {
    let id = UUID()
    var name: String
}

class ViewModel: ObservableObject {
    @Published var fruits: [Fruit] = [Fruit(name: "apple"), Fruit(name: "banana"), Fruit(name: "orange")]
}

struct ListView: View {
    @StateObject var viewModel = ViewModel()
    var body: some View {
        List {
            ForEach($viewModel.fruits) { $fruit in
                ListViewRow(fruit: $fruit)
            }
        }
    }
}

struct ListViewRow: View {
    @Binding var fruit: Fruit
    
    var body: some View {
        TextField("", text: $fruit.name)
    }
}

话虽如此,您确实需要查看在 cmets 中链接的 Apple Swift 教程。

编辑:Lorem Ipsum 的完整项目代码:

//
//  ContentView.swift
//  FruitApp
//
//  Created by Developer on 11/27/21.
//

import SwiftUI

struct Fruit: Identifiable {
    let id = UUID()
    var name: String
}

class ViewModel: ObservableObject {
    @Published var fruits: [Fruit] = [Fruit(name: "apple"), Fruit(name: "banana"), Fruit(name: "orange")]
}

struct ListView: View {
    @StateObject var viewModel = ViewModel()
    var body: some View {
        List {
            ForEach($viewModel.fruits) { $fruit in
                ListViewRow(fruit: $fruit)
            }
        }
    }
}

struct ListViewRow: View {
    @Binding var fruit: Fruit
    
    var body: some View {
        TextField("", text: $fruit.name)
    }
}

struct ContentView: View {
    
    var body: some View {
        ListView()
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

【讨论】:

  • 谢谢!现在它可以正常工作了。感谢您提及 Apple Swift 教程,我将深入探讨。
  • @Yrb Lol,所以你只是复制了我的答案并给出了我的建议并作为你自己的建议?通过您编辑的代码,甚至当您只复制我的部分答案时您错过的var 捕获。
  • @loremipsum,不,我没有。实际上,您在我准备好之前的几分钟内就给出了答案,但是如果您更仔细地查看ForEach,您会明白为什么我仍然发布它。我使用了一个属性包装数组,它将输出一个属性包装元素。然后,您可以直接在绑定中使用它。那将是简化部分。我对简化的评论是因为您先发布。我发帖是因为我觉得我的回答稍微好一点,但它们本质上是一样的。
  • @Yrb 这与循环的答案完全相同,您的答案最初不起作用,因为您没有更改struct 中的var。但没关系,我删除了我的,我可以取消删除,你可以看到没有编辑。没什么大不了的,我只是指出来。
  • 实际上,在我的 Xcode 代码中,我确实将结构中的 let 更改为 var。我只是想提出最低限度的必要代码。然后我意识到我也必须发布它。我将用每一段代码更新我的答案。这真的不是我想要的分歧,因为我尊重你的意见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-17
  • 1970-01-01
  • 1970-01-01
  • 2019-08-29
  • 1970-01-01
  • 1970-01-01
  • 2012-10-07
相关资源
最近更新 更多