From 01c71ed5c3d23ecc3db8f95cd13a70b2f983a50d Mon Sep 17 00:00:00 2001 From: matt Sullivan Date: Mon, 10 May 2021 16:37:33 -0700 Subject: [PATCH 1/2] Hacked in play/pause to SwiftUI --- .../Source/SwiftUI/RiveSwiftUIView.swift | 27 ++- .../Source/SwiftUI/RiveViewWrapper.swift | 52 +++-- Example-iOS/Source/UIkit/Layout.swift | 4 +- Source/Renderer/Rive.h | 1 + Source/Renderer/Rive.mm | 17 +- Source/Views/RiveView.swift | 185 ++++++++++++++---- 6 files changed, 219 insertions(+), 67 deletions(-) diff --git a/Example-iOS/Source/SwiftUI/RiveSwiftUIView.swift b/Example-iOS/Source/SwiftUI/RiveSwiftUIView.swift index 89ddc867..904887d5 100644 --- a/Example-iOS/Source/SwiftUI/RiveSwiftUIView.swift +++ b/Example-iOS/Source/SwiftUI/RiveSwiftUIView.swift @@ -3,7 +3,7 @@ import RiveRuntime struct RiveSwiftUIView: View { var dismiss: () -> Void = {} - @State private var isPlaying: Bool = true + @State private var playback: Playback = Playback.play @State private var fit: Fit = Fit.Cover @State private var alignment: RiveRuntime.Alignment = RiveRuntime.Alignment.Center @State private var loopCount: Int = 0 @@ -14,8 +14,15 @@ struct RiveSwiftUIView: View { resource: "basketball", fit: fit, alignment: alignment, + playback: playback, loopAction: { name, type in loopCount += 1 + }, + playAction: { name in + print("Playing \(name)") + }, + pauseAction: { name in + print("Pausing \(name)") } ) VStack { @@ -45,8 +52,22 @@ struct RiveSwiftUIView: View { } .padding() HStack { - Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/, - label: { Text(isPlaying ? "Pause" : "Play") }) + Button(action: { + if playback == Playback.play { + playback = Playback.pause + } else { + playback = Playback.play + } + }, + label: { + switch playback { + case .play: + Text("Pause") + case .pause, .stop: + Text("Play") + } + }) + Spacer() Button(action: dismiss, label: { Text("Dismiss") diff --git a/Example-iOS/Source/SwiftUI/RiveViewWrapper.swift b/Example-iOS/Source/SwiftUI/RiveViewWrapper.swift index e27e8a0c..453391f3 100644 --- a/Example-iOS/Source/SwiftUI/RiveViewWrapper.swift +++ b/Example-iOS/Source/SwiftUI/RiveViewWrapper.swift @@ -1,52 +1,72 @@ import SwiftUI import RiveRuntime -typealias LoopAction = ((String, Int) -> Void)? - struct UIRiveView: UIViewRepresentable { let resource: String var fit: Fit = Fit.Contain var alignment: RiveRuntime.Alignment = RiveRuntime.Alignment.Center + var playback: Playback = Playback.play + + // Delegate handlers for loop and play events var loopAction: LoopAction = nil + var playAction: PlaybackAction = nil + var pauseAction: PlaybackAction = nil - // Constructs the view + /// Constructs the view func makeUIView(context: Context) -> RiveView { - let riveView = RiveView(riveFile: getRiveFile(resourceName: resource)) - riveView.setFit(fit: fit) - riveView.setAlignment(alignment: alignment) - - // Set the delegates - riveView.loopDelegate = context.coordinator - + let riveView = RiveView( + riveFile: getRiveFile(resourceName: resource), + fit: fit, + alignment: alignment, + autoplay: playback == Playback.play, + loopDelegate: context.coordinator, + playDelegate: context.coordinator, + pauseDelegate: context.coordinator + ) return riveView } - // Called when a bound variable changes state + /// Called when a bound variable changes state func updateUIView(_ uiView: RiveView, context: Context) { print("updateUI") - uiView.setFit(fit: fit) - uiView.setAlignment(alignment: alignment) + uiView.fit = fit + uiView.alignment = alignment + uiView.playback = playback } // Constructs a coordinator for managing updating state func makeCoordinator() -> Coordinator { // Coordinator(loopCount: $loopCount) - Coordinator(loopAction: loopAction) + Coordinator(loopAction: loopAction, playAction: playAction, pauseAction: pauseAction) } } // Coordinator between RiveView and UIRiveView -class Coordinator: NSObject, LoopDelegate { +class Coordinator: NSObject, LoopDelegate, PlayDelegate, PauseDelegate { + private var loopAction: LoopAction + private var playAction: PlaybackAction + private var pauseAction: PlaybackAction + - init(loopAction: LoopAction) { + init(loopAction: LoopAction, playAction: PlaybackAction, pauseAction: PlaybackAction) { self.loopAction = loopAction + self.playAction = playAction + self.pauseAction = pauseAction } func loop(_ animationName: String, type: Int) { loopAction?(animationName, type) } + + func play(_ animationName: String) { + playAction?(animationName) + } + + func pause(_ animationName: String) { + pauseAction?(animationName) + } // @Binding private var loopCount: Int // diff --git a/Example-iOS/Source/UIkit/Layout.swift b/Example-iOS/Source/UIkit/Layout.swift index 96bcc21a..8b6a6a4d 100644 --- a/Example-iOS/Source/UIkit/Layout.swift +++ b/Example-iOS/Source/UIkit/Layout.swift @@ -58,7 +58,7 @@ class LayoutViewController: UIViewController { default: fit = Fit.Contain } - layoutView.riveView.setFit(fit:fit) + layoutView.riveView.fit = fit } func setAlignmnet(name:String){ @@ -85,7 +85,7 @@ class LayoutViewController: UIViewController { default: alignment = Alignment.Center } - layoutView.riveView.setAlignment(alignment:alignment) + layoutView.riveView.alignment = alignment } layoutView.fitButtonAction = setFit diff --git a/Source/Renderer/Rive.h b/Source/Renderer/Rive.h index ec753e88..1d05b317 100644 --- a/Source/Renderer/Rive.h +++ b/Source/Renderer/Rive.h @@ -81,6 +81,7 @@ typedef NS_ENUM(NSInteger, Alignment) { - (const RiveSMIBool *)getBool:(NSString*)name; - (const RiveSMITrigger *)getTrigger:(NSString*)name; - (const RiveSMINumber *)getNumber:(NSString*)name; +- (NSString* )name; @end diff --git a/Source/Renderer/Rive.mm b/Source/Renderer/Rive.mm index 05fecf14..ae0ee2ac 100644 --- a/Source/Renderer/Rive.mm +++ b/Source/Renderer/Rive.mm @@ -32,7 +32,7 @@ - (instancetype)initWithStateMachine:(const rive::StateMachine *)stateMachine; * RiveStateMachine interface */ @interface RiveStateMachine () -- (instancetype)initWithStateMachine:(const rive::StateMachine *)riveStateMachine; +- (instancetype)initWithStateMachine:(const rive::StateMachine *)stateMachine; @end /* @@ -449,9 +449,9 @@ @implementation RiveStateMachine { } // Creates a new RiveStateMachine from a cpp StateMachine -- (instancetype)initWithStateMachine:(const rive::StateMachine *)riveStateMachine { +- (instancetype)initWithStateMachine:(const rive::StateMachine *)stateMachine { if (self = [super init]) { - stateMachine = riveStateMachine; + self->stateMachine = stateMachine; return self; } else { return nil; @@ -485,12 +485,14 @@ - (RiveStateMachineInstance *)instance { * RiveStateMachineInstance */ @implementation RiveStateMachineInstance { + const rive::StateMachine *stateMachine; rive::StateMachineInstance *instance; } // Creates a new RiveStateMachineInstance from a cpp StateMachine - (instancetype)initWithStateMachine:(const rive::StateMachine *)stateMachine { if (self = [super init]) { + self->stateMachine = stateMachine; instance = new rive::StateMachineInstance(stateMachine); return self; } else { @@ -498,11 +500,11 @@ - (instancetype)initWithStateMachine:(const rive::StateMachine *)stateMachine { } } --(void) applyTo:(RiveArtboard*) artboard { +- (void) applyTo:(RiveArtboard*)artboard { instance->apply(artboard.artboard); } --(bool) advanceBy:(double)elapsedSeconds { +- (bool) advanceBy:(double)elapsedSeconds { return instance->advance(elapsedSeconds); } @@ -541,6 +543,11 @@ - (RiveSMINumber *)getNumber:(NSString *) name { } } +- (NSString *)name { + std::string str = stateMachine->name(); + return [NSString stringWithCString:str.c_str() encoding:[NSString defaultCStringEncoding]]; +} + @end /* diff --git a/Source/Views/RiveView.swift b/Source/Views/RiveView.swift index 507823e2..a90c002a 100644 --- a/Source/Views/RiveView.swift +++ b/Source/Views/RiveView.swift @@ -8,17 +8,69 @@ import UIKit +// Signature for a loop action delegate function +public typealias LoopAction = ((String, Int) -> Void)? + // Delegate for handling loop events public protocol LoopDelegate: AnyObject { func loop(_ animationName: String, type: Int) } +// signature for a play action delegate function +public typealias PlaybackAction = ((String) -> Void)? + +// Delegate for handling play action +public protocol PlayDelegate: AnyObject { + func play(_ animationName: String) +} + +// Delegate for handling pause action +public protocol PauseDelegate: AnyObject { + func pause(_ animationName: String) +} + +/// Playback states for a Rive file +public enum Playback { + case play + case pause + case stop +} + public class RiveView: UIView { var displayLink: CADisplayLink? - var fit = Fit.Contain - var alignment = Alignment.Center + // Queue of events that need to be done outside view updates + private var eventQueue = EventQueue() + + private var _fit = Fit.Contain + + open var fit: Fit { + set { _fit = newValue } + get { return _fit } + } + + private var _alignment = Alignment.Center + + open var alignment: Alignment { + set { _alignment = newValue } + get { return _alignment } + } + + var isPlaying: Bool { + get { return !playingAnimations.isEmpty || !playingStateMachines.isEmpty } + } + + open var playback: Playback { + get { return isPlaying ? Playback.play : Playback.pause } + set { + if newValue == Playback.play { + play() + } else { + pause() + } + } + } var riveFile: RiveFile? @@ -34,12 +86,24 @@ public class RiveView: UIView { // Delegates public weak var loopDelegate: LoopDelegate? - - public init(riveFile: RiveFile, fit: Fit = Fit.Contain, alignment: Alignment = Alignment.Center) { + public weak var playDelegate: PlayDelegate? + public weak var pauseDelegate: PauseDelegate? + + public init( + riveFile: RiveFile, + fit: Fit = Fit.Contain, + alignment: Alignment = Alignment.Center, + autoplay: Bool = true, + loopDelegate: LoopDelegate? = nil, + playDelegate: PlayDelegate? = nil, + pauseDelegate: PauseDelegate? = nil + ) { super.init(frame: .zero) - self.configure(withRiveFile: riveFile) - setFit(fit: fit) - setAlignment(alignment: alignment) + self.fit = fit + self.alignment = alignment + self.loopDelegate = loopDelegate + self.playDelegate = playDelegate + self.configure(withRiveFile: riveFile, andAutoPlay: autoplay) } public init() { @@ -49,21 +113,8 @@ public class RiveView: UIView { required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - - - func isPlaying() -> Bool { - return !playingAnimations.isEmpty || !playingStateMachines.isEmpty - } - - open func setFit(fit: Fit){ - self.fit = fit - } - - open func setAlignment(alignment: Alignment) { - self.alignment = alignment - } - + @objc func animationWillMoveToBackground() { print("Triggers when app is moving to background") } @@ -140,21 +191,21 @@ public class RiveView: UIView { // Starts the animation timer func runTimer() { - if (displayLink == nil){ + if displayLink == nil { displayLink = CADisplayLink(target: self, selector: #selector(tick)); // Note: common didnt pause on scroll. displayLink?.add(to: .main, forMode: .common) } - if (displayLink?.isPaused==true){ - lastTime=0 - displayLink!.isPaused=false + if displayLink?.isPaused == true { + lastTime = 0 + displayLink!.isPaused = false } } // Stops the animation timer func stopTimer() { // should we pause or invalidate? - displayLink?.isPaused=true + displayLink?.isPaused = true } func clear() { @@ -163,7 +214,7 @@ public class RiveView: UIView { animations.removeAll() stateMachines.removeAll() stopTimer() - lastTime=0 + lastTime = 0 } public func reset() { @@ -186,7 +237,7 @@ public class RiveView: UIView { let timestamp = displayLink.timestamp // last time needs to be set on the first tick - if (lastTime == 0) { + if lastTime == 0 { lastTime = timestamp } @@ -194,15 +245,19 @@ public class RiveView: UIView { let elapsedTime = timestamp - lastTime; lastTime = timestamp; advance(delta: elapsedTime) - if(!isPlaying()){ + if(!isPlaying){ stopTimer() } } - func advance(delta:Double){ + func advance(delta:Double) { guard let artboard = artboard else { return } + + // Testing firing events here + eventQueue.fireAll() + animations.forEach{ animation in if playingAnimations.contains(animation) { let stillPlaying = animation.advance(by: delta) @@ -252,7 +307,11 @@ public class RiveView: UIView { runTimer() } - + /// Plays the specified animation or state machine with optional loop and directions + /// - Parameter animationName: name of the animation to play + /// - Parameter loop: overrides the animation's loop setting + /// - Parameter direction: overrides the animation's default direction (forwards) + /// - Parameter isStateMachine: true of the name refers to a state machine and not an animation public func play( animationName: String, loop: Loop = Loop.LoopAuto, @@ -268,6 +327,12 @@ public class RiveView: UIView { runTimer() } + /// Pauses all playing animations and state machines + public func pause() { + playingAnimations.forEach { animation in _pause(animation) } + playingStateMachines.forEach { stateMachine in _pause(stateMachine) } + } + private func _getOrCreateStateMachines( animationName: String ) -> [RiveStateMachineInstance]{ @@ -292,7 +357,7 @@ public class RiveView: UIView { if (isStateMachine) { let stateMachineInstances = _getOrCreateStateMachines(animationName:animationName) stateMachineInstances.forEach { stateMachineInstance in - _play(stateMachine: stateMachineInstance) + _play(stateMachineInstance) } } else { let animationInstances = _animations(animationName: animationName) @@ -315,7 +380,6 @@ public class RiveView: UIView { } } - private func _animations(animationName: String)->[RiveLinearAnimationInstance] { return _animations(animationNames:[animationName]) } @@ -359,12 +423,20 @@ public class RiveView: UIView { } playingAnimations.insert(animationInstance) - // notifyPlay(animationInstance) + eventQueue.add( { self.playDelegate?.play(animationInstance.name()) } ) + } + + /// Pauses a playing animation + /// + /// - Parameter animation: the animation to pause + private func _pause(_ animation: RiveLinearAnimationInstance) { + let removed = playingAnimations.remove(animation) + if removed != nil { + eventQueue.add( { self.pauseDelegate?.pause(animation.name()) } ) + } } - private func _play( - stateMachine stateMachineInstance: RiveStateMachineInstance - ) { + private func _play(_ stateMachineInstance: RiveStateMachineInstance) { if (!stateMachines.contains(stateMachineInstance)) { stateMachines.append( stateMachineInstance @@ -372,14 +444,24 @@ public class RiveView: UIView { } playingStateMachines.insert(stateMachineInstance) - + eventQueue.add( { self.playDelegate?.play(stateMachineInstance.name()) } ) + } + + /// Pauses a playing state machine + /// + /// - Parameter stateMachine: the state machine to pause + private func _pause(_ stateMachine: RiveStateMachineInstance) { + let removed = playingStateMachines.remove(stateMachine) + if removed != nil { + eventQueue.add( { self.pauseDelegate?.pause(stateMachine.name()) } ) + } } open func fireState(stateMachineName: String, inputName: String) { let stateMachineInstances = _getOrCreateStateMachines(animationName: stateMachineName) stateMachineInstances.forEach { stateMachine in stateMachine.getTrigger(inputName).fire() - _play(stateMachine: stateMachine) + _play(stateMachine) } runTimer() } @@ -388,7 +470,7 @@ public class RiveView: UIView { let stateMachineInstances = _getOrCreateStateMachines(animationName: stateMachineName) stateMachineInstances.forEach { stateMachine in stateMachine.getBool(inputName).setValue(value) - _play(stateMachine:stateMachine) + _play(stateMachine) } runTimer() } @@ -397,8 +479,29 @@ public class RiveView: UIView { let stateMachineInstances = _getOrCreateStateMachines(animationName: stateMachineName) stateMachineInstances.forEach { stateMachine in stateMachine.getNumber(inputName).setValue(value) - _play(stateMachine:stateMachine) + _play(stateMachine) } runTimer() } + +} + + +// Tracks a queue of events that haven't been fired yet. We do this so +// that we're not calling delegates and modifying state while a view is +// updating (e.g. being initialized, as we autoplay and fire play events +// during the view's init otherwise +class EventQueue { + var events:[() -> Void] = [] + + func add(_ event: @escaping () -> Void) { + events.append(event) + } + + func fireAll() { + events.forEach { event in + event() + } + events.removeAll() + } } From dd37b75bd806483d647236258eff8060a149c308 Mon Sep 17 00:00:00 2001 From: matt Sullivan Date: Mon, 10 May 2021 18:56:10 -0700 Subject: [PATCH 2/2] Moved to controller-style for SwiftUI --- .../Source/SwiftUI/RiveSwiftUIView.swift | 47 +++---- .../Source/SwiftUI/RiveViewWrapper.swift | 131 ++++++++++++------ Source/Views/RiveView.swift | 14 +- 3 files changed, 121 insertions(+), 71 deletions(-) diff --git a/Example-iOS/Source/SwiftUI/RiveSwiftUIView.swift b/Example-iOS/Source/SwiftUI/RiveSwiftUIView.swift index 904887d5..1f30875f 100644 --- a/Example-iOS/Source/SwiftUI/RiveSwiftUIView.swift +++ b/Example-iOS/Source/SwiftUI/RiveSwiftUIView.swift @@ -2,65 +2,62 @@ import SwiftUI import RiveRuntime struct RiveSwiftUIView: View { + @ObservedObject private var riveController: RiveController = RiveController() + var dismiss: () -> Void = {} - @State private var playback: Playback = Playback.play - @State private var fit: Fit = Fit.Cover - @State private var alignment: RiveRuntime.Alignment = RiveRuntime.Alignment.Center @State private var loopCount: Int = 0 var body: some View { ZStack(alignment: .bottomLeading) { UIRiveView( resource: "basketball", - fit: fit, - alignment: alignment, - playback: playback, - loopAction: { name, type in - loopCount += 1 - }, - playAction: { name in - print("Playing \(name)") - }, - pauseAction: { name in - print("Pausing \(name)") - } + controller: riveController +// loopAction: { name, type in +// loopCount += 1 +// }, +// playAction: { name in +// print("Playing \(name)") +// }, +// pauseAction: { name in +// print("Pausing \(name)") +// } ) VStack { Text("Looped \(loopCount) times") .foregroundColor(.blue) .padding() HStack { - Button(action: { alignment = RiveRuntime.Alignment.TopLeft }, + Button(action: { riveController.alignment = RiveRuntime.Alignment.TopLeft }, label: { Text("Top Left") }) Spacer() - Button(action: { alignment = RiveRuntime.Alignment.Center }, + Button(action: { riveController.alignment = RiveRuntime.Alignment.Center }, label: { Text("Center") }) Spacer() - Button(action: { alignment = RiveRuntime.Alignment.BottomRight }, + Button(action: { riveController.alignment = RiveRuntime.Alignment.BottomRight }, label: { Text("Bottom Right") }) } .padding() HStack { - Button(action: { fit = Fit.Contain }, + Button(action: { riveController.fit = Fit.Contain }, label: { Text("Contain") }) Spacer() - Button(action: { fit = Fit.Cover }, + Button(action: { riveController.fit = Fit.Cover }, label: { Text("Cover") }) Spacer() - Button(action: { fit = Fit.Fill }, + Button(action: { riveController.fit = Fit.Fill }, label: { Text("Fill") }) } .padding() HStack { Button(action: { - if playback == Playback.play { - playback = Playback.pause + if riveController.playback == Playback.play { + riveController.pause() } else { - playback = Playback.play + riveController.play() } }, label: { - switch playback { + switch riveController.playback { case .play: Text("Pause") case .pause, .stop: diff --git a/Example-iOS/Source/SwiftUI/RiveViewWrapper.swift b/Example-iOS/Source/SwiftUI/RiveViewWrapper.swift index 453391f3..45890495 100644 --- a/Example-iOS/Source/SwiftUI/RiveViewWrapper.swift +++ b/Example-iOS/Source/SwiftUI/RiveViewWrapper.swift @@ -1,80 +1,123 @@ import SwiftUI +import Combine import RiveRuntime +/// Controller manages the state of the Rive animation +class RiveController: ObservableObject { + @Published var fit: Fit + @Published var alignment: RiveRuntime.Alignment + @Published var playback = Playback.play + + init( + fit: Fit = Fit.Contain, + alignment: RiveRuntime.Alignment = RiveRuntime.Alignment.Center, + autoplay: Bool = false + ) { + self.fit = fit + self.alignment = alignment + self.playback = autoplay ? Playback.play : Playback.stop + } + + /// Play animations + func play() { + self.playback = Playback.play + } + + /// Pause all animations and state machines + func pause() { + self.playback = Playback.pause + } +} + + struct UIRiveView: UIViewRepresentable { + + // MARK: - Properties + let resource: String - var fit: Fit = Fit.Contain - var alignment: RiveRuntime.Alignment = RiveRuntime.Alignment.Center - var playback: Playback = Playback.play + @ObservedObject var controller: RiveController // Delegate handlers for loop and play events var loopAction: LoopAction = nil var playAction: PlaybackAction = nil var pauseAction: PlaybackAction = nil + // MARK: - UIViewRepresentable + /// Constructs the view func makeUIView(context: Context) -> RiveView { let riveView = RiveView( riveFile: getRiveFile(resourceName: resource), - fit: fit, - alignment: alignment, - autoplay: playback == Playback.play, + fit: controller.fit, + alignment: controller.alignment, + autoplay: controller.playback == Playback.play, loopDelegate: context.coordinator, playDelegate: context.coordinator, pauseDelegate: context.coordinator ) + + // Try out target-action? + return riveView } - /// Called when a bound variable changes state - func updateUIView(_ uiView: RiveView, context: Context) { - print("updateUI") - uiView.fit = fit - uiView.alignment = alignment - uiView.playback = playback + /// Called when the view model changes + func updateUIView(_ uiView: RiveView, context: UIViewRepresentableContext) { + uiView.fit = controller.fit + uiView.alignment = controller.alignment + uiView.playback = controller.playback } // Constructs a coordinator for managing updating state func makeCoordinator() -> Coordinator { // Coordinator(loopCount: $loopCount) - Coordinator(loopAction: loopAction, playAction: playAction, pauseAction: pauseAction) + Coordinator(controller: controller, loopAction: loopAction, playAction: playAction, pauseAction: pauseAction) } - } -// Coordinator between RiveView and UIRiveView -class Coordinator: NSObject, LoopDelegate, PlayDelegate, PauseDelegate { - - private var loopAction: LoopAction - private var playAction: PlaybackAction - private var pauseAction: PlaybackAction +extension UIRiveView { + // MARK: - Coordinator - init(loopAction: LoopAction, playAction: PlaybackAction, pauseAction: PlaybackAction) { - self.loopAction = loopAction - self.playAction = playAction - self.pauseAction = pauseAction - } - - func loop(_ animationName: String, type: Int) { - loopAction?(animationName, type) + // Coordinator between RiveView and UIRiveView + class Coordinator: NSObject, LoopDelegate, PlayDelegate, PauseDelegate { + + private var controller: RiveController + private var loopAction: LoopAction + private var playAction: PlaybackAction + private var pauseAction: PlaybackAction + var subscribers: [AnyCancellable] = [] + + init(controller: RiveController, loopAction: LoopAction, playAction: PlaybackAction, pauseAction: PlaybackAction) { + self.loopAction = loopAction + self.playAction = playAction + self.pauseAction = pauseAction + self.controller = controller + + // This stuff is all experimental and may get removed + let fitSubscription = controller.$fit.receive(on: RunLoop.main).sink(receiveValue: fitDidChange) + subscribers.append(fitSubscription) + } + + // Cancel subscribers when Coordinator is deinitialized + deinit { + subscribers.forEach { $0.cancel() } } + + var fitDidChange: (Fit) -> Void = { fit in + print("Fit changed to \(fit)") + } + + func loop(_ animationName: String, type: Int) { + loopAction?(animationName, type) + } - func play(_ animationName: String) { - playAction?(animationName) - } - - func pause(_ animationName: String) { - pauseAction?(animationName) + func play(_ animationName: String) { + playAction?(animationName) + } + + func pause(_ animationName: String) { + pauseAction?(animationName) + } } - -// @Binding private var loopCount: Int -// -// init(loopCount: Binding) { -// self._loopCount = loopCount -// } -// -// func loop(_ animationName: String, type: Int) { -// loopCount += 1 -// } } diff --git a/Source/Views/RiveView.swift b/Source/Views/RiveView.swift index a90c002a..8c9a9199 100644 --- a/Source/Views/RiveView.swift +++ b/Source/Views/RiveView.swift @@ -46,14 +46,24 @@ public class RiveView: UIView { private var _fit = Fit.Contain open var fit: Fit { - set { _fit = newValue } + set { + _fit = newValue + // Advance the artboard if there's one so that Rive redraws with the new fit + // TODO: this does nothing when animations are paused as they're skipped for drawing + artboard?.advance(by: 0) + } get { return _fit } } private var _alignment = Alignment.Center open var alignment: Alignment { - set { _alignment = newValue } + set { + _alignment = newValue + // Advance the artboard if there's one so that Rive redraws with the new alignment + // TODO: this does nothing when animations are paused as they're skipped for drawing + artboard?.advance(by: 0) + } get { return _alignment } }