1

I'm not quite a SwiftUI veteran but I've shipped a couple of apps of moderate complexity. Still, I can't claim that I fully understand it and I'm hoping someone with deeper knowledge could shed some light on this issue:

I have some content that I want to toggle on and off, not unlike .sheet(), but I want more control over it. Here is some "reconstructed" code but it should be able capture the essence:

struct ContentView: View {

    @State private var isShown = false

    var body: some View {

        GeometryReader { g in

            VStack {

                ZStack(alignment: .top) {

                    // This element "holds" the size
                    // while the content is hidden
                    Color.clear

                    // Content to be toggled
                    if self.isShown {
                        ScrollView {
                            Rectangle()
                                .aspectRatio(1, contentMode: .fit)
                                .frame(width: g.size.width) // This is a "work-around"
                        } // ScrollView
                            .transition(.move(edge: .bottom))
                            .animation(.easeOut)
                    }

                } // ZStack

                // Button to show / hide the content
                Button(action: {
                    self.isShown.toggle()
                }) {
                    Text(self.isShown ? "Hide" : "Show")
                }

            } // VStack

        } // GeometryReader

    }
}

What it does is, it toggles on and off some content block (represented here by a Rectangle within a ScrollView). When that happens, the content view in transitioned by moving in from the bottom with some animation. The opposite happens when the button is tapped again.

This particular piece of code works as intended but only because of this line:

.frame(width: g.size.width) // This is a "work-around"

Which, in turn, requires an extra GeometryReader, otherwise, the width of the content is animated, producing an unwanted effect (another "fix" I've discovered is using the .fixedSize() modifier but, to produce reasonable effects, it requires content that assumes its own width like Text)

My question to the wise is: is it possible to nicely transition in content encapsulated within a ScrollView without using such "fixes"? Alternatively, is there a more elegant fix for that?

A quick addition to the question following @Asperi's answer: contents should remain animatable. You are my only hope,

–Baglan

Baglan
  • 1,057
  • 10
  • 23

1 Answers1

1

Here is a solution (updated body w/o GeometryReader). Tested with Xcode 11.4 / iOS 13.4

demo

var body: some View {
    VStack {
        ZStack(alignment: .top) {
            // This element "holds" the size
            // while the content is hidden
            Color.clear

            // Content to be toggled
            if self.isShown {
                ScrollView {
                    Rectangle()
                        .aspectRatio(1, contentMode: .fit)
                        .animation(nil)     // << here !!
                } // ScrollView
                    .transition(.move(edge: .bottom))
                    .animation(.easeOut)
            }
        } // ZStack

        // Button to show / hide the content
        Button(action: {
            self.isShown.toggle()
        }) {
            Text(self.isShown ? "Hide" : "Show")
        }
    } // VStack
}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Hi, thank you for the answer, that's a valid solution! The only issue I can see with is that I might want to animate contents. In the actual project, there are some sub-views that are presented in a similar fashion with animations or transitions and setting animations to nil prevents those. Of course you couldn't deduce that from the code :). A valid answer nevertheless, thank you! – Baglan Jun 06 '20 at 11:07
  • I have a similar issue with wanting to add animations to subviews as well https://stackoverflow.com/questions/64088038/how-to-stop-swiftui-draggesture-from-animating-subviews – daihovey Sep 27 '20 at 12:11