UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Day 35 Animation question

Forums > SwiftUI

Ok - so I am going slightly mad with this one and i'm sure it's down to

  • A lack of understanding of the animation stack
  • Trying to think like an event driven coder and not a stack defined coder

Ok so the simple question is - if I have child views and they have animations - how do I then put a different animation in the parent view without it "overriding" the child animations?

Thats the tl;dr version - the slightly longer version follows.

1.) Multiple child views but requiring a delay in the start of the animation

So I created a three by four grid to hold the numbers 1 - 12 (this is for the multiplication task for those that need a refresher) In each cell I created a button view like this

Button(action: { self.buttonSelected.updateInformation(self.number) })
        {
            VStack
            {
                AnimalImage(image: self.animals[self.number], AnimationAmount: self.$AnimationAmount)
                .rotation3DEffect(.degrees(self.AnimationAmount), axis: (x:0, y:1, z:0))
                .animateForever(autoreverses: true)
                {
                    self.AnimationAmount = 360.0
                }
                Text("\(self.number)")
                .foregroundColor(.white)

            }
        }
        .padding()
        .overlay(RoundedRectangle(cornerRadius: 15).stroke(Color.white, lineWidth: 1))
        .buttonStyle(PlainButtonStyle())
        .opacity(self.screenOneOpacity)

AnimalImage is just a simple struct to put an image onto the button

struct AnimalImage : View
{
    var image: String;

    var body : some View
    {
        return Image(self.image)
         .resizable()
         .scaledToFit()
         .frame(width: 25, height: 25)
         .padding()
    }
}

Now I used your animateForever extension

extension View {
    func animateForever(using animation: Animation = Animation.easeInOut(duration: Double.random(in: 0...3)).delay(Double.random(in: 0...1)), autoreverses: Bool = false, _ action: @escaping () -> Void) -> some View
    {
        let repeated = animation.repeatForever(autoreverses: autoreverses)

        return onAppear {
            withAnimation(repeated) {
                action()
            }
        }
    }
}

Note that I put a random duration and a random delay into the animation

The PrimaryView is doing essentially this

...Group.....ZStack, VStack etc,....
// This is going to be a 3 x 4 grid
                        ForEach(0..<3)
                        {
                            vstack in
                            HStack
                            {
                                ForEach(1..<5)
                                {
                                    number in
                                    AnimalButton(number: number + (vstack * 4), buttonSelected: self.buttonSelected, screenOneOpacity: self.$screenOneOpacity, AnimationAmount: self.$AnimationAmount)
                                }
                            }
                        }
  ......

So What I expected to happen is that when the app shows - each image in the app should rotate around the Y axis for a slightly different duration and starting at a slightly different time - however ALL 12 rotate in sync - almost as if the LAST animation created has overridden the animations of the other 11

2.) The second part is that all of this 'view' - with the 12 buttons is created as it's own view - so that I can transition between the "select times table" view and the "answer questions" view - and I want that to be animated with a fade out of view1 and a fade in of view2 (I'll take that to begin with I can get fancier later)

However if I put something like this

SettingsView(buttonSelected: observableSelection, screenOneOpacity: $screenOneOpacity, AnimationAmount: $AnimationAmount)
                    .onReceive(observableSelection.updatePublisher)
                {
                    // Check the state now
                    if self.observableSelection.buttonPressed != -1
                    {
                        self.screenOneOpacity = 0.0
                    }

                }
                .animation(.easeIn(duration: 1.0))
                .transition(.opacity)

Then adding that final animation means that ALL the animations in the stack are now overridden and the button images just rotate once and then stop!

You will note that I am using an observableObject in this - it's pretty simple

class AnimalButtonObject: ObservableObject
{
    @Published var buttonPressed = -1
    let updatePublisher = PassthroughSubject<Void, Never>()

    func updateInformation(_ buttonClicked: Int) {
        buttonPressed = buttonClicked
        updatePublisher.send()
    }
}

It get's called when the user clicks on the buttons - it's just a way to track state from the button view - as that is a separate struct.

So OBVS I am doing something wrong here - and it's totally frustrating as I'd have a clue how to battle it with UIKit - but I want to do this the right way.

Any help will be gratefully accepted with humility and much "long live the king" etc....

I can upload my project to github of required

2      

You can see what I mean in this video

https://youtu.be/uVKeGkUGcMk

The first bit shows what happens if I try and animate the buttons with a slightly different duration and delay

I then recompile - putting the fade transition onto the parent view as you can see this then only allows the child views to animate ONCE but it does then transition to zero opacity over a second

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.