#swift #xcode #list #swiftui #mutable
Вопрос:
Xcode 12. Код и жизненный цикл являются быстрыми.
Я посмотрел на вопросы, связанные с другими людьми, и мне кажется, что они просто выкладывают здесь весь свой код проектов; немного сложно выбрать из него то, что мне нужно. Итак, я разбил проблему на самый простой пример, который я могу.
Цель состоит в том, чтобы число X повторялось, когда я нажимаю кнопку .
Заранее спасибо!
struct InfoData: Identifiable { var id = UUID() var nameX: String var numberX: Int } class ContentX: ObservableObject { @Published var infoX = [ InfoData(nameX: "Example", numberX: 1) ] } struct ContentView: View { @StateObject var contentX = ContentX() var body: some View { List(contentX.infoX) { item in HStack { Text("(item.nameX)") Spacer() Button(" ") { item.numberX = 1 //Eror shows up here << } Text("(item.numberX)") } } } }
Комментарии:
1. Вызовите contentX и попросите его напрямую обновить массив infoX требуемым значением.
Ответ №1:
В синтаксисе, который вы используете, item
это неизменяемое значение, как сообщает вам ошибка. Вы не можете изменить его, потому что он не представляет собой истинное соединение с массивом, из которого он получен, — это просто временная читаемая копия, используемая в List
итерации.
Если вы можете перейти на Xcode 13, у вас будет доступ к так называемому синтаксису привязки элементов List
, и ForEach
это позволит вам сделать это:
struct ContentView: View { @StateObject var contentX = ContentX() var body: some View { List($contentX.infoX) { $item in //<-- Here HStack { Text("(item.nameX)") Spacer() Button(" ") { item.numberX = 1 } Text("(item.numberX)") } } } }
Это дает вам Binding
возможность item
изменять значение, позволяя изменять его значения и отражать их в исходном массиве.
До Xcode 13/Swift 5.5 вам нужно было бы определить свой собственный способ изменения элемента в массиве. Это одно из решений:
class ContentX: ObservableObject { @Published var infoX = [ InfoData(nameX: "Example", numberX: 1) ] func alterItem(item: InfoData) { self.infoX = self.infoX.map { $0.id == item.id ? item : $0 } } } struct ContentView: View { @StateObject var contentX = ContentX() var body: some View { List(contentX.infoX) { item in HStack { Text("(item.nameX)") Spacer() Button(" ") { var newItem = item newItem.numberX = 1 contentX.alterItem(item: newItem) } Text("(item.numberX)") } } } }
Или другой вариант, в котором используется пользовательский Binding
:
class ContentX: ObservableObject { @Published var infoX = [ InfoData(nameX: "Example", numberX: 1) ] func bindingForItem(item: InfoData) -> Binding<InfoData> { .init { self.infoX.first { $0.id == item.id }! } set: { newValue in self.infoX = self.infoX.map { $0.id == item.id ? newValue : $0 } } } } struct ContentView: View { @StateObject var contentX = ContentX() var body: some View { List(contentX.infoX) { item in HStack { Text("(item.nameX)") Spacer() Button(" ") { contentX.bindingForItem(item: item).wrappedValue.numberX = 1 } Text("(item.numberX)") } } } }
Комментарии:
1. Большое вам спасибо! Я совершенно новичок в коде, поэтому иногда я просто еще не понял, как все работает. Я с нетерпением жду Xcode 13, но сейчас я нахожусь в середине курса, и я беспокоюсь, что не смогу следить за ним, если обновлю. Хотя, возможно, мой страх необоснован.
2. Да, неплохая идея, чтобы все было удобно, пока ты учишься.