Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hacked in play/pause to SwiftUI #28

Merged
merged 2 commits into from
May 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love it

}

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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:o does the instance even have a 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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohh :P yeah there's a question of names like this :P should we prefix everything with rive, or leave that as implied...

@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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what formatter do you use? my xcode doesnt do shit for formatting, is there something we can use here?

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();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh!

return [NSString stringWithCString:str.c_str() encoding:[NSString defaultCStringEncoding]];
}

@end

/*
Expand Down
Loading