Reputation: 4796
Working with Beta4, it seems that the bug is still existing. The following sequence of views (a list, where a tap on a list entry opens another list) allows to present the ListView
exactly once; the onDisappear
is never called, so the showModal
flag changes, but does not triggers the redisplay of ListView
when tapped again. So, for each GridCellBodyEntry
, the .sheet
presentation works exactly once, and then never again.
I tried around with several suggestions and workarounds, but none worked (e.g., encapsulating with a NavigationViewModel). I even tried to remove the List, because there was an assumption that the List
causes that behaviour, but even this did not change anything.
Are there any ideas around?
The setup:
GridCellBody
with this view:var body: some View {
GeometryReader { geometry in
VStack {
List {
Section(footer: self.footerView) {
ForEach(self.rawEntries) { rawEntry in
GridCellBodyEntry(entityType: rawEntry)
}
}
}
.background(Color.white)
}
}
}
GridCellBodyEntry
with this definition:struct GridCellBodyEntry: View {
let entityType: EntityType
let viewModel: BaseViewModel
init(entityType: EntityType) {
self.entityType = entityType
self.viewModel = BaseViewModel(entityType: self.entityType)
}
@State var showModal = false {
didSet {
print("showModal: \(showModal)")
}
}
var body: some View {
Group {
Button(action: {
self.showModal.toggle()
},
label: {
Text(entityType.localizedPlural ?? "")
.foregroundColor(Color.black)
})
.sheet(isPresented: $showModal, content: {
ListView(showModal: self.$showModal,
viewModel: self.viewModel)
})
}.onAppear{
print("Profile appeared")
}.onDisappear{
print("Profile disappeared")
}
}
}
ListView
with this definition:struct ListView: View {
// MARK: - Private properties
// MARK: - Public interface
@Binding var showModal: Bool
@ObjectBinding var viewModel: BaseViewModel
// MARK: - Main view
var body: some View {
NavigationView {
VStack {
List {
Section(footer: Text("\(viewModel.list.count) entries")) {
ForEach(viewModel.list, id: \.objectID) { item in
NavigationLink(destination: ItemView(),
label: {
Text("\(item.objectID)")
})
}
}
}
}
.navigationBarItems(leading:
Button(action: {
self.showModal = false
}, label: {
Text("Close")
}))
.navigationBarTitle(Text(viewModel.entityType.localizedPlural ?? ""))
}
}
}
BaseViewModel
(excerpt): class BaseViewModel: BindableObject {
/// The binding support.
var willChange = PassthroughSubject<Void, Never>()
/// The context.
var context: NSManagedObjectContext
/// The current list of typed items.
var list: [NSManagedObject] = []
// ... other stuff ...
}
where willChange.send()
is called whenever something changes (create, modify, delete operations).
Upvotes: 5
Views: 1122
Reputation: 1679
This is a variant of swiftUI PresentaionLink does not work second time
The following simplified code exhibits the behavior you're experiencing (the sheet only displays once):
import SwiftUI
struct ContentView: View {
@State var isPresented = false
@State var whichPresented = -1
var body: some View {
NavigationView {
List {
ForEach(0 ..< 10) { i in
Button(action: {
self.whichPresented = i
self.isPresented.toggle()
})
{ Text("Button \(i)") }
}.sheet(isPresented: $isPresented, content: {
Text("Destination View \(self.whichPresented)") })
}
}
}
}
There appears to be a bug in SwiftUI when you put the .sheet
inside a List or a ForEach. If you move the .sheet
outside of the List, you should be able to get the correct behavior.
import SwiftUI
struct ContentView: View {
@State var isPresented = false
@State var whichPresented = -1
var body: some View {
NavigationView {
List {
ForEach(0 ..< 10) { i in
Button(action: {
self.whichPresented = i
self.isPresented.toggle()
})
{ Text("Button \(i)") }
}
}
}.sheet(isPresented: $isPresented, content: { Text("Destination View \(self.whichPresented)") })
}
}
Upvotes: 3