I'm attempting to dismiss the detail view in a master detail view on an iPad (e.g. if that item is deleted in the master view and needs to blank out the right hand side).
import SwiftUI
struct ContentView: View {
@State var selection: String?
var body: some View {
NavigationView {
List {
NavigationLink(tag: "item1", selection: $selection) {
DetailView(item: "Item 1", selection: $selection)
} label: {
Text("Item 1")
}
NavigationLink(tag: "item2", selection: $selection) {
DetailView(item: "Item 2", selection: $selection)
} label: {
Text("Item 2")
}
NavigationLink(tag: "item3", selection: $selection) {
DetailView(item: "Item 3", selection: $selection)
} label: {
Text("Item 3")
}
}
Text("Blank View")
}
}
}
struct DetailView: View {
let item: String
@Binding var selection: String?
@Environment(\.dismiss) var dismiss
var body: some View {
VStack {
Text("Detail for \(item)")
Button {
selection = nil
// dismiss()
} label: {
Text("Dismiss")
}
}
}
}
This is an example that demonstrates the issue. When dismiss is pressed it nils the selection which on iPhone dismisses the detail controller. On the iPad it keeps the detail view there, rather than returning to the blank view.
Here is the view before selecting an item. You can see the blank view on the right hand side.
After selecting an item the left hand side is selected, and the right hand side shows the appropriate detail view.
After dismissing the view by nilling the selection (or using the environment dismiss) the selection on the left disappears as it should, but the detail view on the right stays put. This should have disappeared and the blank view should show again.
CodePudding user response:
If you're willing to give up the splitView
(having the items on that sidebar), you can override the default navigation settings to make it behave as it would on an iPhone (using stacked navigation views), then the dismiss button works just fine. Add this modifier .navigationViewStyle(StackNavigationViewStyle())
to NavigationView
:
struct ContentView: View {
@State var selection: String?
var body: some View {
NavigationView {
List {
NavigationLink(tag: "item1", selection: $selection) {
DetailView(item: "Item 1", selection: $selection)
} label: {
Text("Item 1")
}
NavigationLink(tag: "item2", selection: $selection) {
DetailView(item: "Item 2", selection: $selection)
} label: {
Text("Item 2")
}
NavigationLink(tag: "item3", selection: $selection) {
DetailView(item: "Item 3", selection: $selection)
} label: {
Text("Item 3")
}
}
Text("Blank View")
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
CodePudding user response:
I have worked out a solution that works however it requires some extra logic. It needs some sort of check to see if the data is there and if it isn't then conditionally show the main view or a blank view.
Here is the modified DetailView
for the example I used in my question. If the selection binding is nil then display the blank view again.
struct DetailView: View {
let item: String
@Binding var selection: String?
var body: some View {
if selection != nil {
VStack {
Text("Detail for \(item)")
Button {
selection = nil
} label: {
Text("Dismiss")
}
}
} else {
Text("Blank View")
}
}
}
In the case of an NSManagedObject
you can check object.managedObjectContext != nil
. I decided to make this an extension to make this easier.
extension View {
@ViewBuilder
public func blankWithoutContext<BlankView>(_ object: NSManagedObject, blankView: () -> BlankView) -> some View where BlankView: View {
if object.managedObjectContext != nil {
self
} else {
blankView()
}
}
}
Which is used like this:
MyView()
.blankWithoutContext(object) {
Text("Blank View")
}