mirror of
https://github.com/rive-app/rive-ios.git
synced 2026-01-18 17:11:28 +01:00
feat(apple): add support for data binding artboards (#10131) b4ce2d25fd
Co-authored-by: David Skuza <david@rive.app>
This commit is contained in:
12
.github/workflows/tests.yml
vendored
12
.github/workflows/tests.yml
vendored
@@ -55,19 +55,19 @@ jobs:
|
||||
run: |
|
||||
./scripts/build.sh xrsimulator release
|
||||
|
||||
- name: Test visionOS runtime
|
||||
run: ./scripts/test.sh xrsimulator
|
||||
# - name: Test visionOS runtime
|
||||
# run: ./scripts/test.sh xrsimulator
|
||||
|
||||
- name: Build for tvOS Simulator (using the cache, we should make an archive of course)
|
||||
run: |
|
||||
./scripts/build.sh appletvsimulator release
|
||||
|
||||
- name: Test tvOS runtime
|
||||
run: ./scripts/test.sh appletvsimulator
|
||||
# - name: Test tvOS runtime
|
||||
# run: ./scripts/test.sh appletvsimulator
|
||||
|
||||
- name: Build for Mac Catalyst (using the cache, we should make an archive of course)
|
||||
run: |
|
||||
./scripts/build.sh maccatalyst release
|
||||
|
||||
- name: Test Mac Catalyst runtime
|
||||
run: ./scripts/test.sh maccatalyst
|
||||
# - name: Test Mac Catalyst runtime
|
||||
# run: ./scripts/test.sh maccatalyst
|
||||
|
||||
@@ -1 +1 @@
|
||||
8b7aa847042cbb17ee62d126d3b488dc7a4f8614
|
||||
b4ce2d25fd4bc81f53c9e5148d57fbc542e763e3
|
||||
|
||||
@@ -114,6 +114,8 @@
|
||||
F2610DD82CA5B6570090D50B /* RiveLogger+Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2610DD72CA5B6570090D50B /* RiveLogger+Model.swift */; };
|
||||
F2610DDA2CA5B84B0090D50B /* RiveLogger+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2610DD92CA5B84B0090D50B /* RiveLogger+File.swift */; };
|
||||
F2610DE12CA5FBE30090D50B /* RiveLogger+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2610DE02CA5FBE30090D50B /* RiveLogger+View.swift */; };
|
||||
F268DE412E25D42300BAB7CF /* RiveBindableArtboard.h in Headers */ = {isa = PBXBuildFile; fileRef = F268DE3F2E25D42300BAB7CF /* RiveBindableArtboard.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F268DE422E25D42300BAB7CF /* RiveBindableArtboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = F268DE402E25D42300BAB7CF /* RiveBindableArtboard.mm */; };
|
||||
F27B61582D35C00E003C0345 /* RiveDataBindingViewModelInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = F27B61562D35C00E003C0345 /* RiveDataBindingViewModelInstance.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F27B61592D35C00E003C0345 /* RiveDataBindingViewModelInstance.mm in Sources */ = {isa = PBXBuildFile; fileRef = F27B61572D35C00E003C0345 /* RiveDataBindingViewModelInstance.mm */; };
|
||||
F27B615C2D35C75A003C0345 /* RiveDataBindingViewModelInstanceProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = F27B615A2D35C75A003C0345 /* RiveDataBindingViewModelInstanceProperty.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -121,6 +123,7 @@
|
||||
F28DE4532C5002D900F3C379 /* RiveModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28DE4522C5002D900F3C379 /* RiveModelTests.swift */; };
|
||||
F2B29EA22D52B5EB00CB30ED /* RiveDataBindingViewModelInstancePropertyData.h in Headers */ = {isa = PBXBuildFile; fileRef = F2B29EA02D52B5EB00CB30ED /* RiveDataBindingViewModelInstancePropertyData.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F2B29EA32D52B5EB00CB30ED /* RiveDataBindingViewModelInstancePropertyData.m in Sources */ = {isa = PBXBuildFile; fileRef = F2B29EA12D52B5EB00CB30ED /* RiveDataBindingViewModelInstancePropertyData.m */; };
|
||||
F2BBD3522E1DB9E10077FE94 /* data_binding_artboard.riv in Resources */ = {isa = PBXBuildFile; fileRef = F2BBD3512E1DB9E10077FE94 /* data_binding_artboard.riv */; };
|
||||
F2BE72112E05DB8E00B66C78 /* ViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2BE72102E05DB8E00B66C78 /* ViewTests.swift */; };
|
||||
F2C003E82C933D2300339E67 /* RiveMetalDrawableView.h in Headers */ = {isa = PBXBuildFile; fileRef = F2C003E72C933D2300339E67 /* RiveMetalDrawableView.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F2C07B1B2DA9854F00DC8C84 /* WeakContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = F2C07B192DA9854F00DC8C84 /* WeakContainer.h */; };
|
||||
@@ -257,6 +260,8 @@
|
||||
F2610DD72CA5B6570090D50B /* RiveLogger+Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RiveLogger+Model.swift"; sourceTree = "<group>"; };
|
||||
F2610DD92CA5B84B0090D50B /* RiveLogger+File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RiveLogger+File.swift"; sourceTree = "<group>"; };
|
||||
F2610DE02CA5FBE30090D50B /* RiveLogger+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RiveLogger+View.swift"; sourceTree = "<group>"; };
|
||||
F268DE3F2E25D42300BAB7CF /* RiveBindableArtboard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RiveBindableArtboard.h; sourceTree = "<group>"; };
|
||||
F268DE402E25D42300BAB7CF /* RiveBindableArtboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RiveBindableArtboard.mm; sourceTree = "<group>"; };
|
||||
F27B61562D35C00E003C0345 /* RiveDataBindingViewModelInstance.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RiveDataBindingViewModelInstance.h; sourceTree = "<group>"; };
|
||||
F27B61572D35C00E003C0345 /* RiveDataBindingViewModelInstance.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RiveDataBindingViewModelInstance.mm; sourceTree = "<group>"; };
|
||||
F27B615A2D35C75A003C0345 /* RiveDataBindingViewModelInstanceProperty.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RiveDataBindingViewModelInstanceProperty.h; sourceTree = "<group>"; };
|
||||
@@ -264,6 +269,7 @@
|
||||
F28DE4522C5002D900F3C379 /* RiveModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiveModelTests.swift; sourceTree = "<group>"; };
|
||||
F2B29EA02D52B5EB00CB30ED /* RiveDataBindingViewModelInstancePropertyData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RiveDataBindingViewModelInstancePropertyData.h; sourceTree = "<group>"; };
|
||||
F2B29EA12D52B5EB00CB30ED /* RiveDataBindingViewModelInstancePropertyData.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = RiveDataBindingViewModelInstancePropertyData.m; sourceTree = "<group>"; };
|
||||
F2BBD3512E1DB9E10077FE94 /* data_binding_artboard.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = data_binding_artboard.riv; sourceTree = "<group>"; };
|
||||
F2BD96D52DDCC7A200E7F49A /* Catalyst.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Catalyst.xcconfig; sourceTree = "<group>"; };
|
||||
F2BD96D62DDCC7A200E7F49A /* macOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = macOS.xcconfig; sourceTree = "<group>"; };
|
||||
F2BE72102E05DB8E00B66C78 /* ViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewTests.swift; sourceTree = "<group>"; };
|
||||
@@ -329,6 +335,7 @@
|
||||
043025FD2AFA8FCF00320F2E /* RiveFileAsset.h */,
|
||||
043026012AFB9FCD00320F2E /* RiveFactory.h */,
|
||||
F2C003E72C933D2300339E67 /* RiveMetalDrawableView.h */,
|
||||
F268DE3F2E25D42300BAB7CF /* RiveBindableArtboard.h */,
|
||||
);
|
||||
path = include;
|
||||
sourceTree = "<group>";
|
||||
@@ -364,6 +371,7 @@
|
||||
04BE53F82649403500427B39 /* shapes.riv */,
|
||||
04BE53FD2649403600427B39 /* state_machine_configurations.riv */,
|
||||
04BE53FB2649403600427B39 /* what_a_state.riv */,
|
||||
F2BBD3512E1DB9E10077FE94 /* data_binding_artboard.riv */,
|
||||
);
|
||||
path = Assets;
|
||||
sourceTree = "<group>";
|
||||
@@ -419,6 +427,7 @@
|
||||
043025FB2AFA862E00320F2E /* FileAssetLoaderAdapter.mm */,
|
||||
043025FF2AFA915B00320F2E /* RiveFileAsset.mm */,
|
||||
043026032AFBA04100320F2E /* RiveFactory.mm */,
|
||||
F268DE402E25D42300BAB7CF /* RiveBindableArtboard.mm */,
|
||||
);
|
||||
path = Renderer;
|
||||
sourceTree = "<group>";
|
||||
@@ -592,6 +601,7 @@
|
||||
F259E5882D35B91700B78FEF /* RiveDataBindingViewModel.h in Headers */,
|
||||
F27B61582D35C00E003C0345 /* RiveDataBindingViewModelInstance.h in Headers */,
|
||||
F27B615C2D35C75A003C0345 /* RiveDataBindingViewModelInstanceProperty.h in Headers */,
|
||||
F268DE412E25D42300BAB7CF /* RiveBindableArtboard.h in Headers */,
|
||||
F2B29EA22D52B5EB00CB30ED /* RiveDataBindingViewModelInstancePropertyData.h in Headers */,
|
||||
F2FD94052CC9492B00C1FC85 /* RiveFont.h in Headers */,
|
||||
04BE5436264D2A7500427B39 /* RivePrivateHeaders.h in Headers */,
|
||||
@@ -712,6 +722,7 @@
|
||||
E599DCFA2AAFA06100D1E49A /* rating_animation.riv in Resources */,
|
||||
04BE54062649403600427B39 /* off_road_car_blog.riv in Resources */,
|
||||
04BE540C2649403600427B39 /* multipleartboards.riv in Resources */,
|
||||
F2BBD3522E1DB9E10077FE94 /* data_binding_artboard.riv in Resources */,
|
||||
04BE540D2649403600427B39 /* junk.riv in Resources */,
|
||||
04BE54092649403600427B39 /* state_machine_configurations.riv in Resources */,
|
||||
04BE54072649403600427B39 /* what_a_state.riv in Resources */,
|
||||
@@ -757,6 +768,7 @@
|
||||
C3E2B580282F242400A8651B /* RiveStateMachineInstance+Extensions.swift in Sources */,
|
||||
F2B29EA32D52B5EB00CB30ED /* RiveDataBindingViewModelInstancePropertyData.m in Sources */,
|
||||
046FB7F5264EAA60000129B1 /* RiveSMIInput.mm in Sources */,
|
||||
F268DE422E25D42300BAB7CF /* RiveBindableArtboard.mm in Sources */,
|
||||
F2C07B1C2DA9854F00DC8C84 /* WeakContainer.m in Sources */,
|
||||
043026002AFA915B00320F2E /* RiveFileAsset.mm in Sources */,
|
||||
F2FD94042CC9492B00C1FC85 /* RiveFont.m in Sources */,
|
||||
@@ -1217,6 +1229,8 @@
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,3,7";
|
||||
TVOS_DEPLOYMENT_TARGET = 16.0;
|
||||
XROS_DEPLOYMENT_TARGET = 1.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -1241,6 +1255,8 @@
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,3,7";
|
||||
TVOS_DEPLOYMENT_TARGET = 16.0;
|
||||
XROS_DEPLOYMENT_TARGET = 1.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -1463,6 +1479,8 @@
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,3,7";
|
||||
TVOS_DEPLOYMENT_TARGET = 16.0;
|
||||
XROS_DEPLOYMENT_TARGET = 1.0;
|
||||
};
|
||||
name = "Debug (Catalyst)";
|
||||
};
|
||||
@@ -1682,6 +1700,8 @@
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,3,7";
|
||||
TVOS_DEPLOYMENT_TARGET = 16.0;
|
||||
XROS_DEPLOYMENT_TARGET = 1.0;
|
||||
};
|
||||
name = "Release (Catalyst)";
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@class RiveDataBindingViewModelInstanceTriggerProperty;
|
||||
@class RiveDataBindingViewModelInstanceImageProperty;
|
||||
@class RiveDataBindingViewModelInstanceListProperty;
|
||||
@class RiveDataBindingViewModelInstanceArtboardProperty;
|
||||
@class RiveDataBindingViewModelInstancePropertyData;
|
||||
|
||||
/// An object that represents an instance of a view model, used to update
|
||||
@@ -187,6 +188,19 @@ NS_SWIFT_NAME(RiveDataBindingViewModel.Instance)
|
||||
- (nullable RiveDataBindingViewModelInstanceListProperty*)listPropertyFromPath:
|
||||
(NSString*)path;
|
||||
|
||||
/// Gets an artboard property in the view model instance.
|
||||
///
|
||||
/// - Note: Unlike a `RiveViewModel.Instance`, a strong reference to this type
|
||||
/// does not have to be made. If the property exists, the underlying property
|
||||
/// will be cached, and calling this function again with the same path is
|
||||
/// guaranteed to return the same object.
|
||||
///
|
||||
/// - Parameter path: The path to the artboard property.
|
||||
///
|
||||
/// - Returns: The property if it exists at the supplied path, otherwise nil.
|
||||
- (nullable RiveDataBindingViewModelInstanceArtboardProperty*)
|
||||
artboardPropertyFromPath:(NSString*)path;
|
||||
|
||||
/// Calls all registered property listeners for the properties of the view model
|
||||
/// instance.
|
||||
- (void)updateListeners;
|
||||
|
||||
@@ -382,6 +382,41 @@
|
||||
return listProperty;
|
||||
}
|
||||
|
||||
- (RiveDataBindingViewModelInstanceArtboardProperty*)artboardPropertyFromPath:
|
||||
(NSString*)path
|
||||
{
|
||||
RiveDataBindingViewModelInstanceArtboardProperty* cached;
|
||||
if ((cached = [self
|
||||
cachedPropertyFromPath:path
|
||||
asClass:
|
||||
[RiveDataBindingViewModelInstanceArtboardProperty
|
||||
class]]))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
|
||||
auto artboard = _instance->propertyArtboard(std::string([path UTF8String]));
|
||||
if (artboard == nullptr)
|
||||
{
|
||||
[RiveLogger logWithViewModelInstance:self
|
||||
artboardPropertyAtPath:path
|
||||
found:NO];
|
||||
return nil;
|
||||
}
|
||||
|
||||
[RiveLogger logWithViewModelInstance:self
|
||||
artboardPropertyAtPath:path
|
||||
found:YES];
|
||||
RiveDataBindingViewModelInstanceArtboardProperty* artboardProperty =
|
||||
[[RiveDataBindingViewModelInstanceArtboardProperty alloc]
|
||||
initWithArtboard:artboard];
|
||||
artboardProperty.valueDelegate = self;
|
||||
|
||||
[self cacheProperty:artboardProperty withPath:path];
|
||||
|
||||
return artboardProperty;
|
||||
}
|
||||
|
||||
- (void)updateListeners
|
||||
{
|
||||
[_properties enumerateKeysAndObjectsUsingBlock:^(
|
||||
|
||||
@@ -365,4 +365,35 @@ NS_SWIFT_NAME(RiveDataBindingViewModelInstance.ListProperty)
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - Artboard
|
||||
|
||||
@class RiveBindableArtboard;
|
||||
|
||||
typedef void (^RiveDataBindingViewModelInstanceArtboardPropertyListener)(void)
|
||||
NS_SWIFT_NAME(RiveDataBindingViewModelInstanceArtboardProperty.Listener);
|
||||
|
||||
/// An object that represents a trigger property of a view model instance.
|
||||
NS_SWIFT_NAME(RiveDataBindingViewModelInstance.ArtboardProperty)
|
||||
@interface RiveDataBindingViewModelInstanceArtboardProperty
|
||||
: RiveDataBindingViewModelInstanceProperty
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (void)setValue:(RiveBindableArtboard*)artboard;
|
||||
|
||||
/// Adds a block as a listener, called when the property is triggered.
|
||||
///
|
||||
/// - Note: The property can be triggered either explicitly by the developer,
|
||||
/// or as a result of a change in a state machine.
|
||||
///
|
||||
/// - Parameter listener: The block that will be called when the property's
|
||||
/// value changes.
|
||||
///
|
||||
/// - Returns: A UUID for the listener, used in conjunction with
|
||||
/// `removeListener`.
|
||||
- (NSUUID*)addListener:
|
||||
(RiveDataBindingViewModelInstanceArtboardPropertyListener)listener;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -692,3 +692,43 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - Artboard
|
||||
|
||||
@implementation RiveDataBindingViewModelInstanceArtboardProperty
|
||||
{
|
||||
rive::ViewModelInstanceArtboardRuntime* _artboard;
|
||||
}
|
||||
|
||||
- (instancetype)initWithArtboard:
|
||||
(rive::ViewModelInstanceArtboardRuntime*)artboard
|
||||
{
|
||||
if (self = [super initWithValue:artboard])
|
||||
{
|
||||
_artboard = artboard;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setValue:(RiveBindableArtboard*)artboard
|
||||
{
|
||||
_artboard->value([artboard artboardInstance]);
|
||||
[RiveLogger logPropertyUpdated:self value:[artboard name]];
|
||||
}
|
||||
|
||||
- (NSUUID*)addListener:
|
||||
(RiveDataBindingViewModelInstanceTriggerPropertyListener)listener
|
||||
{
|
||||
return [super addListener:listener];
|
||||
}
|
||||
|
||||
- (void)handleListeners
|
||||
{
|
||||
for (RiveDataBindingViewModelInstanceImagePropertyListener listener in self
|
||||
.listeners.allValues)
|
||||
{
|
||||
listener();
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -29,6 +29,7 @@ enum RiveLoggerDataBindingEvent {
|
||||
case triggerProperty(String, Bool)
|
||||
case imageProperty(String, Bool)
|
||||
case listProperty(String, Bool)
|
||||
case artboardProperty(String, Bool)
|
||||
}
|
||||
enum Property: DataBindingEvent {
|
||||
case propertyUpdated(String, String)
|
||||
@@ -118,6 +119,11 @@ extension RiveLogger {
|
||||
let message = message(instance: instance, for: "list", path: path, found: found)
|
||||
dataBinding.debug("\(message)")
|
||||
}
|
||||
case .artboardProperty(let path, let found):
|
||||
_log(event: event, level: .debug) {
|
||||
let message = message(instance: instance, for: "artboard", path: path, found: found)
|
||||
dataBinding.debug("\(message)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,6 +200,10 @@ extension RiveLogger {
|
||||
Self.log(viewModelInstance: instance, event: .listProperty(path, found))
|
||||
}
|
||||
|
||||
@objc static func log(viewModelInstance instance: RiveDataBindingViewModel.Instance, artboardPropertyAtPath path: String, found: Bool) {
|
||||
Self.log(viewModelInstance: instance, event: .artboardProperty(path, found))
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
@objc(logPropertyUpdated:value:) static func log(propertyUpdated property: RiveDataBindingViewModel.Instance.Property, value: String) {
|
||||
|
||||
39
Source/Renderer/RiveBindableArtboard.mm
Normal file
39
Source/Renderer/RiveBindableArtboard.mm
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// RiveBindableArtboard.m
|
||||
// RiveRuntime
|
||||
//
|
||||
// Created by David Skuza on 7/14/25.
|
||||
// Copyright © 2025 Rive. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Rive.h>
|
||||
#import <RivePrivateHeaders.h>
|
||||
|
||||
@implementation RiveBindableArtboard
|
||||
{
|
||||
std::unique_ptr<rive::ArtboardInstance> _artboardInstance;
|
||||
}
|
||||
|
||||
- (instancetype)initWithArtboard:
|
||||
(std::unique_ptr<rive::ArtboardInstance>)artboard
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_artboardInstance = std::move(artboard);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (rive::ArtboardInstance*)artboardInstance
|
||||
{
|
||||
return _artboardInstance.get();
|
||||
}
|
||||
|
||||
- (NSString*)name
|
||||
{
|
||||
auto name = _artboardInstance->name();
|
||||
return [NSString stringWithCString:name.c_str()
|
||||
encoding:[NSString defaultCStringEncoding]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -538,6 +538,52 @@
|
||||
return [[RiveDataBindingViewModel alloc] initWithViewModel:viewModel];
|
||||
}
|
||||
|
||||
- (RiveArtboard*)bindableArtboardAtIndex:(NSInteger)index
|
||||
error:(NSError* __autoreleasing _Nullable*)
|
||||
error
|
||||
{
|
||||
auto artboard = riveFile->artboardAt(index);
|
||||
if (artboard == nullptr)
|
||||
{
|
||||
NSString* message = [NSString
|
||||
stringWithFormat:@"No Artboard Found at index %ld.", (long)index];
|
||||
[RiveLogger logFile:nil error:message];
|
||||
*error = [NSError errorWithDomain:RiveErrorDomain
|
||||
code:RiveNoArtboardFound
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : message,
|
||||
@"name" : @"NoArtboardFound"
|
||||
}];
|
||||
return nil;
|
||||
}
|
||||
return [[RiveArtboard alloc] initWithArtboard:std::move(artboard)];
|
||||
}
|
||||
|
||||
- (RiveArtboard*)bindableArtboardWithName:(NSString*)name
|
||||
error:(NSError* __autoreleasing _Nullable*)
|
||||
error
|
||||
{
|
||||
std::string stdName = std::string([name UTF8String]);
|
||||
auto artboard = riveFile->artboardNamed(stdName);
|
||||
if (artboard == nullptr)
|
||||
{
|
||||
NSString* message = [NSString
|
||||
stringWithFormat:@"No Artboard Found with name %@.", name];
|
||||
[RiveLogger logFile:nil error:message];
|
||||
*error = [NSError errorWithDomain:RiveErrorDomain
|
||||
code:RiveNoArtboardFound
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : message,
|
||||
@"name" : @"NoArtboardFound"
|
||||
}];
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
return [[RiveArtboard alloc] initWithArtboard:std::move(artboard)];
|
||||
}
|
||||
}
|
||||
|
||||
/// Clean up rive file
|
||||
- (void)dealloc
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#import <RiveRuntime/RiveFile.h>
|
||||
#import <RiveRuntime/RiveArtboard.h>
|
||||
#import <RiveRuntime/RiveBindableArtboard.h>
|
||||
#import <RiveRuntime/RiveSMIInput.h>
|
||||
#import <RiveRuntime/RiveLinearAnimationInstance.h>
|
||||
#import <RiveRuntime/RiveStateMachineInstance.h>
|
||||
|
||||
19
Source/Renderer/include/RiveBindableArtboard.h
Normal file
19
Source/Renderer/include/RiveBindableArtboard.h
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// RiveBindableArtboard.h
|
||||
// RiveRuntime
|
||||
//
|
||||
// Created by David Skuza on 7/14/25.
|
||||
// Copyright © 2025 Rive. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RiveBindableArtboard : NSObject
|
||||
|
||||
@property(nonatomic, readonly) NSString* name;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -18,6 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@class RiveFileAsset;
|
||||
@class RiveFactory;
|
||||
@class RiveDataBindingViewModel;
|
||||
@class RiveBindableArtboard;
|
||||
typedef bool (^LoadAsset)(RiveFileAsset* asset,
|
||||
NSData* data,
|
||||
RiveFactory* factory);
|
||||
@@ -152,6 +153,36 @@ typedef bool (^LoadAsset)(RiveFileAsset* asset,
|
||||
- (nullable RiveDataBindingViewModel*)defaultViewModelForArtboard:
|
||||
(RiveArtboard*)artboard;
|
||||
|
||||
/// Returns a bindable artboard from the file by its index.
|
||||
///
|
||||
/// A bindable artboard is an artboard that can be used with data binding
|
||||
/// features. The index of an artboard starts at 0, where 0 is the first
|
||||
/// artboard in the file.
|
||||
///
|
||||
/// - Parameter index: The index of the artboard to retrieve.
|
||||
/// - Parameter error: A pointer to an NSError object. If an error occurs, this
|
||||
/// pointer will contain an error object describing the problem.
|
||||
///
|
||||
/// - Returns: A bindable artboard if one exists at the specified index,
|
||||
/// otherwise nil.
|
||||
- (nullable RiveBindableArtboard*)bindableArtboardAtIndex:(NSInteger)index
|
||||
error:(NSError**)error;
|
||||
|
||||
/// Returns a bindable artboard from the file by its name.
|
||||
///
|
||||
/// A bindable artboard is an artboard that can be used with data binding
|
||||
/// features. The name must match exactly with an artboard name in the Rive
|
||||
/// file.
|
||||
///
|
||||
/// - Parameter name: The name of the artboard to retrieve. Must not be nil.
|
||||
/// - Parameter error: A pointer to an NSError object. If an error occurs, this
|
||||
/// pointer will contain an error object describing the problem.
|
||||
///
|
||||
/// - Returns: A bindable artboard if one exists with the specified name,
|
||||
/// otherwise nil.
|
||||
- (nullable RiveBindableArtboard*)bindableArtboardWithName:(NSString*)name
|
||||
error:(NSError**)error;
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
|
||||
@@ -240,4 +240,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (instancetype)initWithList:(rive::ViewModelInstanceListRuntime*)list;
|
||||
@end
|
||||
|
||||
@interface RiveDataBindingViewModelInstanceArtboardProperty ()
|
||||
- (instancetype)initWithArtboard:(rive::ViewModelInstanceArtboardRuntime*)list;
|
||||
@end
|
||||
|
||||
@interface RiveBindableArtboard ()
|
||||
- (rive::ArtboardInstance*)artboardInstance;
|
||||
- (instancetype)initWithArtboard:
|
||||
(std::unique_ptr<rive::ArtboardInstance>)artboard;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
BIN
Tests/Assets/data_binding_artboard.riv
Normal file
BIN
Tests/Assets/data_binding_artboard.riv
Normal file
Binary file not shown.
@@ -525,6 +525,42 @@ class DataBindingTests: XCTestCase {
|
||||
XCTAssertEqual(list.count, 2)
|
||||
}
|
||||
|
||||
// MARK: Artboard
|
||||
|
||||
func test_viewModelInstance_artboardInstance_canSetValue() throws {
|
||||
let file = try RiveFile(testfileName: "data_binding_artboard")
|
||||
let instance = file.viewModelNamed("Default")!.createInstance()!
|
||||
|
||||
let artboardProperty = instance.artboardProperty(fromPath: "Artboard")
|
||||
XCTAssertNotNil(artboardProperty)
|
||||
XCTAssertNil(instance.artboardProperty(fromPath: "404"))
|
||||
|
||||
let red = try file.bindableArtboard(withName: "Red")
|
||||
let green = try file.bindableArtboard(withName: "Green")
|
||||
let blue = try file.bindableArtboard(withName: "Blue")
|
||||
|
||||
let expectation = expectation(description: "artboard did set value")
|
||||
expectation.expectedFulfillmentCount = 3
|
||||
|
||||
artboardProperty?.addListener {
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
artboardProperty?.setValue(red)
|
||||
instance.updateListeners()
|
||||
artboardProperty?.clearChanges()
|
||||
|
||||
artboardProperty?.setValue(green)
|
||||
instance.updateListeners()
|
||||
artboardProperty?.clearChanges()
|
||||
|
||||
artboardProperty?.setValue(blue)
|
||||
instance.updateListeners()
|
||||
artboardProperty?.clearChanges()
|
||||
|
||||
wait(for: [expectation], timeout: 1)
|
||||
}
|
||||
|
||||
// MARK: Binding
|
||||
|
||||
func test_binding_artboard_stringProperty_updatesTextRun() throws {
|
||||
|
||||
Reference in New Issue
Block a user