Skip to content

Commit

Permalink
Add new state machine delegate callback for touch event hit results
Browse files Browse the repository at this point in the history
This pull request adds a new delegate function that can be used to perform (UI) updates based on the result of a touch or a mouse event. This is an additive change that will not break existing behavior.

An example of where this could be used:
- In a collection view, where the animation should not prevent scrolling from occurring if a non-interactive portion of the animation is touched.
  - By utilizing the new callback, a developer can check for a hit when a touch began, and update the appropriate collection view properties.

- Add support for:
  - [x] iOS
  - [x] macOS

Diffs=
fe60de3c3 Add new state machine delegate callback for touch event hit results (#7579)

Co-authored-by: David Skuza <[email protected]>
  • Loading branch information
dskuza and dskuza committed Jul 16, 2024
1 parent 3799ddc commit cb45051
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
63b6a13d3a2adc1c555747fcd76234360bad7a1d
fe60de3c316eda974dab3e00522e9ee7a135834d
41 changes: 33 additions & 8 deletions Source/Renderer/RiveStateMachineInstance.mm
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,27 @@

static int smInstanceCount = 0;

/// Returns a RiveHitResult value, converting from the Rive C++ type.
RiveHitResult RiveHitResultFromRuntime(rive::HitResult result)
{
if (result == rive::HitResult::none)
{
return none;
}
else if (result == rive::HitResult::hit)
{
return hit;
}
else if (result == rive::HitResult::hitOpaque)
{
return hitOpaque;
}
else
{
return none;
}
}

// MARK: - RiveStateMachineInstance

@interface RiveStateMachineInstance ()
Expand Down Expand Up @@ -360,24 +381,28 @@ - (NSInteger)layerCount

// MARK: Touch

- (void)touchBeganAtLocation:(CGPoint)touchLocation
- (RiveHitResult)touchBeganAtLocation:(CGPoint)touchLocation
{
instance->pointerDown(rive::Vec2D(touchLocation.x, touchLocation.y));
return RiveHitResultFromRuntime(
instance->pointerDown(rive::Vec2D(touchLocation.x, touchLocation.y)));
}

- (void)touchMovedAtLocation:(CGPoint)touchLocation
- (RiveHitResult)touchMovedAtLocation:(CGPoint)touchLocation
{
instance->pointerMove(rive::Vec2D(touchLocation.x, touchLocation.y));
return RiveHitResultFromRuntime(
instance->pointerMove(rive::Vec2D(touchLocation.x, touchLocation.y)));
}

- (void)touchEndedAtLocation:(CGPoint)touchLocation
- (RiveHitResult)touchEndedAtLocation:(CGPoint)touchLocation
{
instance->pointerUp(rive::Vec2D(touchLocation.x, touchLocation.y));
return RiveHitResultFromRuntime(
instance->pointerUp(rive::Vec2D(touchLocation.x, touchLocation.y)));
}

- (void)touchCancelledAtLocation:(CGPoint)touchLocation
- (RiveHitResult)touchCancelledAtLocation:(CGPoint)touchLocation
{
instance->pointerUp(rive::Vec2D(touchLocation.x, touchLocation.y));
return RiveHitResultFromRuntime(
instance->pointerUp(rive::Vec2D(touchLocation.x, touchLocation.y)));
}

@end
15 changes: 11 additions & 4 deletions Source/Renderer/include/RiveStateMachineInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ NS_ASSUME_NONNULL_BEGIN
@class RiveLayerState;
@class RiveEvent;

/// A type mirroring rive::HitResult, but available in both ObjC and Swift.
typedef NS_ENUM(NSInteger, RiveHitResult) { none, hit, hitOpaque };

/*
* RiveStateMachineInstance
*/
Expand All @@ -47,19 +50,23 @@ NS_ASSUME_NONNULL_BEGIN

/// Tells this StateMachineInstance that a user began touching the artboard
/// @param touchLocation A CGPoint in the coordinate space of the animating artboard
- (void)touchBeganAtLocation:(CGPoint)touchLocation;
/// @return The RiveHitResult of a touch beginning at the provided location.
- (RiveHitResult)touchBeganAtLocation:(CGPoint)touchLocation;

/// Tells this StateMachineInstance that a touch moved on the artboard
/// @param touchLocation A CGPoint in the coordinate space of the animating artboard
- (void)touchMovedAtLocation:(CGPoint)touchLocation;
/// @return The RiveHitResult of a touch moving at the provided location.
- (RiveHitResult)touchMovedAtLocation:(CGPoint)touchLocation;

/// Tells this StateMachineInstance that a user finished touching the artboard
/// @param touchLocation A CGPoint in the coordinate space of the animating artboard
- (void)touchEndedAtLocation:(CGPoint)touchLocation;
/// @return The RiveHitResult of a touch ending at the provided location.
- (RiveHitResult)touchEndedAtLocation:(CGPoint)touchLocation;

/// Tells this StateMachineInstance that a user cancelled touching the artboard
/// @param touchLocation A CGPoint in the coordinate space of the animating artboard
- (void)touchCancelledAtLocation:(CGPoint)touchLocation;
/// @return The RiveHitResult of a touch being cancelled at the provided location.
- (RiveHitResult)touchCancelledAtLocation:(CGPoint)touchLocation;

// MARK: Debug

Expand Down
59 changes: 49 additions & 10 deletions Source/RiveView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import Foundation


open class RiveView: RiveRendererView {
// MARK: Configuration
internal weak var riveModel: RiveModel?
Expand Down Expand Up @@ -264,25 +263,37 @@ open class RiveView: RiveRendererView {
#if os(iOS)
open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
handleTouch(touches.first!, delegate: stateMachineDelegate?.touchBegan) {
$0.touchBegan(atLocation: $1)
let result = $0.touchBegan(atLocation: $1)
if let stateMachine = riveModel?.stateMachine {
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .began)
}
}
}

open override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
handleTouch(touches.first!, delegate: stateMachineDelegate?.touchMoved) {
$0.touchMoved(atLocation: $1)
let result = $0.touchMoved(atLocation: $1)
if let stateMachine = riveModel?.stateMachine {
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .moved)
}
}
}

