skip to Main Content

Context

I have a Menu (including multiple Buttons) inside a SwiftUI Toolbar and a .sheet() Modifier inside the Toolbar, too.

The problem is, that pressing the Button with the show.toggle() action does not present the Sheet as expected.

Important: I noticed the weird behaviour, that when I give showOption an initial value like var showOption: Option? = .option1, this works perfectly fine (except the initial Sheet popup of course). However, once I initiate the showOption property with nil, it breaks.


Code

struct MainView: View {
    var showOption: Option?
    // var showOption: Option? = .option -> this would work perfectly fine.

    var body: some View {
        NavigationStack {
            Text("Hello World")
                .toolbar {
                    ToolbarItem {
                        Menu { 
                            ... Button(action: { option = .option1 } { ... } ... 
                        }
                        .sheet(item: $showOption) { option in
                            switch option {
                            case .option1: Text("Hello World 1")
                            ...
                            }
                        }
                    }
                }
        }
    }
}

Questions

  • Is this caused by the .sheet() being inside the Toolbar or is there anything else I missed?
  • How can I solve this (ideally without moving the .sheet() outside of the Toolbar)?

3

Answers


  1. if you move it outside of Menu (still in Toolbar) it works fine:

    struct ContentView: View {
        
        @State private var show: Bool = false
        
        var body: some View {
            NavigationStack {
                Text("Hello World")
                    .toolbar {
                        ToolbarItem {
                            Menu("Test") {
                                Button("Show") {
                                    show.toggle()
                                }
                            }
                            .sheet(isPresented: $show) {
                                Text("Hello World 2")
                            }
                        }
                    }
            }
        }
    }
    
    Login or Signup to reply.
  2. In SwiftUI whenever we apply a modifier to view it actually create a new view with that change applied instead of modifying the existing view in the place. This behavior makes sense: our views only hold the exact properties we give them. Have a look at the sample code below.

    struct ContentView: View {
        
        @State private var show: Bool = false
        
        var body: some View {
            NavigationStack {
                Text("Hello World")
                    .toolbar {
                        ToolbarItem {
                            Menu("Test") {
                                Button("Show") {
                                    print(type(of: self.body))
                                    show.toggle()
                                }
                            }
                            .sheet(isPresented: $show) {
                                Text("Hello World 2")
                            }
                        }
                    }
            }
        }
    }
    

    Swift’s type(of:) method prints the exact type of a particular view/value, if we add the .sheet modifier under the Menu, print(of:) shows the following output

    ModifiedContent<Text, ToolbarModifier<(),
    TupleToolbarContent<ToolbarItem<(), ModifiedContent<Menu<Text,
    Button>, SheetPresentationModifier>>>>>

    Nests the elements in the following way:
    Text -> ToolbarModifier -> TupleToolbarContent -> ToolbarItem -> ModifiedContent -> Menu -> Text -> Button -> SheetPresentationModifier

    if we add the .sheet modifier under the button print(of:) shows the following output

    ModifiedContent<Text, ToolbarModifier<(),
    TupleToolbarContent<ToolbarItem<(), Menu<Text,
    ModifiedContent<Button, SheetPresentationModifier>>>>>

    Nest the elements in the following way:
    Text -> ToolbarModifier -> TupleToolbarContent -> ToolbarItem -> Menu -> Text -> ModifiedContent -> Button -> SheetPresentationModifier

    So, as we discussed earlier that the order of modifier effect on the functionality of SwiftUI. If we add .sheet to the Menu element then it would have additional functionality or appearance that is specific to the ModifiedContent and shows the sheet. While for the case of button ModifiedContent does not work.

    Login or Signup to reply.
  3. The answer is still the same and it still works, if you move .sheet out of the Menu:

    enum Option: Int, Identifiable {
        case option1 = 1
        case option2 = 2
        case option3 = 3
        
        var id: Int { self.rawValue }
    }
    
    struct ContentView: View {
        @State private var showOption: Option?
        
        var body: some View {
            NavigationStack {
                Text("Hello World")
                    .toolbar {
                        ToolbarItem {
                            Menu("Menu") {
                                Button("1") { showOption = .option1 }
                                Button("2") { showOption = .option2 }
                                Button("3") { showOption = .option3 }
                            }
                            .sheet(item: $showOption) { option in
                                Text("Hello World (option.rawValue)")
                            }
                        }
                    }
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search