Added a helper method for animations to determine if they've ended. Moved model modifications out of RiveView's play method and into RiveViewModel's. When playing from the RiveViewModel we now check if an animation has ended so it can play again.

This commit is contained in:
Zachary Duncan
2022-05-24 17:20:55 -04:00
committed by Zachary Duncan
parent 6c172f7279
commit b51be942bb
7 changed files with 96 additions and 39 deletions

Binary file not shown.

View File

@@ -52,6 +52,7 @@
C3460A002800A6CE002DBCB7 /* bird.riv in Resources */ = {isa = PBXBuildFile; fileRef = C34609FD2800A6CE002DBCB7 /* bird.riv */; };
C3468E6227FDCBC6008652FD /* SimpleSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3468E6127FDCBC6008652FD /* SimpleSlider.swift */; };
C3745FCE282AE2320087F4AF /* hero_editor.riv in Resources */ = {isa = PBXBuildFile; fileRef = C3745FCB282AE2320087F4AF /* hero_editor.riv */; };
C3C07482283D582B00E8EB33 /* button.riv in Resources */ = {isa = PBXBuildFile; fileRef = C3C0747F283D582B00E8EB33 /* button.riv */; };
C3D187F3280751A8008B739A /* RiveProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D187F2280751A8008B739A /* RiveProgressBar.swift */; };
C3D187F728075B6C008B739A /* riveslider.riv in Resources */ = {isa = PBXBuildFile; fileRef = C3D187F628075B6C008B739A /* riveslider.riv */; };
C3D187F9280770EA008B739A /* truck.riv in Resources */ = {isa = PBXBuildFile; fileRef = C3D187F8280770EA008B739A /* truck.riv */; };
@@ -155,6 +156,7 @@
C34609FD2800A6CE002DBCB7 /* bird.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = bird.riv; sourceTree = "<group>"; };
C3468E6127FDCBC6008652FD /* SimpleSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleSlider.swift; sourceTree = "<group>"; };
C3745FCB282AE2320087F4AF /* hero_editor.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = hero_editor.riv; sourceTree = "<group>"; };
C3C0747F283D582B00E8EB33 /* button.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = button.riv; sourceTree = "<group>"; };
C3D187F2280751A8008B739A /* RiveProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiveProgressBar.swift; sourceTree = "<group>"; };
C3D187F628075B6C008B739A /* riveslider.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = riveslider.riv; sourceTree = "<group>"; };
C3D187F8280770EA008B739A /* truck.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = truck.riv; sourceTree = "<group>"; };
@@ -251,6 +253,7 @@
C9696B0E24FC6FD10041502A /* Assets */ = {
isa = PBXGroup;
children = (
C3C0747F283D582B00E8EB33 /* button.riv */,
C3E2B5872833ECB500A8651B /* bullet_man_game.riv */,
C3745FCB282AE2320087F4AF /* hero_editor.riv */,
C3E383472837E6B00029D65E /* watch_v1.riv */,
@@ -479,6 +482,7 @@
042C88DD2644447500E7DBB2 /* ui_swipe_left_to_delete.riv in Resources */,
042C88E52644447500E7DBB2 /* explorer.riv in Resources */,
042C88E22644447500E7DBB2 /* f22.riv in Resources */,
C3C07482283D582B00E8EB33 /* button.riv in Resources */,
046AFA712673AF04004ED497 /* blendmodes.riv in Resources */,
042C88EC2644447500E7DBB2 /* vader.riv in Resources */,
C9C73E9E24FC471E00EF9516 /* Assets.xcassets in Resources */,

View File

@@ -12,25 +12,36 @@ import RiveRuntime
struct SwiftSimpleAnimation: DismissableView {
var dismiss: () -> Void = {}
var viewModel = RiveViewModel(fileName: "truck", autoPlay: false)
var viewModel = RiveViewModel(fileName: "button", autoPlay: false)
var body: some View {
viewModel.view()
HStack {
PlayerButton(title: "▶︎") {
viewModel.play()
}
PlayerButton(title: " ▍▍") {
viewModel.pause()
}
PlayerButton(title: "◼︎") {
viewModel.stop()
ZStack {
Color.gray
VStack {
viewModel.view()
HStack {
PlayerButton(title: "play") {
viewModel.play(animationName: "active")
}
PlayerButton(title: "pause") {
viewModel.pause()
}
PlayerButton(title: "stop") {
viewModel.stop()
}
PlayerButton(title: "backward.end") {
viewModel.reset()
}
}
}
.padding()
}
.padding()
.ignoresSafeArea()
}
struct PlayerButton: View {
@@ -44,7 +55,7 @@ struct SwiftSimpleAnimation: DismissableView {
} label: {
ZStack {
Color.blue
Text(title)
Image(systemName: title + ".fill")
.foregroundColor(.white)
}
.aspectRatio(1, contentMode: .fit)

View File

@@ -74,23 +74,11 @@ open class RiveView: RiveRendererView {
// MARK: - Controls
/// Starts the render loop
internal func play(loop: Loop = .loopAuto, direction: Direction = .directionAuto) {
internal func play() {
eventQueue.add {
self.playerDelegate?.player(playedWithModel: self.riveModel)
}
if let animation = riveModel.animation {
if loop != .loopAuto {
animation.loop(Int32(loop.rawValue))
}
if direction == .directionForwards {
animation.direction(1)
} else if direction == .directionBackwards {
animation.direction(-1)
}
}
isPlaying = true
startTimer()
}
@@ -107,8 +95,22 @@ open class RiveView: RiveRendererView {
/// Asks the render loop to stop on the next cycle
internal func stop() {
self.playerDelegate?.player(stoppedWithModel: self.riveModel)
isPlaying = false
playerDelegate?.player(stoppedWithModel: self.riveModel)
lastTime = 0
if !isPlaying {
advance(delta: 0)
} else {
isPlaying = false
}
}
internal func reset() {
lastTime = 0
if !isPlaying {
advance(delta: 0)
}
}
// MARK: - Render Loop

View File

@@ -119,29 +119,64 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
didSet { riveView?.alignment = alignment }
}
open func play(animationName: String? = nil, loop: Loop? = nil) {
/// Starts the active Animation or StateMachine from it's last position. It will start
/// from the beginning if the active Animation has ended or a new one is provided.
/// - Parameters:
/// - animationName: The name of a new Animation to play on the current Artboard
/// - loop: The loop mode for the active Animation
open func play(animationName: String? = nil, loop: Loop = .loopAuto, direction: Direction = .directionAuto) {
if let name = animationName {
try! riveModel?.setAnimation(name)
}
if let loop = loop?.rawValue {
riveModel?.animation?.loop(Int32(loop))
if let animation = riveModel?.animation {
if loop != .loopAuto {
animation.loop(Int32(loop.rawValue))
}
if direction == .directionForwards {
animation.direction(1)
} else if direction == .directionBackwards {
animation.direction(-1)
}
if animation.hasEnded() {
// Restarts Animation from beginning
animation.setTime(0)
}
}
// We're not checking if a StateMachine is "ended" or "ExitState"
// But we may want to in the future to enable restarting it by playing again
riveView?.play()
}
/// Haults the active Animation or StateMachine and will resume from it's current position when next played
open func pause() {
riveView?.pause()
}
/// Haults the active Animation or StateMachine and will restart from the beginning when next played
open func stop() {
resetScene()
riveView?.stop()
try! resetModelToDefault()
}
@available(*, deprecated, renamed: "stop")
/// Sets the active Animation or StateMachine back to their starting position
open func reset() {
stop()
resetScene()
riveView?.reset()
}
private func resetScene() {
if let stateMachine = riveModel?.stateMachine {
// StateMachine can't reset itself so we reload from the Artboard
try! riveModel!.setStateMachine(stateMachine.name())
}
else if let animation = riveModel?.animation {
animation.setTime(0)
}
}
// MARK: - RiveModel

View File

@@ -40,9 +40,11 @@
- (bool)advanceBy:(double)elapsedSeconds {
return instance->advance(elapsedSeconds);
}
- (void)direction:(int)direction {
instance->direction(direction);
}
- (int)direction {
return instance->direction();
}
@@ -68,8 +70,6 @@
delete instance;
}
- (NSInteger)fps {
return instance->fps();
}
@@ -108,4 +108,8 @@
return animation->duration() / fps;
}
- (bool)hasEnded {
return [self time] >= [self endTime];
}
@end

View File

@@ -37,6 +37,7 @@ NS_ASSUME_NONNULL_BEGIN
- (NSInteger)duration;
- (NSInteger)effectiveDuration;
- (float)effectiveDurationInSeconds;
- (bool)hasEnded;
@end