mirror of
https://github.com/rive-app/rive-ios.git
synced 2026-01-18 17:11:28 +01:00
Add support for nested text runs on iOS
This adds support for getting / setting nested text runs on iOS, via the exposed lower-level API. Tests have been added for: testing that setting the text run works at the artboard level, and at the view model level, and additionally that the parent text run will be returned if the path is empty. Diffs= bbec5cbbc Add support for nested text runs on iOS (#8108) Co-authored-by: David Skuza <david@rive.app>
This commit is contained in:
@@ -1 +1 @@
|
||||
a4e15fb7b532b276e15b5d3b3a60843581641a05
|
||||
bbec5cbbcce1ab111261066f885b0d46ecee4186
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
E599DCF92AAFA06100D1E49A /* rating_animation.riv in Resources */ = {isa = PBXBuildFile; fileRef = E599DCF82AAFA06100D1E49A /* rating_animation.riv */; };
|
||||
E599DCFA2AAFA06100D1E49A /* rating_animation.riv in Resources */ = {isa = PBXBuildFile; fileRef = E599DCF82AAFA06100D1E49A /* rating_animation.riv */; };
|
||||
F21F08142C66526D00FFA205 /* RiveFallbackFontDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F21F08132C66526D00FFA205 /* RiveFallbackFontDescriptor.swift */; };
|
||||
F23626AA2C8F90FA00727D9A /* nested_text_run.riv in Resources */ = {isa = PBXBuildFile; fileRef = F23626A92C8F90FA00727D9A /* nested_text_run.riv */; };
|
||||
F28DE4532C5002D900F3C379 /* RiveModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28DE4522C5002D900F3C379 /* RiveModelTests.swift */; };
|
||||
F2D285492C6D469900728340 /* RiveFallbackFontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D285482C6D469900728340 /* RiveFallbackFontProvider.swift */; };
|
||||
F2ECC2312C666824008B20E5 /* RiveFallbackFontDescriptor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2ECC2302C666824008B20E5 /* RiveFallbackFontDescriptor+Extensions.swift */; };
|
||||
@@ -200,6 +201,7 @@
|
||||
E5964A972A9697B600140479 /* RiveEvent.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = RiveEvent.mm; sourceTree = "<group>"; };
|
||||
E599DCF82AAFA06100D1E49A /* rating_animation.riv */ = {isa = PBXFileReference; lastKnownFileType = file; name = rating_animation.riv; path = "Example-iOS/Assets/rating_animation.riv"; sourceTree = SOURCE_ROOT; };
|
||||
F21F08132C66526D00FFA205 /* RiveFallbackFontDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiveFallbackFontDescriptor.swift; sourceTree = "<group>"; };
|
||||
F23626A92C8F90FA00727D9A /* nested_text_run.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = nested_text_run.riv; sourceTree = "<group>"; };
|
||||
F28DE4522C5002D900F3C379 /* RiveModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiveModelTests.swift; sourceTree = "<group>"; };
|
||||
F2D285482C6D469900728340 /* RiveFallbackFontProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiveFallbackFontProvider.swift; sourceTree = "<group>"; };
|
||||
F2ECC2302C666824008B20E5 /* RiveFallbackFontDescriptor+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RiveFallbackFontDescriptor+Extensions.swift"; sourceTree = "<group>"; };
|
||||
@@ -260,6 +262,7 @@
|
||||
04BE53F726493FE600427B39 /* Assets */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F23626A92C8F90FA00727D9A /* nested_text_run.riv */,
|
||||
0424A8752BD59435000A9A1C /* img_test.riv */,
|
||||
04E2223F2BE3C85100D82668 /* ball_test.riv */,
|
||||
0424A8732BD592F2000A9A1C /* audio_test.riv */,
|
||||
@@ -544,6 +547,7 @@
|
||||
C38BB5F4287629C20039E385 /* defaultstatemachine.riv in Resources */,
|
||||
04BE54082649403600427B39 /* flux_capacitor.riv in Resources */,
|
||||
04BE54052649403600427B39 /* noartboard.riv in Resources */,
|
||||
F23626AA2C8F90FA00727D9A /* nested_text_run.riv in Resources */,
|
||||
0424A8742BD592F2000A9A1C /* audio_test.riv in Resources */,
|
||||
0412652A2B0CCB8E009400EC /* embedded_assets.riv in Resources */,
|
||||
04BE540B2649403600427B39 /* multiple_animations.riv in Resources */,
|
||||
|
||||
@@ -243,6 +243,24 @@ static int artInstanceCount = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- (RiveTextValueRun*)textRun:(NSString*)name path:(NSString*)path
|
||||
{
|
||||
if (path.length == 0)
|
||||
{
|
||||
return [self textRun:name];
|
||||
}
|
||||
|
||||
const std::string stdName = std::string([name UTF8String]);
|
||||
const std::string stdPath = std::string([path UTF8String]);
|
||||
// Can we update the cpp library to handle empty paths / default to parent if nullptr?
|
||||
auto riveTextRun = _artboardInstance->getTextRun(stdName, stdPath);
|
||||
if (riveTextRun != nullptr)
|
||||
{
|
||||
return [[RiveTextValueRun alloc] initWithTextValueRun:std::move(riveTextRun)];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- (RiveSMIBool*)getBool:(NSString*)name path:(NSString*)path
|
||||
{
|
||||
// Create a unique dictionary name for nested artboards + booleans;
|
||||
|
||||
@@ -46,6 +46,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (RiveStateMachineInstance* __nullable)defaultStateMachine;
|
||||
|
||||
- (RiveTextValueRun* __nullable)textRun:(NSString*)name;
|
||||
- (RiveTextValueRun* __nullable)textRun:(NSString*)name path:(NSString*)path;
|
||||
|
||||
- (void)advanceBy:(double)elapsedSeconds;
|
||||
- (void)draw:(RiveRenderer*)renderer;
|
||||
|
||||
@@ -399,7 +399,19 @@ import Combine
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
/// Get a text value from a specified text run
|
||||
/// - Parameters:
|
||||
/// - textRunName: The name of a `Text Run` on the active Artboard
|
||||
/// - path: The path to the nested text run.
|
||||
/// - Returns: String text value of the specified text run if applicable
|
||||
@objc open func getTextRunValue(_ textRunName: String, path: String) -> String? {
|
||||
if let textRun = riveModel?.artboard?.textRun(textRunName, path: path) {
|
||||
return textRun.text()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Set a text value for a specified text run
|
||||
/// - Parameters:
|
||||
/// - textRunName: The name of a `Text Run` on the active Artboard
|
||||
@@ -407,11 +419,33 @@ import Combine
|
||||
@objc open func setTextRunValue(_ textRunName: String, textValue: String) throws {
|
||||
if let textRun = riveModel?.artboard?.textRun(textRunName) {
|
||||
textRun.setText(textValue)
|
||||
|
||||
if isPlaying == false {
|
||||
riveView?.advance(delta: 0)
|
||||
}
|
||||
} else {
|
||||
throw RiveError.textValueRunError("Could not set text value on text run: \(textRunName) as the text run could not be found from the active artboard")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Set a text value for a specified text run
|
||||
/// - Parameters:
|
||||
/// - textRunName: The name of a `Text Run` on the active Artboard
|
||||
/// - path: The path to the nested text run.
|
||||
/// - value: A String value for the text run
|
||||
/// - Note: If the specified path is empty, the parent artboard will be used to find the text run.
|
||||
@objc open func setTextRunValue(_ textRunName: String, path: String, textValue: String) throws {
|
||||
if let textRun = riveModel?.artboard?.textRun(textRunName, path: path) {
|
||||
textRun.setText(textValue)
|
||||
|
||||
if isPlaying == false {
|
||||
riveView?.advance(delta: 0)
|
||||
}
|
||||
} else {
|
||||
throw RiveError.textValueRunError("Could not set text value on text run: \(textRunName) as the text run could not be found from the active artboard")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace this with a more robust structure of the file's contents
|
||||
@objc open func artboardNames() -> [String] {
|
||||
return riveModel?.riveFile.artboardNames() ?? []
|
||||
|
||||
BIN
Tests/Assets/nested_text_run.riv
Normal file
BIN
Tests/Assets/nested_text_run.riv
Normal file
Binary file not shown.
@@ -141,6 +141,27 @@
|
||||
XCTAssertTrue([[textRun text] isEqualToString:@"Hello text"]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test setting a nested RiveTextValueRun text value
|
||||
*/
|
||||
- (void)testSettingNestedTextRunValue
|
||||
{
|
||||
RiveFile* file = [Util loadTestFile:@"nested_text_run" error:nil];
|
||||
NSError* error = nil;
|
||||
RiveArtboard* artboard = [file artboardFromName:@"Artboard" error:&error];
|
||||
|
||||
// If there is no path specified, check the parent artboard
|
||||
RiveTextValueRun* textRun = [artboard textRun:@"parent" path:@""];
|
||||
XCTAssertTrue([[textRun text] isEqualToString:@"Parent"]);
|
||||
|
||||
// Otherwise, test nested artboard naming
|
||||
textRun = [artboard textRun:@"text" path:@"Nested/Two-Deep"];
|
||||
XCTAssertTrue([[textRun text] isEqualToString:@"Text"]);
|
||||
[textRun setText:@"Hello text"];
|
||||
|
||||
XCTAssertTrue([[textRun text] isEqualToString:@"Hello text"]);
|
||||
}
|
||||
|
||||
- (void)testCatchingErrorOnBadTextRun
|
||||
{
|
||||
RiveFile* file = [Util loadTestFile:@"testtext" error:nil];
|
||||
|
||||
@@ -23,13 +23,49 @@ class RiveViewModelTest: XCTestCase {
|
||||
view.advance(delta: 0.1)
|
||||
}
|
||||
|
||||
func testChangingTextRun() throws {
|
||||
func testChangingTextRun_updatesText_andAdvances() throws {
|
||||
let file = try RiveFile(testfileName: "testtext")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, autoPlay: false)
|
||||
|
||||
let delegate = PlayerDelegate()
|
||||
let view = viewModel.createRiveView()
|
||||
view.playerDelegate = delegate
|
||||
|
||||
XCTAssertEqual(viewModel.getTextRunValue("MyRun"), "Hello there")
|
||||
try viewModel.setTextRunValue("MyRun", textValue: "Hello test")
|
||||
XCTAssertEqual(viewModel.getTextRunValue("MyRun"), "Hello test")
|
||||
XCTAssertTrue(delegate.didAdvance)
|
||||
}
|
||||
|
||||
func testChangingNestedTextRun_updatesText_andAdvances() throws {
|
||||
let file = try RiveFile(testfileName: "nested_text_run")
|
||||
let model = RiveModel(riveFile: file)
|
||||
let viewModel = RiveViewModel(model, autoPlay: false)
|
||||
let view = viewModel.createRiveView()
|
||||
let delegate = PlayerDelegate()
|
||||
view.playerDelegate = delegate
|
||||
|
||||
XCTAssertEqual(viewModel.getTextRunValue("text", path: "Nested/Two-Deep"), "Text")
|
||||
try viewModel.setTextRunValue("text", path: "Nested/Two-Deep", textValue: "Hello test")
|
||||
XCTAssertEqual(viewModel.getTextRunValue("text", path: "Nested/Two-Deep"), "Hello test")
|
||||
XCTAssertTrue(delegate.didAdvance)
|
||||
}
|
||||
}
|
||||
|
||||
private extension RiveViewModelTest {
|
||||
class PlayerDelegate: NSObject, RivePlayerDelegate {
|
||||
var didAdvance = false
|
||||
|
||||
func player(playedWithModel riveModel: RiveRuntime.RiveModel?) { }
|
||||
|
||||
func player(pausedWithModel riveModel: RiveRuntime.RiveModel?) { }
|
||||
|
||||
func player(loopedWithModel riveModel: RiveRuntime.RiveModel?, type: Int) { }
|
||||
|
||||
func player(stoppedWithModel riveModel: RiveRuntime.RiveModel?) { }
|
||||
|
||||
func player(didAdvanceby seconds: Double, riveModel: RiveModel?) {
|
||||
didAdvance = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user