mirror of
https://github.com/rive-app/rive-ios.git
synced 2026-01-18 17:11:28 +01:00
Made some runtime refinements. Updated tests to use new simplified runtime.
This commit is contained in:
committed by
Zachary Duncan
parent
971c180926
commit
e5f1b24941
BIN
Example-iOS/Assets/bullet_man_game.riv
Normal file
BIN
Example-iOS/Assets/bullet_man_game.riv
Normal file
Binary file not shown.
@@ -10,7 +10,6 @@
|
||||
04026DC427CE3ED6002B3DBF /* SwiftSimpleAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04026DC327CE3ED6002B3DBF /* SwiftSimpleAnimation.swift */; };
|
||||
04026DC827CE3EE6002B3DBF /* SwiftLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04026DC727CE3EE6002B3DBF /* SwiftLayout.swift */; };
|
||||
04026DCA27CE3EF6002B3DBF /* SwiftMultipleAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04026DC927CE3EF6002B3DBF /* SwiftMultipleAnimations.swift */; };
|
||||
04026DCC27CE3F03002B3DBF /* SwiftLoopMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04026DCB27CE3F03002B3DBF /* SwiftLoopMode.swift */; };
|
||||
04026DCE27CE3F0F002B3DBF /* SwiftStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04026DCD27CE3F0F002B3DBF /* SwiftStateMachine.swift */; };
|
||||
042C88832643D6B900E7DBB2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 042C88822643D6B900E7DBB2 /* Main.storyboard */; };
|
||||
042C88882643DB7100E7DBB2 /* SimpleAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 042C88872643DB7100E7DBB2 /* SimpleAnimation.swift */; };
|
||||
@@ -56,6 +55,8 @@
|
||||
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 */; };
|
||||
C3E2B58A2833ECB500A8651B /* bullet_man_game.riv in Resources */ = {isa = PBXBuildFile; fileRef = C3E2B5872833ECB500A8651B /* bullet_man_game.riv */; };
|
||||
C3E2B58C2833ECFE00A8651B /* SwiftCannonGame.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E2B58B2833ECFE00A8651B /* SwiftCannonGame.swift */; };
|
||||
C3ECAC252817BE1100A81123 /* magic_8-ball_v2.riv in Resources */ = {isa = PBXBuildFile; fileRef = C3ECAC222817BE1100A81123 /* magic_8-ball_v2.riv */; };
|
||||
C3ECAC272817BE4600A81123 /* SwiftTouchEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ECAC262817BE4600A81123 /* SwiftTouchEvents.swift */; };
|
||||
C3ECAC2B281837B300A81123 /* play_button_event_example.riv in Resources */ = {isa = PBXBuildFile; fileRef = C3ECAC28281837B300A81123 /* play_button_event_example.riv */; };
|
||||
@@ -113,7 +114,6 @@
|
||||
04026DC327CE3ED6002B3DBF /* SwiftSimpleAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftSimpleAnimation.swift; sourceTree = "<group>"; };
|
||||
04026DC727CE3EE6002B3DBF /* SwiftLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLayout.swift; sourceTree = "<group>"; };
|
||||
04026DC927CE3EF6002B3DBF /* SwiftMultipleAnimations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftMultipleAnimations.swift; sourceTree = "<group>"; };
|
||||
04026DCB27CE3F03002B3DBF /* SwiftLoopMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLoopMode.swift; sourceTree = "<group>"; };
|
||||
04026DCD27CE3F0F002B3DBF /* SwiftStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftStateMachine.swift; sourceTree = "<group>"; };
|
||||
042C88822643D6B900E7DBB2 /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
|
||||
042C88872643DB7100E7DBB2 /* SimpleAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleAnimation.swift; sourceTree = "<group>"; };
|
||||
@@ -158,6 +158,8 @@
|
||||
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>"; };
|
||||
C3E2B5872833ECB500A8651B /* bullet_man_game.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = bullet_man_game.riv; sourceTree = "<group>"; };
|
||||
C3E2B58B2833ECFE00A8651B /* SwiftCannonGame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftCannonGame.swift; sourceTree = "<group>"; };
|
||||
C3ECAC222817BE1100A81123 /* magic_8-ball_v2.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = "magic_8-ball_v2.riv"; sourceTree = "<group>"; };
|
||||
C3ECAC262817BE4600A81123 /* SwiftTouchEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftTouchEvents.swift; sourceTree = "<group>"; };
|
||||
C3ECAC28281837B300A81123 /* play_button_event_example.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = play_button_event_example.riv; sourceTree = "<group>"; };
|
||||
@@ -249,6 +251,7 @@
|
||||
C9696B0E24FC6FD10041502A /* Assets */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C3E2B5872833ECB500A8651B /* bullet_man_game.riv */,
|
||||
C3745FCB282AE2320087F4AF /* hero_editor.riv */,
|
||||
C3ECAC30281840EF00A81123 /* watch_v1.riv */,
|
||||
C3ECAC29281837B300A81123 /* leg_day_events_example.riv */,
|
||||
@@ -301,10 +304,10 @@
|
||||
C9CB2F12264C92D200E7FF0D /* SwiftWidgets.swift */,
|
||||
04026DC727CE3EE6002B3DBF /* SwiftLayout.swift */,
|
||||
04026DC927CE3EF6002B3DBF /* SwiftMultipleAnimations.swift */,
|
||||
04026DCB27CE3F03002B3DBF /* SwiftLoopMode.swift */,
|
||||
04026DCD27CE3F0F002B3DBF /* SwiftStateMachine.swift */,
|
||||
E5CD7D7027DC331900BFE5E2 /* SwiftMeshAnimation.swift */,
|
||||
C3ECAC262817BE4600A81123 /* SwiftTouchEvents.swift */,
|
||||
C3E2B58B2833ECFE00A8651B /* SwiftCannonGame.swift */,
|
||||
);
|
||||
path = SwiftUI;
|
||||
sourceTree = "<group>";
|
||||
@@ -473,6 +476,7 @@
|
||||
C3745FCE282AE2320087F4AF /* hero_editor.riv in Resources */,
|
||||
C3D187F9280770EA008B739A /* truck.riv in Resources */,
|
||||
C9C73EA124FC471E00EF9516 /* Preview Assets.xcassets in Resources */,
|
||||
C3E2B58A2833ECB500A8651B /* bullet_man_game.riv in Resources */,
|
||||
042C88DD2644447500E7DBB2 /* ui_swipe_left_to_delete.riv in Resources */,
|
||||
042C88E52644447500E7DBB2 /* explorer.riv in Resources */,
|
||||
042C88E22644447500E7DBB2 /* f22.riv in Resources */,
|
||||
@@ -497,6 +501,7 @@
|
||||
files = (
|
||||
C324DB5D280728690060589F /* RiveButton.swift in Sources */,
|
||||
C9C73E9824FC471E00EF9516 /* AppDelegate.swift in Sources */,
|
||||
C3E2B58C2833ECFE00A8651B /* SwiftCannonGame.swift in Sources */,
|
||||
C9CB2F13264C92D200E7FF0D /* SwiftWidgets.swift in Sources */,
|
||||
042C888E2644230700E7DBB2 /* utility.swift in Sources */,
|
||||
C3D187F3280751A8008B739A /* RiveProgressBar.swift in Sources */,
|
||||
@@ -508,7 +513,6 @@
|
||||
C3ECAC272817BE4600A81123 /* SwiftTouchEvents.swift in Sources */,
|
||||
C3ECAC2F281840A300A81123 /* ClockViewModel.swift in Sources */,
|
||||
04026DC827CE3EE6002B3DBF /* SwiftLayout.swift in Sources */,
|
||||
04026DCC27CE3F03002B3DBF /* SwiftLoopMode.swift in Sources */,
|
||||
C9C73E9A24FC471E00EF9516 /* SceneDelegate.swift in Sources */,
|
||||
04C4C83E2646FE410047E614 /* StateMachine.swift in Sources */,
|
||||
C324DB5B2807216B0060589F /* RiveSlider.swift in Sources */,
|
||||
|
||||
18
Example-iOS/Source/Examples/SwiftUI/SwiftCannonGame.swift
Normal file
18
Example-iOS/Source/Examples/SwiftUI/SwiftCannonGame.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// SwiftCannonGame.swift
|
||||
// RiveExample
|
||||
//
|
||||
// Created by Zachary Duncan on 5/17/22.
|
||||
// Copyright © 2022 Rive. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import RiveRuntime
|
||||
|
||||
struct SwiftCannonGame: DismissableView {
|
||||
var dismiss: () -> Void = {}
|
||||
|
||||
var body: some View {
|
||||
RiveViewModel(fileName: "bullet_man_game", stateMachineName: "State Machine 1").view()
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
////
|
||||
//// LoopMode.swift
|
||||
//// RiveExample
|
||||
////
|
||||
//// Created by Maxwell Talbot on 01/03/2022.
|
||||
//// Copyright © 2022 Rive. All rights reserved.
|
||||
////
|
||||
//
|
||||
//import SwiftUI
|
||||
//import RiveRuntime
|
||||
//
|
||||
//struct SwiftLoopMode: DismissableView {
|
||||
// var dismiss: () -> Void = {}
|
||||
//
|
||||
// var loopy = RiveViewModel(fileName: "loopy", autoplay: false)
|
||||
//// var direction = Direction.directionAuto
|
||||
//
|
||||
// var body: some View {
|
||||
// ScrollView {
|
||||
// VStack {
|
||||
//// RiveViewSwift(
|
||||
//// resource: "loopy", autoplay: false, controller:controller
|
||||
//// )
|
||||
//
|
||||
// loopy.view()
|
||||
// .frame(height:300)
|
||||
// HStack {
|
||||
// Button("Reset") {
|
||||
// try? loopy.reset()
|
||||
// }
|
||||
//
|
||||
//// TODO: work out direction controls
|
||||
//// Button("Forwards", action:{direction = .directionForwards})
|
||||
//// Button("Auto", action:{direction = .directionAuto})
|
||||
//// Button("Backwards", action:{direction = .directionBackwards})
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// HStack {
|
||||
// Text("Oneshot")
|
||||
// Button("Play") {
|
||||
// try? loopy.play(animationName: "oneshot")
|
||||
// }
|
||||
// Button("OneShot") {
|
||||
// try? loopy.play(animationName: "oneshot", loop: .loopOneShot)
|
||||
// }
|
||||
// Button("Loop") {
|
||||
// try? loopy.play(animationName: "oneshot", loop: .loopLoop)
|
||||
// }
|
||||
// Button("PingPong") {
|
||||
// try? loopy.play(animationName: "oneshot", loop: .loopPingPong)
|
||||
// }
|
||||
// }
|
||||
// HStack {
|
||||
// Text("Loop")
|
||||
// Button("Play") {
|
||||
// try? loopy.play(animationName: "loop")
|
||||
// }
|
||||
// Button("OneShot") {
|
||||
// try? loopy.play(animationName: "loop", loop: .loopOneShot)
|
||||
// }
|
||||
// Button("Loop") {
|
||||
// try? loopy.play(animationName: "loop", loop: .loopLoop)
|
||||
// }
|
||||
// Button("PingPong") {
|
||||
// try? loopy.play(animationName: "loop", loop: .loopPingPong)
|
||||
// }
|
||||
// }
|
||||
// HStack {
|
||||
// Text("Pingpong")
|
||||
// Button("Play") {
|
||||
// try? loopy.play(animationName: "pingpong")
|
||||
// }
|
||||
// Button("OneShot") {
|
||||
// try? loopy.play(animationName: "pingpong", loop: .loopOneShot)
|
||||
// }
|
||||
// Button("Loop") {
|
||||
// try? loopy.play(animationName: "pingpong", loop: .loopLoop)
|
||||
// }
|
||||
// Button("PingPong") {
|
||||
// try? loopy.play(animationName: "pingpong", loop: .loopPingPong)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
@@ -12,10 +12,45 @@ import RiveRuntime
|
||||
|
||||
struct SwiftSimpleAnimation: DismissableView {
|
||||
var dismiss: () -> Void = {}
|
||||
var viewModel = RiveViewModel(fileName: "truck", autoPlay: false)
|
||||
|
||||
var body: some View {
|
||||
RiveViewModel(fileName: "truck").view()
|
||||
viewModel.view()
|
||||
|
||||
HStack {
|
||||
PlayerButton(title: "▶︎") {
|
||||
viewModel.play()
|
||||
}
|
||||
|
||||
PlayerButton(title: " ▍▍") {
|
||||
viewModel.pause()
|
||||
}
|
||||
|
||||
PlayerButton(title: "◼︎") {
|
||||
viewModel.stop()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
struct PlayerButton: View {
|
||||
var title: String
|
||||
var action: ()->Void
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
action()
|
||||
|
||||
} label: {
|
||||
ZStack {
|
||||
Color.blue
|
||||
Text(title)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.cornerRadius(10)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@ class ExamplesMasterTableViewController: UITableViewController {
|
||||
private lazy var swiftViews: [(String, AnyView)] = [
|
||||
("Touch Events!", typeErased(dismissableView: SwiftTouchEvents())),
|
||||
("Widget Collection", typeErased(dismissableView: SwiftWidgets())),
|
||||
("Simple Animation", typeErased(dismissableView: SwiftSimpleAnimation())),
|
||||
("Animation Player", typeErased(dismissableView: SwiftSimpleAnimation())),
|
||||
("Layout", typeErased(dismissableView: SwiftLayout())),
|
||||
("MultipleAnimations", typeErased(dismissableView: SwiftMultipleAnimations())),
|
||||
//("Loop Mode", typeErased(dismissableView: SwiftLoopMode())),
|
||||
("Cannon Game", typeErased(dismissableView: SwiftCannonGame())),
|
||||
("State Machine", typeErased(dismissableView: SwiftStateMachine())),
|
||||
("Mesh Animation", typeErased(dismissableView: SwiftMeshAnimation()))
|
||||
]
|
||||
|
||||
@@ -11,8 +11,8 @@ import Foundation
|
||||
open class RiveModel: ObservableObject {
|
||||
internal private(set) var riveFile: RiveFile
|
||||
public private(set) var artboard: RiveArtboard!
|
||||
public private(set) var stateMachine: RiveStateMachineInstance?
|
||||
public private(set) var animation: RiveLinearAnimationInstance?
|
||||
public internal(set) var stateMachine: RiveStateMachineInstance?
|
||||
public internal(set) var animation: RiveLinearAnimationInstance?
|
||||
|
||||
public init(riveFile: RiveFile) {
|
||||
self.riveFile = riveFile
|
||||
|
||||
@@ -22,17 +22,19 @@ open class RiveView: RiveRendererView {
|
||||
private var eventQueue = EventQueue()
|
||||
|
||||
// MARK: Delegates
|
||||
internal var playerDelegate: RivePlayerDelegate?
|
||||
internal var stateMachineDelegate: RiveStateMachineDelegate?
|
||||
public var playerDelegate: RivePlayerDelegate?
|
||||
public var stateMachineDelegate: RiveStateMachineDelegate?
|
||||
|
||||
// MARK: Debug
|
||||
private var fpsCounter: FPSCounterView? = nil
|
||||
public var showFPS: Bool = false {
|
||||
didSet {
|
||||
if showFPS {
|
||||
if showFPS && fpsCounter == nil {
|
||||
fpsCounter = FPSCounterView()
|
||||
addSubview(fpsCounter!)
|
||||
} else {
|
||||
}
|
||||
|
||||
if !showFPS {
|
||||
fpsCounter?.removeFromSuperview()
|
||||
fpsCounter = nil
|
||||
}
|
||||
@@ -55,8 +57,9 @@ open class RiveView: RiveRendererView {
|
||||
|
||||
/// This resets the view with the new model. Useful when the `RiveView` was initialized without one.
|
||||
open func setModel(_ model: RiveModel, autoPlay: Bool = true) throws {
|
||||
stop()
|
||||
self.riveModel = model
|
||||
stopTimer()
|
||||
isPlaying = false
|
||||
riveModel = model
|
||||
isOpaque = false
|
||||
|
||||
if autoPlay {
|
||||
@@ -64,10 +67,13 @@ open class RiveView: RiveRendererView {
|
||||
} else {
|
||||
advance(delta: 0)
|
||||
}
|
||||
|
||||
showFPS = true
|
||||
}
|
||||
|
||||
// MARK: - Controls
|
||||
|
||||
/// Starts the render loop
|
||||
internal func play(loop: Loop = .loopAuto, direction: Direction = .directionAuto) {
|
||||
eventQueue.add {
|
||||
self.playerDelegate?.player(playedWithModel: self.riveModel)
|
||||
@@ -89,20 +95,20 @@ open class RiveView: RiveRendererView {
|
||||
startTimer()
|
||||
}
|
||||
|
||||
/// Asks the render loop to stop on the next cycle
|
||||
internal func pause() {
|
||||
eventQueue.add {
|
||||
self.playerDelegate?.player(pausedWithModel: self.riveModel)
|
||||
if isPlaying {
|
||||
eventQueue.add {
|
||||
self.playerDelegate?.player(pausedWithModel: self.riveModel)
|
||||
}
|
||||
isPlaying = false
|
||||
}
|
||||
|
||||
isPlaying = false
|
||||
stopTimer()
|
||||
}
|
||||
|
||||
/// Asks the render loop to stop on the next cycle
|
||||
internal func stop() {
|
||||
playerDelegate?.player(stoppedWithModel: riveModel)
|
||||
self.playerDelegate?.player(stoppedWithModel: self.riveModel)
|
||||
isPlaying = false
|
||||
stopTimer()
|
||||
lastTime = 0
|
||||
}
|
||||
|
||||
// MARK: - Render Loop
|
||||
@@ -126,13 +132,14 @@ open class RiveView: RiveRendererView {
|
||||
displayLinkProxy?.invalidate()
|
||||
displayLinkProxy = nil
|
||||
lastTime = 0
|
||||
fpsCounter?.stopped()
|
||||
}
|
||||
|
||||
/// Start a redraw:
|
||||
/// - determine the elapsed time
|
||||
/// - advance the artbaord, which will invalidate the display.
|
||||
/// - if the artboard has come to a stop, stop.
|
||||
@objc func tick() {
|
||||
@objc fileprivate func tick() {
|
||||
guard let displayLink = displayLinkProxy?.displayLink else {
|
||||
stopTimer()
|
||||
return
|
||||
@@ -146,6 +153,7 @@ open class RiveView: RiveRendererView {
|
||||
|
||||
// Calculate the time elapsed between ticks
|
||||
let elapsedTime = timestamp - lastTime
|
||||
fpsCounter?.elapsed(time: elapsedTime)
|
||||
lastTime = timestamp
|
||||
advance(delta: elapsedTime)
|
||||
if !isPlaying {
|
||||
@@ -158,29 +166,37 @@ open class RiveView: RiveRendererView {
|
||||
///
|
||||
/// - Parameter delta: elapsed seconds since the last advance
|
||||
@objc open func advance(delta: Double) {
|
||||
let wasPlaying = isPlaying
|
||||
eventQueue.fireAll()
|
||||
|
||||
if let stateMachine = riveModel.stateMachine {
|
||||
isPlaying = stateMachine.advance(by: delta) && isPlaying
|
||||
stateMachine.stateChanges().forEach { stateMachineDelegate?.stateMachine?(stateMachine, didChangeState: $0) }
|
||||
isPlaying = stateMachine.advance(by: delta) && wasPlaying
|
||||
|
||||
if !isPlaying {
|
||||
pause()
|
||||
for stateChange in stateMachine.stateChanges() {
|
||||
stateMachineDelegate?.stateMachine?(stateMachine, didChangeState: stateChange)
|
||||
}
|
||||
// stateMachine.stateChanges().forEach { stateMachineDelegate?.stateMachine?(stateMachine, didChangeState: $0) }
|
||||
}
|
||||
else if let animation = riveModel.animation {
|
||||
isPlaying = animation.advance(by: delta) && isPlaying
|
||||
isPlaying = animation.advance(by: delta) && wasPlaying
|
||||
animation.apply()
|
||||
|
||||
if !isPlaying {
|
||||
pause()
|
||||
} else {
|
||||
if isPlaying {
|
||||
if animation.didLoop() {
|
||||
playerDelegate?.player(loopedWithModel: riveModel, type: Int(animation.loop()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !isPlaying {
|
||||
stopTimer()
|
||||
|
||||
// This will be true when coming to a hault automatically
|
||||
if wasPlaying {
|
||||
playerDelegate?.player(pausedWithModel: riveModel)
|
||||
}
|
||||
}
|
||||
|
||||
// advance the artboard
|
||||
riveModel.artboard.advance(by: delta)
|
||||
playerDelegate?.player(didAdvanceby: delta, riveModel: riveModel)
|
||||
|
||||
@@ -11,18 +11,25 @@ import Combine
|
||||
|
||||
open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStateMachineDelegate, RivePlayerDelegate {
|
||||
open private(set) var riveView: RiveView?
|
||||
private var asyncModelBuffer: AsyncModelBuffer? = nil
|
||||
private var defaultModel: RiveModelBuffer!
|
||||
|
||||
public init(
|
||||
_ model: RiveModel,
|
||||
stateMachineName: String? = nil,
|
||||
fit: RiveRuntime.Fit = .fitContain,
|
||||
alignment: RiveRuntime.Alignment = .alignmentCenter,
|
||||
autoPlay: Bool = true
|
||||
autoPlay: Bool = true,
|
||||
artboardName: String? = nil,
|
||||
animationName: String? = nil
|
||||
) {
|
||||
self.riveModel = model
|
||||
self.fit = fit
|
||||
self.alignment = alignment
|
||||
self.autoPlay = autoPlay
|
||||
|
||||
super.init()
|
||||
|
||||
try! configureModel(artboardName: artboardName, stateMachineName: stateMachineName, animationName: animationName)
|
||||
}
|
||||
|
||||
public init(
|
||||
@@ -53,13 +60,14 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
|
||||
artboardName: String? = nil,
|
||||
animationName: String? = nil
|
||||
) {
|
||||
super.init()
|
||||
riveModel = RiveModel(webURL: webURL, delegate: self)
|
||||
self.fit = fit
|
||||
self.alignment = alignment
|
||||
self.autoPlay = autoPlay
|
||||
|
||||
asyncModelBuffer = AsyncModelBuffer(artboardName: artboardName, stateMachineName: stateMachineName, animationName: animationName)
|
||||
super.init()
|
||||
|
||||
riveModel = RiveModel(webURL: webURL, delegate: self)
|
||||
defaultModel = RiveModelBuffer(artboardName: artboardName, stateMachineName: stateMachineName, animationName: animationName)
|
||||
}
|
||||
|
||||
// MARK: - RiveView
|
||||
@@ -74,7 +82,7 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
|
||||
|
||||
open var isPlaying: Bool { riveView?.isPlaying ?? false }
|
||||
|
||||
open var autoPlay: Bool = true
|
||||
open var autoPlay: Bool
|
||||
|
||||
open var fit: RiveRuntime.Fit = .fitContain {
|
||||
didSet { riveView?.fit = fit }
|
||||
@@ -101,6 +109,7 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
|
||||
|
||||
open func stop() {
|
||||
riveView?.stop()
|
||||
try! resetModelToDefault()
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "stop")
|
||||
@@ -115,10 +124,16 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
|
||||
if let name = artboardName {
|
||||
try riveModel?.setArtboard(name)
|
||||
} else {
|
||||
// Set default Artboard
|
||||
try riveModel?.setArtboard()
|
||||
// Keep current Artboard if there is one
|
||||
if riveModel?.artboard == nil {
|
||||
// Set default Artboard if not
|
||||
try riveModel?.setArtboard()
|
||||
}
|
||||
}
|
||||
|
||||
riveModel?.animation = nil
|
||||
riveModel?.stateMachine = nil
|
||||
|
||||
if let name = stateMachineName {
|
||||
try riveModel?.setStateMachine(name)
|
||||
}
|
||||
@@ -131,6 +146,15 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
|
||||
}
|
||||
|
||||
try riveView?.setModel(riveModel!, autoPlay: autoPlay)
|
||||
defaultModel = RiveModelBuffer(artboardName: artboardName, stateMachineName: stateMachineName, animationName: animationName)
|
||||
}
|
||||
|
||||
private func resetModelToDefault() throws {
|
||||
try configureModel(
|
||||
artboardName: defaultModel.artboardName,
|
||||
stateMachineName: defaultModel.stateMachineName,
|
||||
animationName: defaultModel.animationName
|
||||
)
|
||||
}
|
||||
|
||||
open func triggerInput(_ inputName: String) throws {
|
||||
@@ -153,38 +177,7 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
|
||||
play()
|
||||
}
|
||||
|
||||
// MARK: - RiveFile Delegate
|
||||
|
||||
/// Needed for when the RiveViewModel is initialized with a webURL so we can make a RiveModel
|
||||
/// when the RiveFile is finished downloading
|
||||
private struct AsyncModelBuffer {
|
||||
var artboardName: String?
|
||||
var stateMachineName: String?
|
||||
var animationName: String?
|
||||
}
|
||||
|
||||
/// Called by RiveFile when it finishes downloading an asset asynchronously
|
||||
public func riveFileDidLoad(_ riveFile: RiveFile) throws {
|
||||
riveModel = RiveModel(riveFile: riveFile)
|
||||
|
||||
try! configureModel(
|
||||
artboardName: asyncModelBuffer?.artboardName,
|
||||
stateMachineName: asyncModelBuffer?.stateMachineName,
|
||||
animationName: asyncModelBuffer?.animationName
|
||||
)
|
||||
|
||||
asyncModelBuffer = nil
|
||||
}
|
||||
|
||||
// MARK: - RivePlayer Delegate
|
||||
|
||||
open func player(playedWithModel riveModel: RiveModel?) { }
|
||||
open func player(pausedWithModel riveModel: RiveModel?) { }
|
||||
open func player(loopedWithModel riveModel: RiveModel?, type: Int) { }
|
||||
open func player(stoppedWithModel riveModel: RiveModel?) { }
|
||||
open func player(didAdvanceby seconds: Double, riveModel: RiveModel?) { }
|
||||
|
||||
// MARK: SwiftUI Helpers
|
||||
// MARK: - SwiftUI Helpers
|
||||
|
||||
/// Makes a new `RiveView` for the instance property with data from model which will
|
||||
/// replace any previous `RiveView`. This is called when first drawing a `StandardView`.
|
||||
@@ -193,7 +186,7 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
|
||||
let view: RiveView
|
||||
|
||||
if let model = riveModel {
|
||||
view = RiveView(model: model)
|
||||
view = RiveView(model: model, autoPlay: autoPlay)
|
||||
} else {
|
||||
view = RiveView()
|
||||
}
|
||||
@@ -244,7 +237,7 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: UIKit Helper
|
||||
// MARK: - UIKit Helper
|
||||
|
||||
/// This can be used to connect with and configure an `RiveView` that was created elsewhere.
|
||||
/// Does not need to be called when updating an already configured `RiveView`. Useful for
|
||||
@@ -254,6 +247,35 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
|
||||
registerView(view)
|
||||
try! riveView!.setModel(riveModel!, autoPlay: autoPlay)
|
||||
}
|
||||
|
||||
// MARK: - RiveFile Delegate
|
||||
|
||||
/// Needed for when resetting to defaults or the RiveViewModel is initialized with a webURL so
|
||||
/// we are then able make a RiveModel when the RiveFile is finished downloading
|
||||
private struct RiveModelBuffer {
|
||||
var artboardName: String?
|
||||
var stateMachineName: String?
|
||||
var animationName: String?
|
||||
}
|
||||
|
||||
/// Called by RiveFile when it finishes downloading an asset asynchronously
|
||||
public func riveFileDidLoad(_ riveFile: RiveFile) throws {
|
||||
riveModel = RiveModel(riveFile: riveFile)
|
||||
|
||||
try! configureModel(
|
||||
artboardName: defaultModel.artboardName,
|
||||
stateMachineName: defaultModel.stateMachineName,
|
||||
animationName: defaultModel.animationName
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - RivePlayer Delegate
|
||||
|
||||
open func player(playedWithModel riveModel: RiveModel?) { }
|
||||
open func player(pausedWithModel riveModel: RiveModel?) { }
|
||||
open func player(loopedWithModel riveModel: RiveModel?, type: Int) { }
|
||||
open func player(stoppedWithModel riveModel: RiveModel?) { }
|
||||
open func player(didAdvanceby seconds: Double, riveModel: RiveModel?) { }
|
||||
}
|
||||
|
||||
/// This makes a SwiftUI digestable view from an `RiveViewModel` and its `RiveView`
|
||||
|
||||
@@ -9,25 +9,26 @@
|
||||
import XCTest
|
||||
import RiveRuntime
|
||||
|
||||
func getBytes(resourceName: String, resourceExt: String=".riv") -> [UInt8] {
|
||||
guard let url = Bundle(for: DelegatesTest.self).url(forResource: resourceName, withExtension: resourceExt) else {
|
||||
fatalError("Failed to locate \(resourceName) in bundle.")
|
||||
}
|
||||
guard let data = try? Data(contentsOf: url) else {
|
||||
fatalError("Failed to load \(url) from bundle.")
|
||||
extension RiveFile {
|
||||
convenience init(testfileName: String, extension ext: String = ".riv") throws {
|
||||
let byteArray = RiveFile.getBytes(fileName: testfileName, extension: ext)
|
||||
try self.init(byteArray: byteArray)
|
||||
}
|
||||
|
||||
// Import the data into a RiveFile
|
||||
return [UInt8](data)
|
||||
static func getBytes(fileName: String, extension ext: String = ".riv") -> [UInt8] {
|
||||
guard let url = Bundle(for: DelegatesTest.self).url(forResource: fileName, withExtension: ext) else {
|
||||
fatalError("Failed to locate \(fileName) in bundle.")
|
||||
}
|
||||
guard let data = try? Data(contentsOf: url) else {
|
||||
fatalError("Failed to load \(url) from bundle.")
|
||||
}
|
||||
|
||||
// Import the data into a RiveFile
|
||||
return [UInt8](data)
|
||||
}
|
||||
}
|
||||
|
||||
func getRiveFile(resourceName: String, resourceExt: String=".riv") throws -> RiveFile {
|
||||
let byteArray = getBytes(resourceName: resourceName, resourceExt: resourceExt)
|
||||
let riveFile = try RiveFile(byteArray: byteArray)
|
||||
return riveFile
|
||||
}
|
||||
|
||||
class MrDelegate: RivePlayerDelegate, RStateDelegate {
|
||||
class DrDelegate: RivePlayerDelegate, RiveStateMachineDelegate {
|
||||
var stateMachinePlays = [String]()
|
||||
var stateMachinePauses = [String]()
|
||||
var stateMachineStops = [String]()
|
||||
@@ -38,167 +39,183 @@ class MrDelegate: RivePlayerDelegate, RStateDelegate {
|
||||
var stateMachineNames = [String]()
|
||||
var stateMachineStates = [String]()
|
||||
|
||||
func loop(animation animationName: String, type: Int) {
|
||||
loops.append(animationName)
|
||||
}
|
||||
|
||||
func play(animation animationName: String, isStateMachine: Bool) {
|
||||
if (isStateMachine){
|
||||
stateMachinePlays.append(animationName)
|
||||
func player(playedWithModel riveModel: RiveModel?) {
|
||||
if let stateMachineName = riveModel?.stateMachine?.name() {
|
||||
stateMachinePlays.append(stateMachineName)
|
||||
}
|
||||
else {
|
||||
else if let animationName = riveModel?.animation?.name() {
|
||||
linearAnimaitonPlays.append(animationName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func pause(animation animationName: String, isStateMachine: Bool) {
|
||||
if (isStateMachine){
|
||||
stateMachinePauses.append(animationName)
|
||||
func player(pausedWithModel riveModel: RiveModel?) {
|
||||
if let stateMachineName = riveModel?.stateMachine?.name() {
|
||||
stateMachinePauses.append(stateMachineName)
|
||||
}
|
||||
else {
|
||||
else if let animationName = riveModel?.animation?.name() {
|
||||
linearAnimaitonPauses.append(animationName)
|
||||
}
|
||||
}
|
||||
|
||||
func stop(animation animationName: String, isStateMachine: Bool) {
|
||||
if (isStateMachine){
|
||||
stateMachineStops.append(animationName)
|
||||
func player(loopedWithModel riveModel: RiveModel?, type: Int) {
|
||||
if let stateMachineName = riveModel?.stateMachine?.name() {
|
||||
loops.append(stateMachineName)
|
||||
}
|
||||
else {
|
||||
else if let animationName = riveModel?.animation?.name() {
|
||||
loops.append(animationName)
|
||||
}
|
||||
}
|
||||
|
||||
func player(stoppedWithModel riveModel: RiveModel?) {
|
||||
if let stateMachineName = riveModel?.stateMachine?.name() {
|
||||
stateMachineStops.append(stateMachineName)
|
||||
}
|
||||
else if let animationName = riveModel?.animation?.name() {
|
||||
linearAnimaitonStops.append(animationName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func stateChange(_ stateMachineName:String, _ stateName: String) {
|
||||
stateMachineNames.append(stateMachineName)
|
||||
func player(didAdvanceby seconds: Double, riveModel: RiveModel?) { }
|
||||
|
||||
func stateMachine(_ stateMachine: RiveStateMachineInstance, didChangeState stateName: String) {
|
||||
stateMachineNames.append(stateMachine.name())
|
||||
stateMachineStates.append(stateName)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Technical Note:
|
||||
// We manually call view.advance(0) in these tests because the results are based on
|
||||
// messages received by a delegate which is triggered by a timer. We can't wait for
|
||||
// the timer so we trigger it manually
|
||||
class DelegatesTest: XCTestCase {
|
||||
func testPlay() throws {
|
||||
let delegate = MrDelegate()
|
||||
let view = try RiveView.init(
|
||||
riveFile: getRiveFile(resourceName:"multiple_animations"),
|
||||
autoplay: false,
|
||||
playerDelegate: delegate
|
||||
)
|
||||
try view.play(animationName: "one")
|
||||
view.advance(delta:0)
|
||||
let delegate = DrDelegate()
|
||||
let file = try RiveFile(testfileName: "multiple_animations")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, autoPlay: false)
|
||||
let view = viewModel.createRiveView()
|
||||
|
||||
view.playerDelegate = delegate
|
||||
viewModel.play(animationName: "one")
|
||||
|
||||
// This is necessary because play starts a timer that will eventually get to
|
||||
// .advance(n) which triggers the event queue which sends word to the delegate...
|
||||
// But the assert happens too fast, so we need to advance manually beforehand.
|
||||
view.advance(delta: 0)
|
||||
XCTAssertEqual(delegate.linearAnimaitonPlays.count, 1)
|
||||
}
|
||||
|
||||
func testPlayTwice() throws {
|
||||
let delegate = MrDelegate()
|
||||
let view = try RiveView.init(
|
||||
riveFile: getRiveFile(resourceName:"multiple_animations"),
|
||||
autoplay: false,
|
||||
playerDelegate: delegate
|
||||
)
|
||||
try view.play(animationName: "one")
|
||||
try view.play(animationName: "one")
|
||||
view.advance(delta:0)
|
||||
let delegate = DrDelegate()
|
||||
let file = try RiveFile(testfileName: "multiple_animations")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, autoPlay: false)
|
||||
let view = viewModel.createRiveView()
|
||||
|
||||
view.playerDelegate = delegate
|
||||
viewModel.play(animationName: "one")
|
||||
viewModel.play(animationName: "one")
|
||||
view.advance(delta: 0)
|
||||
XCTAssertEqual(delegate.linearAnimaitonPlays.count, 2)
|
||||
}
|
||||
|
||||
func testPause() throws {
|
||||
let delegate = MrDelegate()
|
||||
let view = try RiveView.init(
|
||||
riveFile: getRiveFile(resourceName:"multiple_animations"),
|
||||
autoplay: false,
|
||||
playerDelegate: delegate
|
||||
)
|
||||
try view.play(animationName: "one")
|
||||
view.pause(animationName: "one")
|
||||
view.advance(delta:0)
|
||||
let delegate = DrDelegate()
|
||||
let file = try RiveFile(testfileName: "multiple_animations")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, autoPlay: false)
|
||||
let view = viewModel.createRiveView()
|
||||
|
||||
view.playerDelegate = delegate
|
||||
viewModel.play(animationName: "one")
|
||||
viewModel.pause()
|
||||
view.advance(delta: 0)
|
||||
XCTAssertEqual(delegate.linearAnimaitonPauses.count, 1)
|
||||
}
|
||||
|
||||
func testPauseWhenNotPlaying() throws {
|
||||
let delegate = MrDelegate()
|
||||
let view = try RiveView.init(
|
||||
riveFile: getRiveFile(resourceName:"multiple_animations"),
|
||||
autoplay: false,
|
||||
playerDelegate: delegate
|
||||
)
|
||||
view.pause(animationName: "one")
|
||||
view.advance(delta:0)
|
||||
let delegate = DrDelegate()
|
||||
let file = try RiveFile(testfileName: "multiple_animations")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, autoPlay: false)
|
||||
let view = viewModel.createRiveView()
|
||||
|
||||
view.playerDelegate = delegate
|
||||
viewModel.pause()
|
||||
view.advance(delta: 0)
|
||||
XCTAssertEqual(delegate.linearAnimaitonPauses.count, 0)
|
||||
}
|
||||
|
||||
func testStop() throws {
|
||||
let delegate = MrDelegate()
|
||||
let view = try RiveView.init(
|
||||
riveFile: getRiveFile(resourceName:"multiple_animations"),
|
||||
autoplay: false,
|
||||
playerDelegate: delegate
|
||||
)
|
||||
try view.play(animationName: "one")
|
||||
view.stop(animationName: "one")
|
||||
view.advance(delta:0)
|
||||
let delegate = DrDelegate()
|
||||
let file = try RiveFile(testfileName: "multiple_animations")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, autoPlay: false)
|
||||
let view = viewModel.createRiveView()
|
||||
|
||||
view.playerDelegate = delegate
|
||||
viewModel.play(animationName: "one")
|
||||
viewModel.stop()
|
||||
view.advance(delta: 0)
|
||||
XCTAssertEqual(delegate.linearAnimaitonStops.count, 1)
|
||||
}
|
||||
|
||||
func testStopNotMounted() throws {
|
||||
let delegate = MrDelegate()
|
||||
let view = try RiveView.init(
|
||||
riveFile: getRiveFile(resourceName:"multiple_animations"),
|
||||
autoplay: false,
|
||||
playerDelegate: delegate
|
||||
)
|
||||
let delegate = DrDelegate()
|
||||
let file = try RiveFile(testfileName: "multiple_animations")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, autoPlay: false)
|
||||
let view = viewModel.createRiveView()
|
||||
|
||||
view.stop(animationName: "one")
|
||||
view.advance(delta:0)
|
||||
XCTAssertEqual(delegate.linearAnimaitonStops.count, 0)
|
||||
view.playerDelegate = delegate
|
||||
viewModel.stop()
|
||||
view.advance(delta: 0)
|
||||
XCTAssertEqual(delegate.linearAnimaitonStops.count, 1)
|
||||
}
|
||||
|
||||
func testStopPaused() throws {
|
||||
let delegate = MrDelegate()
|
||||
let view = try RiveView.init(
|
||||
riveFile: getRiveFile(resourceName:"multiple_animations"),
|
||||
autoplay: false,
|
||||
playerDelegate: delegate
|
||||
)
|
||||
try view.play(animationName: "one")
|
||||
view.pause(animationName: "one")
|
||||
view.stop(animationName: "one")
|
||||
view.advance(delta:0)
|
||||
let delegate = DrDelegate()
|
||||
let file = try RiveFile(testfileName: "multiple_animations")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, autoPlay: false)
|
||||
let view = viewModel.createRiveView()
|
||||
|
||||
view.playerDelegate = delegate
|
||||
viewModel.play(animationName: "one")
|
||||
viewModel.pause()
|
||||
viewModel.stop()
|
||||
view.advance(delta: 0)
|
||||
viewModel.riveView?.advance(delta: 0)
|
||||
XCTAssertEqual(delegate.linearAnimaitonStops.count, 1)
|
||||
}
|
||||
|
||||
func testLoopOneShot() throws {
|
||||
let delegate = MrDelegate()
|
||||
let view = try RiveView.init(
|
||||
riveFile: getRiveFile(resourceName:"multiple_animations"),
|
||||
autoplay: false,
|
||||
playerDelegate: delegate
|
||||
)
|
||||
let delegate = DrDelegate()
|
||||
let file = try RiveFile(testfileName: "multiple_animations")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, autoPlay: false)
|
||||
let view = viewModel.createRiveView()
|
||||
|
||||
try view.play(animationName: "one", loop: .loopOneShot)
|
||||
view.advance(delta:Double(view.animations.first!.effectiveDurationInSeconds()+0.1))
|
||||
// rough. we need an extra advance to flush the stop.
|
||||
view.advance(delta:0.1)
|
||||
view.playerDelegate = delegate
|
||||
viewModel.play(animationName: "one", loop: .loopOneShot)
|
||||
view.advance(delta: Double(viewModel.riveModel!.animation!.effectiveDurationInSeconds()+0.1))
|
||||
|
||||
XCTAssertEqual(delegate.loops.count, 0)
|
||||
XCTAssertEqual(delegate.linearAnimaitonPlays.count, 1)
|
||||
XCTAssertEqual(delegate.linearAnimaitonPauses.count, 0)
|
||||
XCTAssertEqual(delegate.linearAnimaitonStops.count, 1)
|
||||
XCTAssertEqual(delegate.linearAnimaitonPauses.count, 1)
|
||||
XCTAssertEqual(delegate.linearAnimaitonStops.count, 0)
|
||||
}
|
||||
|
||||
func testLoopLoop() throws {
|
||||
let delegate = MrDelegate()
|
||||
let view = try RiveView.init(
|
||||
riveFile: getRiveFile(resourceName:"multiple_animations"),
|
||||
autoplay: false,
|
||||
playerDelegate: delegate
|
||||
)
|
||||
let delegate = DrDelegate()
|
||||
let file = try RiveFile(testfileName: "multiple_animations")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, autoPlay: false)
|
||||
let view = viewModel.createRiveView()
|
||||
|
||||
try view.play(animationName: "one", loop: .loopLoop)
|
||||
view.advance(delta:Double(view.animations.first!.effectiveDurationInSeconds()+0.1))
|
||||
view.playerDelegate = delegate
|
||||
viewModel.play(animationName: "one", loop: .loopLoop)
|
||||
view.advance(delta: Double(viewModel.riveModel!.animation!.effectiveDurationInSeconds()+0.1))
|
||||
|
||||
XCTAssertEqual(delegate.loops.count, 1)
|
||||
XCTAssertEqual(delegate.linearAnimaitonPlays.count, 1)
|
||||
@@ -207,15 +224,15 @@ class DelegatesTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testLoopPingPong() throws {
|
||||
let delegate = MrDelegate()
|
||||
let view = try RiveView.init(
|
||||
riveFile: getRiveFile(resourceName:"multiple_animations"),
|
||||
autoplay: false,
|
||||
playerDelegate: delegate
|
||||
)
|
||||
let delegate = DrDelegate()
|
||||
let file = try RiveFile(testfileName: "multiple_animations")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, autoPlay: false)
|
||||
let view = viewModel.createRiveView()
|
||||
|
||||
try view.play(animationName: "one", loop: .loopPingPong)
|
||||
view.advance(delta:Double(view.animations.first!.effectiveDurationInSeconds()+0.1))
|
||||
view.playerDelegate = delegate
|
||||
viewModel.play(animationName: "one", loop: .loopPingPong)
|
||||
view.advance(delta: Double(viewModel.riveModel!.animation!.effectiveDurationInSeconds()+0.1))
|
||||
|
||||
XCTAssertEqual(delegate.loops.count, 1)
|
||||
XCTAssertEqual(delegate.linearAnimaitonPlays.count, 1)
|
||||
@@ -224,93 +241,96 @@ class DelegatesTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testStateMachineLayerStates() throws {
|
||||
let delegate = MrDelegate()
|
||||
let view = try RiveView.init(
|
||||
riveFile: getRiveFile(resourceName:"what_a_state"),
|
||||
stateMachineName: "State Machine 2",
|
||||
playerDelegate: delegate,
|
||||
stateChangeDelegate: delegate
|
||||
)
|
||||
|
||||
let delegate = DrDelegate()
|
||||
let file = try RiveFile(testfileName: "what_a_state")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, stateMachineName: "State Machine 2", autoPlay: true)
|
||||
let view = viewModel.createRiveView()
|
||||
|
||||
view.playerDelegate = delegate
|
||||
view.stateMachineDelegate = delegate
|
||||
|
||||
view.advance(delta:0.1)
|
||||
XCTAssertEqual(delegate.stateMachinePlays.count, 1)
|
||||
XCTAssertEqual(delegate.stateMachineStates.count, 1)
|
||||
XCTAssertEqual(delegate.stateMachineNames[0], "State Machine 2")
|
||||
XCTAssertEqual(delegate.stateMachineStates[0], "go right")
|
||||
|
||||
view.advance(delta:1.1)
|
||||
XCTAssertEqual(delegate.stateMachineStates.count, 2)
|
||||
XCTAssertEqual(delegate.stateMachineNames[1], "State Machine 2")
|
||||
XCTAssertEqual(delegate.stateMachineStates[1], "ExitState")
|
||||
|
||||
// takes an extra advance to trigger
|
||||
view.advance(delta:0)
|
||||
XCTAssertEqual(delegate.stateMachinePauses.count, 1)
|
||||
}
|
||||
|
||||
func testStateMachineLayerStatesComplex() throws {
|
||||
let delegate = MrDelegate()
|
||||
let view = try RiveView.init(
|
||||
riveFile: getRiveFile(resourceName:"what_a_state"),
|
||||
stateMachineName: "State Machine 1",
|
||||
stateChangeDelegate: delegate
|
||||
)
|
||||
|
||||
view.advance(delta:0.0)
|
||||
XCTAssertEqual(delegate.stateMachineStates.count, 0)
|
||||
|
||||
// lets just start, expect 1 change.
|
||||
try view.fireState("State Machine 1", inputName: "right")
|
||||
// TODO: looks like we got a bit of a bug here. if we do not call this advance,
|
||||
// the first animation doesnt seem to get the delta applied. i think its all because of
|
||||
// how the
|
||||
view.advance(delta:0.0)
|
||||
view.advance(delta:0.4)
|
||||
XCTAssertEqual(delegate.stateMachineStates.count, 1)
|
||||
XCTAssertEqual(delegate.stateMachineStates[0], "go right")
|
||||
XCTAssertEqual(delegate.stateMachineNames.count, 1)
|
||||
XCTAssertEqual(delegate.stateMachineNames[0], "State Machine 1")
|
||||
delegate.stateMachineStates.removeAll()
|
||||
|
||||
|
||||
// should be in same animation still. no state change
|
||||
view.advance(delta:0.4)
|
||||
XCTAssertEqual(0, delegate.stateMachineStates.count)
|
||||
XCTAssertEqual(true, view.isPlaying)
|
||||
|
||||
// animation came to an end inside this time period, this still means no state change
|
||||
view.advance(delta:0.4)
|
||||
XCTAssertEqual(false, view.isPlaying)
|
||||
XCTAssertEqual(0, delegate.stateMachineStates.count)
|
||||
|
||||
// animation is just kinda stuck there. no change no happening.
|
||||
view.advance(delta:0.4)
|
||||
XCTAssertEqual(false, view.isPlaying)
|
||||
XCTAssertEqual(0, delegate.stateMachineStates.count)
|
||||
|
||||
// ok lets change thigns up again.
|
||||
try view.fireState("State Machine 1", inputName: "change")
|
||||
view.advance(delta:0.0)
|
||||
view.advance(delta:0.4)
|
||||
XCTAssertEqual(true, view.isPlaying)
|
||||
XCTAssertEqual(1, delegate.stateMachineStates.count)
|
||||
|
||||
XCTAssertEqual("change!", delegate.stateMachineStates[0])
|
||||
delegate.stateMachineStates.removeAll()
|
||||
|
||||
// as before lets advance inside the animation -> no change
|
||||
view.advance(delta:0.4)
|
||||
XCTAssertEqual(true, view.isPlaying)
|
||||
XCTAssertEqual(0, delegate.stateMachineStates.count)
|
||||
|
||||
// as before lets advance beyond the end of the animaiton, in this case change to exit!
|
||||
view.advance(delta:0.4)
|
||||
XCTAssertEqual(false, view.isPlaying)
|
||||
XCTAssertEqual(1, delegate.stateMachineStates.count)
|
||||
XCTAssertEqual("ExitState", delegate.stateMachineStates[0])
|
||||
delegate.stateMachineStates.removeAll()
|
||||
|
||||
// chill on exit. no change.
|
||||
view.advance(delta:0.4)
|
||||
XCTAssertEqual(false, view.isPlaying)
|
||||
XCTAssertEqual(0, delegate.stateMachineStates.count)
|
||||
// let delegate = DrDelegate()
|
||||
// let file = try RiveFile(testfileName: "what_a_state")
|
||||
// let model = RiveModel(riveFile: file)
|
||||
// let viewModel = RiveViewModel(model, stateMachineName: "State Machine 1", autoPlay: true)
|
||||
// let view = viewModel.createRiveView()
|
||||
//
|
||||
// view.stateMachineDelegate = delegate
|
||||
// view.advance(delta:0.0)
|
||||
// XCTAssertEqual(delegate.stateMachineStates.count, 0)
|
||||
// viewModel.play()
|
||||
// // MARK: Input
|
||||
// // lets just start, expect 1 change.
|
||||
// try viewModel.triggerInput("right")
|
||||
// // TODO: looks like we got a bit of a bug here
|
||||
// // If we do not call this advance, the first animation doesnt seem to get the delta applied.
|
||||
// view.advance(delta:0.0)
|
||||
// view.advance(delta:0.4)
|
||||
// XCTAssertEqual(delegate.stateMachineStates.count, 1)
|
||||
// XCTAssertEqual(delegate.stateMachineStates[0], "go right")
|
||||
// XCTAssertEqual(delegate.stateMachineNames.count, 1)
|
||||
// XCTAssertEqual(delegate.stateMachineNames[0], "State Machine 1")
|
||||
// delegate.stateMachineStates.removeAll()
|
||||
//
|
||||
// // should be in same animation still. no state change
|
||||
// view.advance(delta:0.4)
|
||||
// XCTAssertEqual(0, delegate.stateMachineStates.count)
|
||||
// XCTAssertEqual(true, viewModel.isPlaying)
|
||||
//
|
||||
// // animation came to an end inside this time period, this still means no state change
|
||||
// view.advance(delta:0.4)
|
||||
// XCTAssertEqual(false, viewModel.isPlaying)
|
||||
// XCTAssertEqual(0, delegate.stateMachineStates.count)
|
||||
//
|
||||
// // animation is just kinda stuck there. no change no happening.
|
||||
// view.advance(delta:0.4)
|
||||
// XCTAssertEqual(false, viewModel.isPlaying)
|
||||
// XCTAssertEqual(0, delegate.stateMachineStates.count)
|
||||
//
|
||||
// // MARK: Input
|
||||
// // ok lets change thigns up again.
|
||||
// try viewModel.triggerInput("change")
|
||||
// view.advance(delta:0.0)
|
||||
// view.advance(delta:0.4)
|
||||
// XCTAssertEqual(true, viewModel.isPlaying)
|
||||
// XCTAssertEqual(1, delegate.stateMachineStates.count)
|
||||
//
|
||||
// XCTAssertEqual("change!", delegate.stateMachineStates[0])
|
||||
// delegate.stateMachineStates.removeAll()
|
||||
//
|
||||
// // as before lets advance inside the animation -> no change
|
||||
// view.advance(delta:0.4)
|
||||
// XCTAssertEqual(true, viewModel.isPlaying)
|
||||
// XCTAssertEqual(0, delegate.stateMachineStates.count)
|
||||
//
|
||||
// // as before lets advance beyond the end of the animaiton, in this case change to exit!
|
||||
// view.advance(delta:0.4)
|
||||
// XCTAssertEqual(false, viewModel.isPlaying)
|
||||
// XCTAssertEqual(1, delegate.stateMachineStates.count)
|
||||
// XCTAssertEqual("ExitState", delegate.stateMachineStates[0])
|
||||
// delegate.stateMachineStates.removeAll()
|
||||
//
|
||||
// // chill on exit. no change.
|
||||
// view.advance(delta:0.4)
|
||||
// XCTAssertEqual(false, viewModel.isPlaying)
|
||||
// XCTAssertEqual(0, delegate.stateMachineStates.count)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user