Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI sheet not animating dismissal on macOS Big Sur

I would like the sheet dismissal animated just like the appearance but reversed. I think this is also the standard behavior. You can see it in Xcode for example when you create a new file.

But as you can see it just disappears without animation

Demo

Here's my code:

struct ContentView: View {

    @State var isAnotherViewPresented: Bool = false

    var body: some View {
        HStack {
            Button(action: {
                isAnotherViewPresented.toggle()
            }, label: {
                Text("Button")
            }).sheet(isPresented: $isAnotherViewPresented, content: {
                AnotherView()
            })
        }
        .frame(width: 500, height: 300, alignment: .center)
    }
}

struct AnotherView: View {

    @Environment(\.presentationMode) var presentationMode

    var body: some View {
        VStack {
            Button(action: {
                presentationMode.wrappedValue.dismiss()
            }, label: {
                Text("Close")
            })
        }.padding()
    }
}

I'm on

  • Mac mini (M1, 2020)
  • macOS Big Sur 11.1 (20C69)
  • Xcode 12.3 (12C33)

But I can reproduce this on a

  • Mac mini (2018)
  • macOS Big Sur 11.0.1 (20B29)
  • Xcode 12.2 (12B45b)
like image 500
Lukas Würzburger Avatar asked Dec 21 '20 09:12

Lukas Würzburger


3 Answers

I finally figured out how to do it, in my SwiftUI app it works if I do this while closing the sheet:

isSheetVisible = false
NSApp.mainWindow?.endSheet(NSApp.keyWindow!)

Example:

struct SheetView: View {
    @Binding var isSheetVisible: Bool

    var body: some View {
        Button("Close") {
            isSheetVisible = false
            NSApp.mainWindow?.endSheet(NSApp.keyWindow!)
        }
    }
}

Working

like image 117
arjndr Avatar answered Oct 24 '22 00:10

arjndr


I like this mix:

assuming you have:

@Environment(\.presentationMode) var presentationMode

then:

presentationMode.wrappedValue.dismiss() // this updates the binding from .sheet(isPresented: ...) to false
NSApp.mainWindow?.endSheet(NSApp.keyWindow!) // this runs the animation
like image 22
Everton Cunha Avatar answered Oct 24 '22 01:10

Everton Cunha


Disclaimer: I had/have the same problem where if I try to dismiss a sheet through a binding, it just disappears instead of having an animation. The below solution worked for me but I am unclear as to why its working.

Solution

Apparently the view you "attach" a modal to has an impact on how it transitions from being presented to not. For instance, in your code the sheet is attached to the button view:

Button(action: {
  isAnotherViewPresented.toggle()
 }, label: {
   Text("Button")
      // sheet is attached here
 }).sheet(isPresented: $isAnotherViewPresented, content: {
    AnotherView()
})

When you call presentationMode.wrappedValue.dismiss() in the second view the modal jolts and disappears instead of sliding away. However, if you attach the sheet to the outer HStack view, then it works and it slides away as expected:

var body: some View {
    HStack {
        Button(action: {
            isAnotherViewPresented.toggle()
        }, label: {
            Text("Button")
        })
    }
    .frame(width: 500, height: 300, alignment: .center)
    .sheet(isPresented: $isAnotherViewPresented, content: {
       AnotherView()
    })
    // sheet is now here
}

For me as long as the sheet wasn't attached to the button the animation worked. I don't know why this works but it did for me and hopefully it will for you as well.

like image 1
Darkisa Avatar answered Oct 24 '22 00:10

Darkisa