1

I have a horizontal ScrollView that holds some Rectangles. What I am trying to achieve is to fadeout each Rectangle and then hide (remove) the ScrollView.

I have partially achieve this:

enter image description here

I said partially because, as you can see, the Rectangles are removed but the ScrollView is still there (see the blue border box that belongs to the ScrollView, I added some paddings o make it more visible).

I'm thinking that I may need to nest transitions (each Rectangle has a .move(edge:) transition and there should be another "outer" transition for the ScrollView - but also this could be the wrong approach.

Any ideas how to achieve this animation and also transition (remove) the ScrollView at the end of the animation? I have tried different options but I have failed. Note: the black Rectangle is fixed.

Here's the sample code:

struct TestAnimation: View {
    @State var show : Bool = true
    var colors : [Color] = [.red, .orange, .yellow, .green, .blue]

    var body: some View {
        VStack(alignment: .leading) {
            Spacer()

            Color.purple
                .frame(height: 100)
                .overlay(
                    Text("Tap Me!").foregroundColor(.white))
                .onTapGesture {
                    self.show.toggle()
            }

            HStack(alignment: .bottom) {
                Rectangle()
                    .fill(Color.black)
                    .frame(width: 70, height: 100)
                    .overlay(Text("A").foregroundColor(.white).font(.largeTitle))
                    .padding(8)

                    ScrollView(.horizontal, showsIndicators: false) {

                        HStack(alignment: .bottom) {
                            if show {
                                self.cellForItemAt(idx: 2)
                                self.cellForItemAt(idx: 3)
                                self.cellForItemAt(idx: 4)
                                self.cellForItemAt(idx: 5)
                            }
                        }
                        .frame(height: 100)
                        .padding(4)
                        .border(Color.red)//HStack


                    }//Scrollview
                    .padding(4)
                    .border(Color.blue)

            }
            .padding(4)
            .border(Color.gray)//Hstack
        }
    }

    func cellForItemAt(idx: Int) -> some View {
        return Rectangle()
            .fill(colors[idx-1])
            .frame(width: 70, height: 100)
            .transition(AnyTransition.move(edge: .bottom).combined(with: .opacity).animation(self.ripple(idx: idx)))
            .animation(ripple(idx: idx))
    }

    func ripple(idx: Int) -> Animation {
        Animation
            .easeInOut(duration: 0.8)
            .delay(0.20 * Double(colors.count - idx) : Double(idx))
    }
}
Aнгел
  • 1,361
  • 3
  • 17
  • 32

1 Answers1

0

My solution to your problem is to create a new state for the scrollview alone then wrap the Rectangles in a Group and wrap the group in if show and on onDisappear set self.showScroll = false

Finally, make sure when tapping Tap Me! to set showScroll to true

Is this the most elegant way? no, but it gets the job done! enter image description here

//
//  TestAnimation.swift
//  playground
//
//  Created by Muhand Jumah on 3/20/20.
//  Copyright © 2020 Muhand Jumah. All rights reserved.
//

import SwiftUI

struct TestAnimation: View {
    @State var show : Bool = true
    @State var showScroll: Bool = true
    var colors : [Color] = [.red, .orange, .yellow, .green, .blue]

    var body: some View {
        VStack(alignment: .leading) {
            Spacer()

            Color.purple
                .frame(height: 100)
                .overlay(
                    Text("Tap Me!").foregroundColor(.white))
                .onTapGesture {
                    self.showScroll = true
                    self.show.toggle()
            }

            HStack(alignment: .bottom) {
                Rectangle()
                    .fill(Color.black)
                    .frame(width: 70, height: 100)
                    .overlay(Text("A").foregroundColor(.white).font(.largeTitle))
                    .padding(8)

                if(showScroll) {
                    ScrollView(.horizontal, showsIndicators: false) {

                        HStack(alignment: .bottom) {
                            if show {
                                Group {
                                    self.cellForItemAt(idx: 2)
                                    self.cellForItemAt(idx: 3)
                                    self.cellForItemAt(idx: 4)
                                    self.cellForItemAt(idx: 5)
                                }.onDisappear {
                                    self.showScroll = false
                                }
                            }
                        }
                        .frame(height: 100)
                        .padding(4)
                        .border(Color.red)//HStack


                    }//Scrollview
                    .padding(4)
                    .border(Color.blue)
                }

            }
            .padding(4)
            .border(Color.gray)//Hstack
        }
    }

    func cellForItemAt(idx: Int) -> some View {
        return Rectangle()
            .fill(colors[idx-1])
            .frame(width: 70, height: 100)
            .transition(AnyTransition.move(edge: .bottom).combined(with: .opacity).animation(self.ripple(idx: idx)))
            .animation(ripple(idx: idx))
    }

    func ripple(idx: Int) -> Animation {
        Animation
            .easeInOut(duration: 0.8)
            .delay(0.20 * Double(colors.count - idx))
    }
}

struct TestAnimation_Previews: PreviewProvider {
    static var previews: some View {
        TestAnimation()
    }
}


Muhand Jumah
  • 1,726
  • 1
  • 11
  • 26