Я работаю над приложением, которое имеет 4 различных представления. Основное представление (ContentView), AddView и EditView. И есть также отдельный DataView с классом, где я передаю все данные ObservableObject в другие представления.
На главном экране у меня есть список предметов. В AddView я добавляю элементы в этот список, а из ContentView я хотел бы иметь возможность редактировать добавленные элементы с помощью навигационной ссылки. Таким образом, в главном окне я хотел бы перейти к EditView, изменить значения и снова вернуться к ContentView, где я вижу измененные значения.
Вы бы использовали ObservableObject для этого или мне нужен EnvironmentObject? Поскольку в данный момент EditView не работает, я не могу передать данные из ContentView в EditView, все текстовые поля в EditView пусты, значения не передаются. Он работает для передачи данных из AddView в ContentView, но не из ContentView в EditView.
Может кто-нибудь сказать мне, как данные должны быть связаны со всеми представлениями?
Всего 1 ответ
Вы должны использовать @EnvironmentObject
. Это позволяет обмениваться объектами, что очень важно для обмена данными с другими представлениями.
Я использую объект Shopping
в этом примере. Это приложение будет действовать как список покупок. Весь этот проект доступен на GitHub здесь .
Я действительно надеюсь, что это полезно, так как это заняло довольно много времени. Это всего лишь общий пример того, как эффективно использовать @EnvironmentObject
между View
.
Приложение выглядит так:
(можно скачать через GitHub, см. ссылку выше)
1: Сначала в вашем SceneDelegate.swift
замените:
let contentView = ContentView()
с:
let contentView = ContentView().environmentObject(Shopping())
2: Xcode сейчас будет жаловаться на то, что Shopping
не сделаны, поэтому мы исправим это следующим образом:
class Shopping: ObservableObject {
@Published var list = [
ShoppingItem("Bread", quantity: 1),
ShoppingItem("Milk", quantity: 2),
ShoppingItem("Eggs", quantity: 12)
]
func addItem(_ item: ShoppingItem) {
list.append(item)
}
}
class ShoppingItem: Identifiable {
var name: String
var quantity: Int
init(_ name: String, quantity: Int) {
self.name = name
self.quantity = quantity
}
}
3: Далее нам нужен основной контент, ContentView
:
struct ContentView: View {
@EnvironmentObject private var shopping: Shopping
@State private var newItem: String?
var body: some View {
NavigationView {
List {
ForEach(shopping.list) { item in
NavigationLink.init(destination: EditView(currentItem: item)) {
HStack {
Text(item.name)
Spacer()
Text(String(item.quantity))
Spacer().frame(width: 10)
}
}
}
if newItem != nil {
TextField("New Item", text: $newItem.bound, onCommit: {
if !self.newItem!.isEmpty {
self.shopping.addItem(ShoppingItem(self.newItem!, quantity: 1))
}
self.newItem = nil
})
}
}
.navigationBarTitle("Shopping List")
.navigationBarItems(trailing: Button(action: {
self.newItem = ""
}, label: {
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 25, height: 25)
}))
}
}
}
4: Наряду с этим extension
для работы необязательного @State
s ( @State
здесь , хотя это было упрощено):
extension Optional where Wrapped == String {
var bound: String {
get {
return self ?? ""
}
set {
self = newValue
}
}
}
5: И, наконец, - EditView
, чтобы вы могли редактировать название товара в списке покупок:
struct EditView: View {
let currentItem: ShoppingItem
@EnvironmentObject private var shopping: Shopping
@State private var name = ""
var body: some View {
TextField("Item", text: $name, onCommit: saveName)
.padding()
.background(Color.gray)
.onAppear(perform: setName)
}
private func saveName() {
shopping.objectWillChange.send()
currentItem.name = name
}
private func setName() {
name = currentItem.name
}
}