I've tracked down an annoying bug in my code and managed to recreate the issue using xCode's default iOS app as a starting point and adding a few bits of code.
Problem
Modifying part of an @ObservedObject (core data entity) causes the View to immediately jump back, when the following conditions are true:
- The View is at least 2 links deep
- @AppStorage is declared in another view
I've confirmed that (1) if I skip SubView2 and link straight to SubView3 (the editing view) then the behavior does not occur and (2) if I comment out the @AppStorage line in SettingsView() then the behavior does not occur.
In SubView3, I tap "Increase" to increase item.value 1. This causes the view to jump back to SubView2.
Code
I started a default Xcode project with CloudKit and SwiftUI, then added a SettingsView (which contains the @AppStorage) and two SubViews to view a specific item.
import SwiftUI
import CoreData
struct SettingsView: View {
@AppStorage("userSettingOne") var userSettingOne = false
var body: some View {
Text("Hello")
}
}
struct SubView2: View {
@ObservedObject var item2: Item
@State private var showSubView3 = false
var body: some View {
NavigationLink(destination: SubView3(item3: item2) , isActive: $showSubView3) { EmptyView() }
Text("Value: \(item2.value)")
Button("SubView3", action: {
showSubView3 = true
})
.navigationTitle("SubView2")
}
}
struct SubView3: View {
@ObservedObject var item3: Item
var body: some View {
VStack {
Text("Value: \(item3.value)")
Button("Increase", action: {
item3.value = 1
})
}
.navigationTitle("SubView3")
}
}
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
animation: .default)
private var items: FetchedResults<Item>
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink("Item", destination: SubView2(item2: item))
}
.onDelete(perform: deleteItems)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
HStack {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
NavigationLink(destination: SettingsView(), label: {
Label("", systemImage: "gear").labelStyle(.iconOnly)
})
}
}
}
Text("Select an item")
}
}
// Boilerplate/default code below here
private func addItem() {
withAnimation {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
offsets.map { items[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
CodePudding user response:
try adding .navigationViewStyle(.stack) to your NavigationView


