Skip to content

Commit

Permalink
Merge pull request #28 from rive-app/fixes_5_10
Browse files Browse the repository at this point in the history
Hacked in play/pause to SwiftUI
  • Loading branch information
mjtalbot authored May 11, 2021
2 parents 17426e9 + dd37b75 commit 1d6a5a1
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 98 deletions.
50 changes: 34 additions & 16 deletions Example-iOS/Source/SwiftUI/RiveSwiftUIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,69 @@ import SwiftUI
import RiveRuntime

struct RiveSwiftUIView: View {
@ObservedObject private var riveController: RiveController = RiveController()

var dismiss: () -> Void = {}
@State private var isPlaying: Bool = true
@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,
loopAction: { name, type in
loopCount += 1
}
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: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/,
label: { Text(isPlaying ? "Pause" : "Play") })
Button(action: {
if riveController.playback == Playback.play {
riveController.pause()
} else {
riveController.play()
}
},
label: {
switch riveController.playback {
case .play:
Text("Pause")
case .pause, .stop:
Text("Play")
}
})

Spacer()
Button(action: dismiss, label: {
Text("Dismiss")
Expand Down
131 changes: 97 additions & 34 deletions Example-iOS/Source/SwiftUI/RiveViewWrapper.swift
Original file line number Diff line number Diff line change
@@ -1,60 +1,123 @@
import SwiftUI
import Combine
import RiveRuntime

typealias LoopAction = ((String, Int) -> Void)?
/// 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
@ObservedObject var controller: RiveController

// Delegate handlers for loop and play events
var loopAction: LoopAction = nil
var playAction: PlaybackAction = nil
var pauseAction: PlaybackAction = nil

// Constructs the view
// MARK: - UIViewRepresentable

/// Constructs the view
func makeUIView(context: Context) -> RiveView {
let riveView = RiveView(riveFile: getRiveFile(resourceName: resource))
riveView.setFit(fit: fit)
riveView.setAlignment(alignment: alignment)
let riveView = RiveView(
riveFile: getRiveFile(resourceName: resource),
fit: controller.fit,
alignment: controller.alignment,
autoplay: controller.playback == Playback.play,
loopDelegate: context.coordinator,
playDelegate: context.coordinator,
pauseDelegate: context.coordinator
)

// Set the delegates
riveView.loopDelegate = context.coordinator
// Try out target-action?

return riveView
}

// Called when a bound variable changes state
func updateUIView(_ uiView: RiveView, context: Context) {
print("updateUI")
uiView.setFit(fit: fit)
uiView.setAlignment(alignment: alignment)
/// Called when the view model changes
func updateUIView(_ uiView: RiveView, context: UIViewRepresentableContext<UIRiveView>) {
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)
Coordinator(controller: controller, loopAction: loopAction, playAction: playAction, pauseAction: pauseAction)
}

}

// Coordinator between RiveView and UIRiveView
class Coordinator: NSObject, LoopDelegate {
private var loopAction: LoopAction
extension UIRiveView {

init(loopAction: LoopAction) {
self.loopAction = loopAction
}
// MARK: - Coordinator

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)
}

// @Binding private var loopCount: Int
//
// init(loopCount: Binding<Int>) {
// self._loopCount = loopCount
// }
//
// func loop(_ animationName: String, type: Int) {
// loopCount += 1
// }

// 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)
}
}
}
4 changes: 2 additions & 2 deletions Example-iOS/Source/UIkit/Layout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class LayoutViewController: UIViewController {
default:
fit = Fit.Contain
}
layoutView.riveView.setFit(fit:fit)
layoutView.riveView.fit = fit
}

func setAlignmnet(name:String){
Expand All @@ -85,7 +85,7 @@ class LayoutViewController: UIViewController {
default:
alignment = Alignment.Center
}
layoutView.riveView.setAlignment(alignment:alignment)
layoutView.riveView.alignment = alignment
}

layoutView.fitButtonAction = setFit
Expand Down
1 change: 1 addition & 0 deletions Source/Renderer/Rive.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
17 changes: 12 additions & 5 deletions Source/Renderer/Rive.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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

/*
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -485,24 +485,26 @@ - (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 {
return nil;
}
}

-(void) applyTo:(RiveArtboard*) artboard {
- (void) applyTo:(RiveArtboard*)artboard {
instance->apply(artboard.artboard);
}

-(bool) advanceBy:(double)elapsedSeconds {
- (bool) advanceBy:(double)elapsedSeconds {
return instance->advance(elapsedSeconds);
}

Expand Down Expand Up @@ -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

/*
Expand Down
Loading

0 comments on commit 1d6a5a1

Please sign in to comment.