lucaszischka / BottomSheet

A sliding Sheet from the bottom of the Screen with 3 States build with SwiftUI.
MIT License
995 stars 137 forks source link

iOS 17.0 Unexpected Animation Behavior #143

Open achtenhagen opened 10 months ago

achtenhagen commented 10 months ago

Describe the bug With devices and simulators running iOS 17.0, the bottom bottom sheet animates from the top left to the bottom when the view appears. This is only preventable when setting the custom animation to nil. This results in an undesirable animation when the bottom sheet appears. However, with the animation value of nil the bottom sheet will end up janky transition on resize.

Minimal reproduce-able code Using the default values for the .bottomSheet modifier and an animation value other than nil.

Expected behavior I would expect only the resizing of the bottom sheet to animate, not the entire sheet itself (especially on appear).

Screenshots N/a.

Target version

Additional context Works as expected in iOS 16.4.

WouterWisse commented 9 months ago

You can work around this issue by using nil as your initial animation value and set it to an actual animation value on onAppear (for example).

achtenhagen commented 9 months ago

Thanks for your response. This is in fact the solution I went with in the end and found it be working well.

timstewardship commented 8 months ago

Thanks for the solution guys - in attempting to reproduce the fix, I couldn't see a change in the animation when setting the customAnimation after the onAppear. Am I missing something?

struct BookDetailView: View {
    @State var bottomSheetPosition: BottomSheetPosition = .hidden
    @State var animationValue:Animation? = nil

    let backgroundColors: [Color] = [Color(red: 0.2, green: 0.85, blue: 0.7), Color(red: 0.13, green: 0.55, blue: 0.45)]
    let readMoreColors: [Color] = [Color(red: 0.70, green: 0.22, blue: 0.22), Color(red: 1, green: 0.32, blue: 0.32)]
    let bookmarkColors: [Color] = [Color(red: 0.28, green: 0.28, blue: 0.53), Color(red: 0.44, green: 0.44, blue: 0.83)]

    var body: some View {
        ZStack(content: {

        //A green gradient as a background that ignores the safe area.
        LinearGradient(gradient: Gradient(colors: self.backgroundColors), startPoint: .topLeading, endPoint: .bottomTrailing)
            .edgesIgnoringSafeArea(.all)

            VStack(alignment: .center, spacing: 30, content: {
                Button(action: {
                    self.bottomSheetPosition = .dynamic
                }, label: {
                    Text("Show detail sheet")
                })
            })
            .edgesIgnoringSafeArea(.all)
            .bottomSheet(bottomSheetPosition: self.$bottomSheetPosition, switchablePositions: [
                .dynamic, .hidden
            ], content: {
                //A short introduction to the book, with a "Read More" button and a "Bookmark" button.
                VStack(spacing: 0) {
                    Text("This tumultuous tale of life in a bleak farmhouse on the Yorkshire moors is a popular set text for GCSE and A-level English study, but away from the demands of the classroom it’s easier to enjoy its drama and intensity. Populated largely by characters whose inability to control their own emotions...")
                        .fixedSize(horizontal: false, vertical: true)

                    HStack {
                        Button(action: {}, label: {
                            Text("Read More")
                                .padding(.horizontal)
                        })
                            .buttonStyle(BookButton(colors: self.readMoreColors)).clipShape(Capsule())

                        Spacer()

                        Button(action: {}, label: {
                            Image(systemName: "bookmark")
                        })
                            .buttonStyle(BookButton(colors: self.bookmarkColors)).clipShape(Circle())
                    }
                    .padding(EdgeInsets(top: 30, leading: 0, bottom: 30, trailing: 0))

                }
                .padding([.horizontal, .top])
                .onAppear {
// Set the animation value here
                    self.animationValue = .linear.speed(1)
                }
            })
            .showDragIndicator(true)
            .enableContentDrag()
            .showCloseButton()
            .enableSwipeToDismiss()
            .enableTapToDismiss()
            .customThreshold(0.1)
            .customAnimation(self.animationValue)
        })

    }
}
lucaArchidiacono commented 2 months ago

@timstewardship

You can use the Task Modifier while adding a Task.sleep(). Something like this:

            .task {
                try? await Task.sleep(for: .seconds(0.25))
                animationValue = .linear.speed(1)
            }

This will let the View render properly and then apply your animation without running into weird Animation issues.