open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
handleTouch(touches.first!, delegate: stateMachineDelegate?.touchEnded) {
$0.touchEnded(atLocation: $1)
let result = $0.touchEnded(atLocation: $1)
if let stateMachine = riveModel?.stateMachine {
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .ended)
}
}
}

open override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
handleTouch(touches.first!, delegate: stateMachineDelegate?.touchCancelled) {
$0.touchCancelled(atLocation: $1)
let result = $0.touchCancelled(atLocation: $1)
if let stateMachine = riveModel?.stateMachine {
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .cancelled)
}
}
}

Expand Down Expand Up @@ -319,31 +330,46 @@ open class RiveView: RiveRendererView {
#else
open override func mouseDown(with event: NSEvent) {
handleTouch(event, delegate: stateMachineDelegate?.touchBegan) {
$0.touchBegan(atLocation: $1)
let result = $0.touchBegan(atLocation: $1)
if let stateMachine = riveModel?.stateMachine {
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .began)
}
}
}

open override func mouseMoved(with event: NSEvent) {
handleTouch(event, delegate: stateMachineDelegate?.touchMoved) {
$0.touchMoved(atLocation: $1)
let result = $0.touchMoved(atLocation: $1)
if let stateMachine = riveModel?.stateMachine {
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .moved)
}
}
}

open override func mouseDragged(with event: NSEvent) {
handleTouch(event, delegate: stateMachineDelegate?.touchMoved) {
$0.touchMoved(atLocation: $1)
let result = $0.touchMoved(atLocation: $1)
if let stateMachine = riveModel?.stateMachine {
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .moved)
}
}
}

open override func mouseUp(with event: NSEvent) {
handleTouch(event, delegate: stateMachineDelegate?.touchEnded) {
$0.touchEnded(atLocation: $1)
let result = $0.touchEnded(atLocation: $1)
if let stateMachine = riveModel?.stateMachine {
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .ended)
}
}
}

open override func mouseExited(with event: NSEvent) {
handleTouch(event, delegate: stateMachineDelegate?.touchCancelled) {
$0.touchCancelled(atLocation: $1)
let result = $0.touchCancelled(atLocation: $1)
if let stateMachine = riveModel?.stateMachine {
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .cancelled)
}
}
}

Expand Down Expand Up @@ -414,6 +440,18 @@ open class RiveView: RiveRendererView {
}
}

/// An enum of possible touch or mouse events when interacting with an animation.
@objc public enum RiveTouchEvent: Int {
/// The touch event that occurs when a touch or mouse button click occurs.
case began
/// The touch event that occurs when a touch or mouse is dragged.
case moved
/// The touch event that occurs when a touch or mouse button is lifted.
case ended
/// The touch event that occurs when a touch or mouse click is cancelled.
case cancelled
}

@objc public protocol RiveStateMachineDelegate: AnyObject {
@objc optional func touchBegan(onArtboard artboard: RiveArtboard?, atLocation location: CGPoint)
@objc optional func touchMoved(onArtboard artboard: RiveArtboard?, atLocation location: CGPoint)
Expand All @@ -422,6 +460,7 @@ open class RiveView: RiveRendererView {

@objc optional func stateMachine(_ stateMachine: RiveStateMachineInstance, receivedInput input: StateMachineInput)
@objc optional func stateMachine(_ stateMachine: RiveStateMachineInstance, didChangeState stateName: String)
@objc optional func stateMachine(_ stateMachine: RiveStateMachineInstance, didReceiveHitResult hitResult: RiveHitResult, from event: RiveTouchEvent)
@objc optional func onRiveEventReceived(onRiveEvent riveEvent: RiveEvent)
}

Expand Down

0 comments on commit cb45051

Please sign in to comment.