skip to Main Content

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.
Before selecting

After selecting an item the left hand side is selected, and the right hand side shows the appropriate detail view.
After selecting

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.
After dismissing

2

Answers


  1. Chosen as BEST ANSWER

    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")
        }
    

  2. 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())
        }
    }
    

    enter image description here

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search