Home > Back-end >  SwiftUI: Animate offset to slide in from off screen
SwiftUI: Animate offset to slide in from off screen

Time:05-13

I'm trying to animate in a view the bottom of its parent view. This is relatively easy to do by animating the offset, like so:

struct ContentView: View {

    @State var isShowingBanner = true

    var bannerOffset: CGFloat {
        isShowingBanner ? 0 : 60
    }

    var body: some View {
        VStack {
            VStack {
                Spacer()

                BannerView()
                    .offset(y: bannerOffset)
            }
            .border(Color.black, width: 1.0)
            .clipped()


            Spacer()

            Button("Toggle Banner") {
                withAnimation {
                    isShowingBanner.toggle()
                }
            }
        }
        .padding()
    }
}

Tapping the toggle button animates the offset

The glaringly obvious problem is that this just uses an arbitrary value for the animated offset, and this quickly breaks when considering dynamic type

the animated offset is less than the height of the view

My question is:

Is there a way to properly determine the height of BannerView to correctly adjust this animation. Or is there a better way to achieve this effect?

Thanks all

CodePudding user response:

It can be done just with transition, like

demo

Tested with Xcode 13.3 / iOS 15.4

struct ContentView: View {

    @State var isShowingBanner = true

    var body: some View {
        VStack {
            VStack {
                Spacer()

                if isShowingBanner {
                    BannerView()
                        .transition(.move(edge: .bottom))  // << here !!
                }
            }
// >> empty container should not shrink !!
            .frame(maxWidth: .infinity, maxHeight: .infinity) 
            .border(Color.black, width: 1.0)
            .clipped()


            Spacer()

            Button("Toggle Banner") {
                withAnimation {
                    isShowingBanner.toggle()
                }
            }
        }
        .padding()
    }
}

CodePudding user response:

If you want to determine the Height of the BannerView() You can use GeometryReader. I have created BannerView() Just for example bellow : -

   struct BannerView() : View {

        @Binding var height : CGFloat

        var body: some View {
            VStack{
               GeometryReader { proxy in
                  Rectangle().fill(.green).onAppear {
                      height = proxy.size.height
                  }
               }
            }.frame(height : 100)
        }
   }

So, the Binding value will provide height of the bannerView to your MainView(). You can use that to determine your offset.

   @State var isShowingBanner = true
   @State var offsetHeight : CGFloat = 0        

    var body: some View {
        VStack {
            VStack {
                Spacer()

                BannerView(height: $offsetHeight)
                    .offset(y: isShowingBanner ? 0 : offsetHeight)
            }
            .border(Color.black, width: 1.0)
            .clipped()


            Spacer()

            Button("Toggle Banner") {
                withAnimation {
                    isShowingBanner.toggle()
                }
            }
        }
        .padding()
    }

Hope you found this useful.

  • Related