0

I have the following custom action sheet with options (details omitted for simplicity):

var sheet: some View {
    VStack {
        HStack { /* some stuff here */ }
        
        list(of: options)
            .readSize($optionsSize)
            .wrappedInScrollView(when: optionsDontFit)
            .transition(.opacity)
    }
    .readSize($sheetSize)
}

where:

  • list(of:) is just a VStack arranging all options' views
  • readSize(…) is a modifier I created that updates a provided binding via a GeometryReader;
  • optionsDontFit is a computed Bool property (depending on sheetSize and optionsSize);
  • wrappedInScrollView(when:) is the following:
fileprivate extension View {
    @ViewBuilder
    func wrappedInScrollView(when condition: Bool) -> some View {
        if condition {
            ScrollView {
                self
            }
        } else {
            self
        }
    }
}

The problem arises when the parent of the sheet is animated e.g. via a transition from the bottom:

var body: some View {
    ZStack {
        /* some stuff here */
        VStack {
            Spacer(minHeight: 42)
            if !isDismissed {
                sheet.transition(.move(edge: .bottom))
            }
        }
    }
    .onAppear { isDismissed = false }
}

The issue is that the sheet itself does not slide with the parent, but appears instead directly at the final position. The closing (dismissing) animation however works as expected.

If I remove the scrollview wrapper func, eveything is fine.

Here is a screen rec of the problem:

preview

Any idea what is going on?

I tried removing the scrollview wrapper func, and it animates correctly, but I have a requirement that the options list should be scrollable on smaller devices where the options take up the entire screen.

I tried moving the transition modifiers elsewhere but to no avail (also those should be the correct places).

I tried putting a transition(edge: .bottom) for the list(of:) as well but this will not work in some other scenarios with the full code I have (I actually have a switch-case over there that either presents a loader, or a list, so when state is changed while it is loading it reappears from the bottom of the screen while the parent is already moving further above).

2
  • If the ScrollView is causing the issue then you could maybe try the technique shown as answer to How to keep the ScrollView height to the minimum necessary in SwiftUI?, as replacement for your wrappedInScrollView approach. Commented Jul 11, 2023 at 11:22
  • @BenzyNeez I need to animate the change in height though, and also I don't always know in advance the size of the options and have a loading spinner there instead. By the way, about the problem described - I detected that it's because the readSize modifier uses a Color.clear.onAppear approach, a slight fraction of the second after things are on screen the value of optionsDontFit changes and produces a new tree without a scroll view, which has this problematic animation. Commented Jul 17, 2023 at 13:15

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.