AndreaMiotto / PartialSheet

A SwiftUI Partial Sheet fully customizable with dynamic height
https://github.com/AndreaMiotto/PartialSheet/wiki
MIT License
1.73k stars 194 forks source link

The sheet disappears when the view is refreshed. #149

Closed qinzhe closed 1 year ago

qinzhe commented 2 years ago

The following code, when the button in the sheet is clicked, the sheet will disappear. If the @EnvironmentObject in PartialSheetTestView is removed, the sheet behaves normally.

` import SwiftUI import PartialSheet

@main struct PartialSheet_ExampleApp: App { @StateObject var model = PartialSheetTestModel() var body: some Scene { WindowGroup { PartialSheetTestView() .environmentObject(model) } } }

struct PartialSheetTestView: View { @EnvironmentObject var model: PartialSheetTestModel //this line @State var sheet = false var body: some View { VStack { Spacer() PSButton(isPresenting: $sheet) { Text("show sheet") } .partialSheet(isPresented: $sheet) { CountSheetView() } Spacer() } .attachPartialSheetToRoot() } }

struct CountSheetView: View { @EnvironmentObject var model: PartialSheetTestModel var body: some View { VStack { Button { model.count += 1 } label: { Text("+ 1") } } .frame(height: 120) } }

class PartialSheetTestModel: ObservableObject { @Published var count: Int = 0 } `

maciesielka commented 2 years ago

The issue here is where you've chosen to use attachPartialSheetToRoot(). When PartialSheetTestModel publishes new updates (via the count property in your example), any view that listens to its updates will re-render by running body. If PartialSheetTestView watches PartialSheetTestModel for updates, that means that it will re-run attachPartialSheetToRoot() and that will reset the internal state of the partial sheet.

The high level idea to fix this would be to use attachPartialSheetToRoot() in a place that won't re-run while your sheet is displayed -- ideally close to the view root. In your specific example, one solution might be to introduce an intermediary view to inject your StateObject so that you can attach the partial sheet at a level that isn't changing when it does. Might be a bunch of workarounds based on your use-case though

@main
struct PartialSheet_ExampleApp: App {
    var body: some Scene {
        WindowGroup {
          InjectionView()
            .attachPartialSheetToRoot()
        }
    }
}

struct InjectionView: View {
    @StateObject var model = PartialSheetTestModel()

    var body: some View {
        PartialSheetTestView()
            .environmentObject(model)
    }
}