Changed the RiveViewModel's view() method to return a type erased AnyView to allow subclasses to override it. This is because currently in Swift opaque result types cannot be used for a non-final declaration within a class. I also removed some throws on methods in RiveViewModel to make the highest level of our API cleaner.

This commit is contained in:
Zachary Duncan
2022-05-31 16:38:19 -04:00
committed by Zachary Duncan
parent 139ae4462f
commit 921298ef35
8 changed files with 79 additions and 70 deletions

View File

@@ -37,7 +37,7 @@ struct SwiftTouchEvents: DismissableView {
bearGuy.view()
.aspectRatio(1, contentMode: .fit)
clock.controlsView()
clock.view()
toggle.view()
.aspectRatio(1, contentMode: .fit)

View File

@@ -47,7 +47,7 @@ struct SwiftWidgets: DismissableView {
VStack {
Text("RiveProgressBar:")
rprogress.formattedView()
rprogress.view()
Slider(value: Binding(
get: {

View File

@@ -11,10 +11,13 @@ import RiveRuntime
class ClockViewModel: RiveViewModel {
private var timer: Timer!
private var hour: Int = 0
private var minute: Int = 0
private var second: Int = 0
@Published var hours: Double = 0 {
@Published var time: Double = 0 {
didSet {
try? setInput("isTime", value: hours > 12 ? hours-12 : hours)
setInput("isTime", value: time > 12 ? time-12 : time)
}
}
@@ -25,11 +28,11 @@ class ClockViewModel: RiveViewModel {
let date = Date()
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date)
let minute = calendar.component(.minute, from: date)
let second = calendar.component(.second, from: date)
self.hour = calendar.component(.hour, from: date)
self.minute = calendar.component(.minute, from: date)
self.second = calendar.component(.second, from: date)
self.hours = Double(hour) + Double(minute)/60 + Double(second)/1200
self.time = Double(self.hour) + Double(self.minute)/60 + Double(self.second)/1200
}
} else {
timer?.invalidate()
@@ -46,39 +49,42 @@ class ClockViewModel: RiveViewModel {
timer?.invalidate()
}
func controlsView() -> some View {
return ZStack {
Color.gray
VStack {
view()
.aspectRatio(1, contentMode: .fit)
override func view() -> AnyView {
AnyView(
ZStack {
Color.gray
Button {
self.followTimer.toggle()
} label: {
ZStack {
Color.blue
Text(self.followTimer ? "Real Time" : "Manual")
.bold()
VStack {
super.view()
.aspectRatio(1, contentMode: .fit)
Button {
self.followTimer.toggle()
} label: {
ZStack {
Color.blue
Text(self.followTimer ? "Real Time" : "Manual")
.bold()
}
}
}
.foregroundColor(.white)
.frame(width: 200, height: 75, alignment: .center)
.cornerRadius(10)
.padding()
Text("Hour: \(round(hours * 100) / 100)")
.foregroundColor(.white)
.padding(.bottom)
Slider(value: Binding(
get: { self.hours },
set: { self.hours = round($0 * 100) / 100 }
), in: 0...24, step: 0.01)
.padding()
.disabled(followTimer)
.frame(width: 200, height: 75, alignment: .center)
.cornerRadius(10)
.padding()
let normalizedHour = hour%12 == 0 ? 12 : hour%12
Text("Time: \(normalizedHour):\(minute):\(second)")
.foregroundColor(.white)
.padding(.bottom)
Slider(value: Binding(
get: { self.time },
set: { self.time = round($0 * 100) / 100 }
), in: 0...24, step: 0.01)
.padding()
.disabled(followTimer)
}
}
}
)
}
}

View File

@@ -25,7 +25,7 @@ class RiveButton: RiveViewModel {
func touchBegan(onArtboard artboard: RiveArtboard?, atLocation location: CGPoint) {
stop()
try? setInput(input, value: true)
setInput(input, value: true)
}
func touchEnded(onArtboard artboard: RiveArtboard?, atLocation location: CGPoint) {
@@ -35,6 +35,6 @@ class RiveButton: RiveViewModel {
}
func touchCancelled(onArtboard artboard: RiveArtboard?, atLocation location: CGPoint) {
try? setInput(input, value: false)
setInput(input, value: false)
}
}

View File

@@ -12,7 +12,7 @@ import SwiftUI
class RiveProgressBar: RiveViewModel {
var progress: Double {
didSet {
try? setInput("Energy", value: progress)
setInput("Energy", value: progress)
}
}
@@ -21,8 +21,10 @@ class RiveProgressBar: RiveViewModel {
super.init(fileName: "energy_bar_example", stateMachineName: "State Machine ", fit: .fitCover)
}
func formattedView() -> some View {
super.view()
.aspectRatio(4, contentMode: .fill)
override func view() -> AnyView {
AnyView(
super.view()
.aspectRatio(4, contentMode: .fill)
)
}
}

View File

@@ -12,7 +12,7 @@ import SwiftUI
class RiveSlider: RiveViewModel {
var progress: Double {
didSet {
try? setInput("FillPercent", value: progress)
setInput("FillPercent", value: progress)
}
}

View File

@@ -84,7 +84,7 @@ extension ExamplesMasterTableViewController {
// Views made by the ViewModels
else if indexPath.section == 2 {
let anyView = AnyView(viewModels[indexPath.row].1.view())
let anyView = viewModels[indexPath.row].1.view()
controller = UIHostingController(rootView: anyView)
}

View File

@@ -231,30 +231,44 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
)
}
open func triggerInput(_ inputName: String) throws {
/// Provide the active StateMachine a `Trigger` input
/// - Parameter inputName: The name of a `Trigger` input on the active StateMachine
open func triggerInput(_ inputName: String) {
riveModel?.stateMachine?.getTrigger(inputName).fire()
play()
}
open func setInput(_ inputName: String, value: Bool) throws {
/// Provide the active StateMachine a `Boolean` input
/// - Parameters:
/// - inputName: The name of a `Boolean` input on the active StateMachine
/// - value: A Bool value for the input
open func setInput(_ inputName: String, value: Bool) {
riveModel?.stateMachine?.getBool(inputName).setValue(value)
play()
}
open func setInput(_ inputName: String, value: Float) throws {
/// Provide the active StateMachine a `Number` input
/// - Parameters:
/// - inputName: The name of a `Number` input on the active StateMachine
/// - value: A Float value for the input
open func setInput(_ inputName: String, value: Float) {
riveModel?.stateMachine?.getNumber(inputName).setValue(value)
play()
}
open func setInput(_ inputName: String, value: Double) throws {
riveModel?.stateMachine?.getNumber(inputName).setValue(Float(value))
play()
/// Provide the active StateMachine a `Number` input
/// - Parameters:
/// - inputName: The name of a `Number` input on the active StateMachine
/// - value: A Double value for the input
open func setInput(_ inputName: String, value: Double) {
setInput(inputName, value: Float(value))
}
// 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`.
/// replace any previous `RiveView`. This is called when first drawing a `RiveViewRepresentable`.
/// - Returns: Reference to the new view that the `RiveViewModel` will be maintaining
open func createRiveView() -> RiveView {
let view: RiveView
@@ -271,7 +285,7 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
}
/// Gives updated layout values to the provided `RiveView`. This is called in
/// the process of re-displaying `StandardView`.
/// the process of re-displaying `RiveViewRepresentable`.
/// - Parameter rview: the `RiveView` that will be updated
@objc open func update(view: RiveView) {
view.fit = fit
@@ -279,7 +293,7 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
}
/// Assigns the provided `RiveView` to its rview property. This is called when creating a
/// `StandardView`
/// `RiveViewRepresentable`
///
/// - Parameter view: the `Rview` that this `RiveViewModel` will maintain
fileprivate func registerView(_ view: RiveView) {
@@ -294,21 +308,8 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
}
/// This can be added to the body of a SwiftUI `View`
open func view() -> some View {
return StandardView(viewModel: self)
}
/// A simple View designed to display
public struct StandardView: View {
let viewModel: RiveViewModel
init(viewModel: RiveViewModel) {
self.viewModel = viewModel
}
public var body: some View {
RiveViewRepresentable(viewModel: viewModel)
}
open func view() -> AnyView {
return AnyView(RiveViewRepresentable(viewModel: self))
}
// MARK: - UIKit Helper