rive-app / rive-ios

iOS runtime for Rive
MIT License
496 stars 57 forks source link

Using Duration and setTime / advanceBy APIs not working as expected #283

Closed yarneo closed 11 months ago

yarneo commented 11 months ago

Description

I have a simple converted lottie to rive animation I'd like to present a specific frame based on progress. As much as I'd love to set the progress of the animation as an input in the Rive file, this seems to only work when blending animations and not using 1 animation converted from Lottie.

Therefore I tried leveraging the RiveLinearAnimationInstance APIs, specifically duration() and setTime(time). However when I do something like:

        let finalProgress = max(0, min(1, animationOffset / (animationEndOffset - animationStartOffset)))
        let duration = buttonAppearAnimationViewModel()?.riveModel?.animation?.duration()
        riveViewModel?.riveModel?.animation?.setTime(Float(finalProgress * duration))

I basically don't see any animation what so ever.

If I set autoLoop to true, I just see the animation continue to run in a loop regardless of the setTime being set. Looks like I either get a looping animation or get nothing at all.

Help appreciate, thanks!

yarneo commented 11 months ago

I was able to make it work, but only in a somewhat hacky way:

       riveViewModel?.riveModel?.animation?.setTime(Float(finalProgress * duration))
       riveViewModel?.play()
       riveViewModel?.pause()

If there's a better way other than needing to play and pause every time, please let me know.

HayesGordon commented 11 months ago

Hi @yarneo glad you got it working. Adding some additional information to access more of the API:

You have more control with the RiveView, which you can access by extending RiveViewModel.

Instead of play/pause you can then just advance the animation by a time of zero. See here:

// Extend RiveViewModel and access riveView
class MyRiveViewModel: RiveViewModel {
    func advance(seconds: Double) {
        riveView?.advance(delta: seconds)
    }

    func setTimeAndUpdate(time: Float) {
        riveModel?.animation?.setTime(time)
        advance(seconds: 0)
    }
}

struct SwiftStateMachine: DismissableView {
    var dismiss: () -> Void = {}
    var body: some View {
        let viewModel = MyRiveViewModel(fileName: "settest", animationName: "Timeline 1")
        ScrollView{
            VStack {
                viewModel.view()
                    .frame(height:200)

                HStack{
                    Button("Set time") {
                        viewModel.setTimeAndUpdate(time: 0.5)
                    }
                }

            }
        }
    }
}

Going to close this issue, but feel free to reopen if you have questions.