我有一个包含行的List,这些行可以推动View。该视图具有另一个推入另一个视图的列表。当数据更改时,原始列表和第一个推送的列表将更新。但是,最后一个视图在推送时不会更新。而且,当我向后滑动时,视图不再更新,即使过去也是如此。
HomeView> UserView> ItemView
用户和项目是可识别的结构。我曾尝试使它们可哈希并使用id: \.self
,但这似乎也不起作用。
class App: ObservableObject {
@Published var users = [User]()
}
struct HomeView {
@EnvironmentObject var app: App
var body {
List {
Section {
ForEach(app.users) { user in
NavigationLink(destination: UserView(user: user)) {
Text(user.name)
}
}
}
}
}
}
// Updates fine when `app.users` updates
// Stops updating after going back from ItemView
struct UserView {
let user: User
var body {
List {
Section {
ForEach(user.items) { item in
NavigationLink(destination: ItemView(user: user, item: item)) {
Text(item.name)
}
}
}
}
}
}
/// Does not update when app.users updates
struct ItemView {
let user: User
let item: Item
var body {
List {
Section {
ForEach(item.details) { detail in
Text(detail)
}
}
}
}
}
答案 0 :(得分:1)
好的,我想我已经开始了解你想要什么了。这样的事情怎么样:
struct ContentView: View {
@EnvironmentObject var app: App
var body: some View {
NavigationView {
List(0..<app.users.count) { i in
NavigationLink(destination: UserView(user: self.$app.users[i])) {
Text(self.app.users[i].name)
}
}
}
}
struct UserView: View {
@Binding var user: User
var body: some View {
List(0..<user.items.count) { i in
NavigationLink(destination:
ItemView(item: self.$user.items[i])) {
Text(self.user.items[i].name)
}
}
}
}
struct ItemView: View {
@Binding var item: Item
@State var hasChanged = false
var body: some View {
VStack {
Button(action: {
self.item.details.append(contentsOf: ["wx","wy"])
self.hasChanged.toggle()
}) {
Text("Add an item")
}
List(0..<item.details.count, id: \.self) { i in
Text(self.item.details[i])
}
}
}
}
答案 1 :(得分:0)
这是我所做的测试,并且运行良好,一切都会按预期更新。
struct User: Identifiable {
var id: String
var name: String
var items: [Item]
}
struct Item: Identifiable {
var id: String
var name: String
var details: [String]
}
class App: ObservableObject {
@Published var users = [User]()
init() {
let items1 = [Item(id: UUID().uuidString, name: "item1", details: ["d1","d2"]), Item(id: UUID().uuidString, name: "item2", details: ["d3","d4"])]
let items2 = [Item(id: UUID().uuidString, name: "item3", details: ["e1","e2"]), Item(id: UUID().uuidString, name: "item4", details: ["e3","e4"])]
users.append(User(id: UUID().uuidString, name: "user1", items: items1))
users.append(User(id: UUID().uuidString, name: "user2", items: items2))
}
}
struct ContentView: View {
@ObservedObject var app = App()
var body: some View {
NavigationView {
List {
ForEach(app.users) { user in
NavigationLink(destination: UserView(user: user)) {
Text(user.name)
}
}
}
}
}
}
struct UserView: View {
@State var user: User
var body: some View {
List {
ForEach(user.items) { item in
NavigationLink(destination: ItemView(item: item)) {
Text(item.name)
}
}
}
}
}
struct ItemView: View {
@State var item: Item
var body: some View {
List {
ForEach(item.details, id: \.self) { detail in
Text(detail)
}
}
}
}
答案 2 :(得分:0)
我遇到了相同的问题,现在我根据本主题中的答案开发如下解决方案
import SwiftUI
struct TItem: Identifiable {
var id: String
var name: String
var accounts: [TAccount]
}
struct TAccount: Identifiable {
var id: String
var name: String
}
class TApp: ObservableObject {
@Published var items: [TItem] = [
TItem(id: "a", name: "A", accounts: [
TAccount(id: "1", name: "Alice"),
TAccount(id: "2", name: "Amazon"),
TAccount(id: "3", name: "Arfa")
]),
TItem(id: "b", name: "B", accounts: [
TAccount(id: "1", name: "Boris"),
TAccount(id: "2", name: "Bob"),
TAccount(id: "3", name: "Blaster")
])
]
}
struct TRootView: View {
var body: some View {
NavigationView {
TContentView().environmentObject(TApp())
}
}
}
struct TContentView: View {
@EnvironmentObject var app: TApp
var body: some View {
List {
ForEach(0..<app.items.count) { index in
let item = $app.items[index]
let wItem = item.wrappedValue
NavigationLink(destination: TItemView(item: item)) {
VStack(alignment: .leading) {
Text("\(wItem.id) - \(wItem.name)")
HStack {
ForEach(wItem.accounts) { account in
Text("\(account.name)")
.font(.callout)
.foregroundColor(.gray)
}
}
}
}
}
Button(action: { app.items[0].name = "XXX" }) {
Text("Change Item Name")
}
}
}
}
struct TItemView: View {
@Binding var item: TItem
var body: some View {
List {
Text("\(item.id) - \(item.name)")
ForEach(0..<item.accounts.count) { index in
NavigationLink(destination: TAccountView(account: $item.accounts[index])) {
Text("\(item.accounts[index].id) - \(item.accounts[index].name)")
}
}
Button(action: { self.$item.name.wrappedValue = "XXX" }) {
Text("Change Item Name")
}
}
}
}
struct TAccountView: View {
@Binding var account: TAccount
var body: some View {
List {
Text("\(account.id) - \(account.name)")
Button(action: { $account.name.wrappedValue = "CCC" }) {
Text("Change Account Name")
}
}
}
}
struct TRootView_Previews: PreviewProvider {
static var previews: some View {
TRootView()
}
}
答案 3 :(得分:-1)
经过长时间的搜索,我想出了我在互联网上其他地方尚未见到的东西。我正在使用@State从父视图初始化我的视图,并使用onReceive更新它。此外,onReceive / onAppear会检查该项是否仍然有效,并在需要时弹出视图。进行初始化来设置国家并将其设为私有可能更正确。
这样做的主要原因是删除导致崩溃。这是我制作的一个完整示例,用于测试更改和删除源。
struct Item: Identifiable {
var id: String
var name: String
var accounts: [Account]
}
struct Account: Identifiable {
var id: String
var name: String
}
class App: ObservableObject {
@Published var items: [Item] = [
Item(id: "a", name: "A", accounts: [
Account(id: "1", name: "one"),
Account(id: "2", name: "two"),
Account(id: "3", name: "three")
])
]
}
struct RootView: View {
var body: some View {
NavigationView {
ContentView().environmentObject(App())
}
}
}
struct ContentView: View {
@EnvironmentObject var app: App
var body: some View {
List {
ForEach(app.items) { item in
NavigationLink(destination: ItemView(item: item)) {
Text("\(item.id) - \(item.name)")
}
}
Button(action: { self.app.items[0].name = "XXX" }) {
Text("Change Item Name")
}
Button(action: { self.app.items = [] }) {
Text("Clear")
}
}
}
}
struct ItemView: View {
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var app: App
@State var item: Item
var body: some View {
List {
Text("\(item.id) - \(item.name)")
ForEach(item.accounts) { account in
NavigationLink(destination: AccountView(item: self.item, account: account)) {
Text("\(account.id) - \(account.name)")
}
}
Button(action: { self.app.items[0].name = "XXX" }) {
Text("Change Item Name")
}
Button(action: { self.app.items[0].accounts[0].name = "AAA" }) {
Text("Change Account Name")
}
Button(action: { self.app.items = [] }) {
Text("Clear")
}
}
.onReceive(app.$items) { items in
guard let item = items.first(where: { $0.id == self.item.id }) else {
self.presentationMode.wrappedValue.dismiss()
return
}
self.item = item
}
.onAppear {
if !self.app.items.contains(where: { $0.id == self.item.id }) {
self.presentationMode.wrappedValue.dismiss()
}
}
}
}
struct AccountView: View {
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var app: App
@State var item: Item
@State var account: Account
var body: some View {
List {
Text("\(item.id) - \(item.name)")
Text("\(account.id) - \(account.name)")
Button(action: { self.app.items[0].name = "XXX" }) {
Text("Change Item Name")
}
Button(action: { self.app.items[0].accounts[0].name = "AAA" }) {
Text("Change Account Name")
}
Button(action: { self.app.items = [] }) {
Text("Clear")
}
}
.onReceive(app.$items) { items in
guard
let item = items.first(where: { $0.id == self.item.id }),
let account = item.accounts.first(where: { $0.id == self.account.id })
else {
self.presentationMode.wrappedValue.dismiss()
return
}
self.item = item
self.account = account
}
}
}