[英]SwiftUI view crashes when updating NavigationLinks in same NavigationView
To explain this behavior, I have built a simplified project that creates same behavior as my working project.为了解释这种行为,我构建了一个简化的项目,它创建了与我的工作项目相同的行为。 Please keep in mind that my real project is much bigger and have more constraints than this one.请记住,我的真实项目比这个更大,并且有更多的限制。
I have a simple struct
with only an Int
as property (named MyObject
) and a model that stores 6 MyObject
.我有一个简单的struct
,只有一个Int
作为属性(名为MyObject
)和一个存储 6 个MyObject
的 model 。
struct MyObject: Identifiable {
let id: Int
}
final class Model: ObservableObject {
let objects: [MyObject] = [
MyObject(id: 1),
MyObject(id: 2),
MyObject(id: 3),
MyObject(id: 4),
MyObject(id: 5),
MyObject(id: 6)
]
}
Please note that in my real project, Model
is more complicated (loading from JSON).请注意,在我的实际项目中, Model
更复杂(从 JSON 加载)。
Then I have a FavoritesManager
that can add and remove a MyObject
as favorite.然后我有一个FavoritesManager
管理器,它可以添加和删除一个MyObject
作为收藏夹。 It has a @Published
array of ids ( Int
):它有一个@Published
id 数组( Int
):
final class FavoritesManager: ObservableObject {
@Published var favoritesIds = [Int]()
func addToFavorites(object: MyObject) {
guard !favoritesIds.contains(object.id) else { return }
favoritesIds.append(object.id)
}
func removeFromFavorites(object: MyObject) {
if let index = favoritesIds.firstIndex(of: object.id) {
favoritesIds.remove(at: index)
}
}
}
My App
struct creates Model and FavoritesManager and injects them in my first view:我的App
结构创建 Model 和 FavoritesManager 并在我的第一个视图中注入它们:
@main
struct testAppApp: App {
@StateObject private var model = Model()
@StateObject private var favoritesManager = FavoritesManager()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(model)
.environmentObject(favoritesManager)
}
}
}
Now I have few views that display my objects:现在我有几个视图显示我的对象:
ContentView
displays a link to ObjectsListView
using NavigationLink
and a FavoritesView
. ContentView
使用NavigationLink
和FavoritesView
显示指向ObjectsListView
的链接。 In my real project, ObjectsListView
does not display all objects, only a selection (by category for example).在我的真实项目中, ObjectsListView
不显示所有对象,只显示一个选择(例如按类别)。
struct ContentView: View {
@EnvironmentObject var model: Model
var body: some View {
NavigationView {
VStack {
NavigationLink(
destination: ObjectsListView(objects: model.objects), label: {
Text("List of objects")
}
)
FavoritesView()
.padding()
}
}
}
}
FavoritesView
displays ids of current selected favorites. FavoritesView
显示当前所选收藏夹的 id。 It observes FavoritesManager
to update its body when a favorite is added/removed.它观察FavoritesManager
夹管理器在添加/删除收藏夹时更新其主体。
Important: it creates a new NavigationLink
to favorites details view.重要提示:它会创建一个新的NavigationLink
到收藏夹详细信息视图。 If you remove this link, bug does not happen.如果您删除此链接,则不会发生错误。
struct FavoritesView: View {
@EnvironmentObject var model: Model
@EnvironmentObject var favoritesManager: FavoritesManager
var body: some View {
HStack {
Text("Favorites:")
let favoriteObjects = model.objects.filter( { favoritesManager.favoritesIds.contains($0.id) })
ForEach(favoriteObjects) { object in
NavigationLink(destination: DetailsView(object: object), label: {
Text("\(object.id)")
})
}
}
}
}
ObjectsListView
simply displays objects and makes a link to DetailsView
. ObjectsListView
只是显示对象并链接到DetailsView
。
struct ObjectsListView: View {
@State var objects: [MyObject]
var body: some View {
List {
ForEach(objects) { object in
NavigationLink(destination: DetailsView(object: object), label: { Text("Object \(object.id)")})
}
}
}
}
DetailsView
displays object details and add a button to add/remove current object as favorite. DetailsView
显示 object 详细信息,并添加一个按钮以添加/删除当前 object 作为收藏夹。 If you press the star, current object will be added as favorite (as expected) and FavoritesView
will be updated to display/remove this new id (as expected).如果您按下星号,当前的 object 将被添加为收藏夹(如预期的那样), FavoritesView
将更新以显示/删除此新 ID(如预期的那样)。
But here is the bug: as soon as you press the star, current view ( DetailsView
) disappears and it goes back (without animation) to previous view ( ObjectsListView
).但这里有一个错误:一旦您按下星号,当前视图 ( DetailsView
) 就会消失,并且它会返回(没有动画)到前一个视图 ( ObjectsListView
)。
struct DetailsView: View {
@EnvironmentObject var favoritesManager: FavoritesManager
@State var object: MyObject
var body: some View {
VStack {
HStack {
Text("You're on object \(object.id) detail page")
Button(action: {
if favoritesManager.favoritesIds.contains(object.id) {
favoritesManager.removeFromFavorites(object: object)
},
else {
favoritesManager.addToFavorites(object: object)
}
}, label: {
Image(systemName: favoritesManager.favoritesIds.contains(object.id) ? "star.fill" : "star")
.foregroundColor(.yellow)
})
}
Text("Bug is here: if your press the star, it will go back to previous view.")
.font(.caption)
.padding()
}
}
}
You can download complete example project here .您可以在此处下载完整的示例项目。
You may ask why I don't display objects list in ContentView
directly.你可能会问为什么我不直接在ContentView
中显示对象列表。 It is because of my real project.这是因为我的真实项目。 It displays objects classified by categories in lists.它在列表中显示按类别分类的对象。 I have reproduce this behavior with ObjectsListView
.我已经用ObjectsListView
重现了这种行为。
I also need to separated Model
and FavoritesManager
because in my real project I don't want that my ContentView
's body is updated everytime a favorite is added/removed.我还需要将Model
和FavoritesManager
分开,因为在我的实际项目中,我不希望每次添加/删除收藏夹时都会更新我的ContentView
的正文。 That's why I have moved all favorites displaying in FavoritesView
.这就是为什么我移动了 FavoritesView 中显示的所有FavoritesView
。
I believe that this issue happens because I add/remove NavigationLinks
in current NavigationView
.我相信这个问题的发生是因为我在当前NavigationView
NavigationLinks
And it looks like it is reloading whole navigation hierarchy.看起来它正在重新加载整个导航层次结构。 But I can't figure out how to get expected result (staying in DetailsView
when pressing star button) with same views hierarchy.但我不知道如何获得具有相同视图层次结构的预期结果(按下星形按钮时留在DetailsView
中)。 I may do something wrong somewhere...我可能在某个地方做错了什么......
Add .navigationViewStyle(.stack)
to your NavigationView
in ContentView
.在ContentView
.navigationViewStyle(.stack)
添加到NavigationView
中。 With that, your code works well for me, I can press the star in DetailsView
, and it remains displayed.这样,您的代码对我来说效果很好,我可以按DetailsView
中的星号,它仍然显示。 It does not go back to the previous View.它不会 go 回到之前的视图。 Using macos 12.2, Xcode 13.2, targets ios 15.2 and macCatalyst 12.1.使用 macos 12.2、Xcode 13.2,针对 ios 15.2 和 macCatalyst 12.1。 Tested on real devices.在真实设备上测试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.