0

See the code below (BTW, List has the same issue):

enum Value: String, Equatable {
    case a = "a"
    case b = "b"
}

struct ContentView: View {
    @State var value: Value = .a
    @State var showSheet = false

    var body: some View {
        Form {
            Section {
                switch value {
                case .a:
                    Text("a")
                        .onTapGesture {
                            showSheet = true
                        }
                        .sheet(isPresented: $showSheet) {
                            Button("Set Value to .b") {
                                value = .b
                            }
                        }
                case .b:
                    Text("b")
                }
            }
        }
    }
}

To reproduce the issue, first click on text 'a' to bring up a sheet, then click on button in the sheet to change value. I'd expect the sheet should be dismissed automatically, because the view it's attached to is gone. But it isn't.

If I remove the Form, or replace it with, say, ScrollView, the behavior is as I expected. On other other hand, however, replacing it with List has the same issue (sheet isn't dismissed).

I'm thinking to file a FB, but would like to ask here first. Does anyone know if it's a bug or a feature? If it's a feature, what's the rationale behind it?

I'm currently using this workaround. Any other suggestions other than calling dismiss() in button handler are appreciated.

.onChange(of: value) { _ in
    showSheet = false
}

UPDATE: I think what I really wanted to ask is if there is an "ownership" concept for sheet? I mean:

  1. Does a sheet belongs to the view it's attached to and hence get dismissed when that view is destroyed?
  2. Or it doesn't matter where a sheet is defined in view hierarchy?

Since I never read about the ownership concept, I suppose item 2 is true. But if so, I don't understand why the sheet is dismissed automatically when I removed Form and Section in the above code.

rayx
  • 1,329
  • 10
  • 23

1 Answers1

1

Try this approach of adding showSheet = false just after value = .b in your sheet Button. Also move your .sheet(...) outside the Form, and especially if you are using a List.

 struct ContentView: View {
     @State var value: Value = .a
     @State var showSheet = false

     var body: some View {
         Form {
             Section {
                 switch value {
                 case .a:
                     Text("a")
                         .onTapGesture {
                             showSheet = true
                         }
                 case .b:
                     Text("b")
                 }
             }
         }
         .sheet(isPresented: $showSheet) {  // <-- here
             Button("Set Value to .b") {
                 value = .b
                 showSheet = false   // <-- here
             }
         }
     }
 }
  • Thanks for the answer. That's my coding style too (moving sheet to the topmost and dismissing sheet explicitly. BTW, I was aware that `dismiss()` didn't work in this case, unless I introduced another view.) But sometimes it seems more natural to put `sheet` where it's needed. That's how I found the inconsistent behavior and hence the question. Your answer helped me to realize what I really wanted to ask. I have just updated my question and upvoted your answer, though I'll leave the question open for other answers. Thanks. – rayx Feb 13 '23 at 10:54