EmergeTools / Pow

Delightful SwiftUI effects for your app

Home Page:https://movingparts.io/pow

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Cancel conditional effect after duration

adammcarter opened this issue · comments

Is there a simple way to disable a conditional animation after a duration?

For example, I have a computed property on the condition: parameter of my conditional smoke effect which watches that computed property and runs the smoke effect when it changes - this works great (and also looks really nice!)

However, this runs for as long as that computed property is true - which in my case is true until the app closes.

I've currently added a work around which involves bookkeeping for that computed property's class's ID and keeping them in a set, using a timer to insert that ID in to a set after a Task.sleep runs for n seconds.

That's a bad explanation but the code is:

@State private var myThingIds: Set<MyThing.ID> = []

// ...

MyView()
    .conditionalEffect(
        .smoke,
        condition: myThing.needsSmokeEffect && myThingIds.contains(myThing.id) == false
    )
    .onChange(of: myThing.needsSmokeEffect) { oldValue, newValue in
        guard newValue == true else {
            return
        }
        
        Task {
            try? await Task.sleep(for: .seconds(5))
            
            myThingIds.insert(myThing.id)
        }
    }

It feels like there's a simpler way to do this given the richness of this package - am I missing something obvious?

Hey @adamcarter93, it's a bit hard for me to infer the right solution for your needs, but my first thought is that it seems like you need an additional piece of state that pertains to when you want the animation to display.

Since SwiftUI is a declarative framework it's almost always best to directly maintain properties to represent state rather than leaning on inferring the right thing to do. While I can't provide exact code, what I would do is change the conditionalEffect to depend on a piece of state that is directly related to whether the smoke should be showing, something like:

.conditionalEffect(
    .smoke,
    condition: isSmokeDisplayed
)

How you calculate isSmokeDisplayed is up to you, it can happen in your View, it can happen in a ViewModel, it can happen in some global state, the real benefit here is that you end up decoupling or forcing the framework to keep it's own internal state which you as a user have no access to. If there was conditional disabling based on a duration the next logical thing to do would be to add cancellation logic to the timeout as well, at which point we've reinvented the concept of tracking state that the condition parameter represents, it'd just be happening opaquely inside of the framework.

I hope that makes sense and please let me know if I've misunderstood anything, it's not always easy to understand someone's problem when looking at a small code snippet but I'd love to do what I can to help!

Ah yeah, you're completely right. For some reason I was thinking of duration as "the length the animation runs for and then cancels itself" and I have no idea why I thought this - just goes to show I should stop coding late at night and go to bed!

Thanks for the detailed reply though @mergesort