如何使用 ForEach 中制作的 TextField 更新结构的属性
Posted
技术标签:
【中文标题】如何使用 ForEach 中制作的 TextField 更新结构的属性【英文标题】:How to update attributes of a struct with TextFields made in ForEach 【发布时间】:2021-09-11 00:47:44 【问题描述】:在 SwiftUI 中,我有一个菜单项列表,每个项都有名称、价格等。有一堆类别,每个类别下都有一个项目列表。
struct ItemList: Identifiable, Codable
var id: Int
var name: String
var picture: String
var list: [Item]
@State var newItemName: String
我一直在寻找一种方法来在每个类别中创建一个 TextField,以添加到其项目数组中。
通过 ForEach 循环创建 TextField 非常简单,但是我在尝试使用输入的文本将新项目添加到正确的类别时遇到了困难。
ForEach(menu.indices) i in
Section(header: Text(menu[i].name))
ForEach(menu[i].list) item in
Text(item.name)
TextField("New Type:", text: /*some kind of bindable here?*/)
menu[i].list.append(Item(name: /*the text entered above*/))
我考虑过使用 @Published 和 Observable Object,如 this other question,但我需要 ItemList 是一个 Codable 结构,所以我无法弄清楚如何将答案放在我的案例中。
TextField("New Type:", text: menu[i].$newItemName)
任何想法都将不胜感激,谢谢!
【问题讨论】:
【参考方案1】:你只需要关注你的View
。
import SwiftUI
struct ExpandingMenuView: View
@State var menu: [ItemList] = [
ItemList(name: "Milk Tea", picture: "", list: [ItemModel(name: "Classic Milk Tea"), ItemModel(name: "Taro milk tea")]),
ItemList(name: "Tea", picture: "", list: [ItemModel(name: "Black Tea"), ItemModel(name: "Green tea")]),
ItemList(name: "Coffee", picture: "", list: [])
]
var body: some View
List
//This particular setup is for ios15+
ForEach($menu) $itemList in
ItemListView(itemList: $itemList)
struct ItemListView: View
@Binding var itemList: ItemList
@State var newItemName: String = ""
var body: some View
Section(header: Text(itemList.name))
ForEach(itemList.list) item in
Text(item.name)
TextField("New Type:", text: $newItemName, onCommit:
//When the user commits add to array and clear the new item variable
itemList.list.append(ItemModel(name: newItemName))
newItemName = ""
)
struct ItemList: Identifiable, Codable
var id: UUID = UUID()
var name: String
var picture: String
var list: [ItemModel]
//@State is ONLY for SwiftUI Views
//@State var newItemName: String
struct ItemModel: Identifiable, Codable
var id: UUID = UUID()
var name: String
struct ExpandingMenuView_Previews: PreviewProvider
static var previews: some View
ExpandingMenuView()
如果您不使用 Xcode 13 和 iOS 15+,SO 中有许多解决方案可用于绑定数组元素。以下只是其中之一
ForEach(menu) itemList in
let proxy = Binding(get: itemList, set: new in
let idx = menu.firstIndex(where:
$0.id == itemList.id
)!
menu[idx] = new
)
ItemListView(itemList: proxy)
另请注意,使用indices
被认为是不安全的。您可以观看 WWDC2021 的 Demystifying SwiftUI 了解更多详情。
【讨论】:
ForEach($menu) $itemList in
语法不仅适用于 iOS 15,它还是 Swift 5.5 的功能。例如,如果在 Binding
上使用 enumerated()
,则只需要 iOS 15。
是的,效果很好,谢谢!升级到 swift 5.5 有点困难,但是 Binding() 方法效果很好,真的很感激【参考方案2】:
您可以将ObservableObject
作为您的数据模型,存储类别,然后存储项目。
然后您可以使用 Swift 5.5 语法绑定到这些项目。这意味着我们可以写List($menu.categories) $category in /* ... */
。然后,当我们写$category.newItem
时,我们在Category
中的newItem
属性上有一个Binding<String>
。
例子:
struct ContentView: View
@StateObject private var menu = Menu(categories: [
Category(name: "Milk Tea", items: [
Item(name: "Classic Milk Tea"),
Item(name: "Taro Milk Tea")
]),
Category(name: "Tea", items: [
Item(name: "Black Tea"),
Item(name: "Green Tea")
]),
Category(name: "Coffee", items: [
Item(name: "Black Coffee")
])
])
var body: some View
List($menu.categories) $category in
Section(header: Text(category.name))
ForEach(category.items) item in
Text(item.name)
TextField("New item", text: $category.newItem, onCommit:
guard !category.newItem.isEmpty else return
category.items.append(Item(name: category.newItem))
category.newItem = ""
)
class Menu: ObservableObject
@Published var categories: [Category]
init(categories: [Category])
self.categories = categories
struct Category: Identifiable
let id = UUID()
let name: String
var items: [Item]
var newItem = ""
struct Item: Identifiable
let id = UUID()
let name: String
结果:
【讨论】:
您可能希望在有人感到困惑之前添加合并导入行。 @ElTomato 但是它使用的是 SwiftUI,所以 Combine 已经被 SwiftUI 导入了 @ElTomato 为什么不呢?自己试一试。编译得很好。不过,请确保您使用的是 Swift 5.5。在这里导入 Combine 是多余的。 好的。你是对的关于结合。对此我很抱歉。 @ElTomato nw!如果你在import SwiftUI
上 command + click
然后查看定义,则从 SwiftUI 的第二个导入是 Combine。以上是关于如何使用 ForEach 中制作的 TextField 更新结构的属性的主要内容,如果未能解决你的问题,请参考以下文章
使用列表时如何在 foreach 中使用 CheckBoxFor? [复制]
使用 ForEach 循环制作多行 Component() ? [SwiftUI]