Add text binding ios

Redoing the PR from: https://github.com/rive-app/rive/pull/5683

Wasn't recognizing latest commits for some reason, Github was having some PR status issues yesterday so might've been from that

Diffs=
fe466871e Add text binding ios (#5687)

Co-authored-by: Zachary Plata <plata.zach@gmail.com>
This commit is contained in:
zplata
2023-07-31 14:07:41 +00:00
parent 8f92e4d2ce
commit 7fd8df317e
19 changed files with 228 additions and 3 deletions

View File

@@ -1 +1 @@
caed30e555827bd4f90339e85a0df7cc3a143c3a
fe466871ee8066d4fe40da8742f810f9a1be31d1

Binary file not shown.

Binary file not shown.

View File

@@ -128,6 +128,11 @@
C9D3DE5B264F3B51001BA265 /* liquid.riv in Resources */ = {isa = PBXBuildFile; fileRef = C9D3DE5A264F3B51001BA265 /* liquid.riv */; };
C9D3DE68264F49F4001BA265 /* life_bar.riv in Resources */ = {isa = PBXBuildFile; fileRef = C9D3DE67264F49F4001BA265 /* life_bar.riv */; };
C9E040A9264DFCFD009ABC7C /* switch.riv in Resources */ = {isa = PBXBuildFile; fileRef = C9E040A8264DFCFD009ABC7C /* switch.riv */; };
E57798A02A72C77900FF25C3 /* testtext.riv in Resources */ = {isa = PBXBuildFile; fileRef = E577989D2A72C77900FF25C3 /* testtext.riv */; };
E57798A92A730A9B00FF25C3 /* SwiftTestText.swift in Sources */ = {isa = PBXBuildFile; fileRef = E57798A82A730A9B00FF25C3 /* SwiftTestText.swift */; };
E57798AC2A731C5800FF25C3 /* testtext.riv in Resources */ = {isa = PBXBuildFile; fileRef = E577989D2A72C77900FF25C3 /* testtext.riv */; };
E57798AE2A73264100FF25C3 /* text_test_2.riv in Resources */ = {isa = PBXBuildFile; fileRef = E57798AD2A73264100FF25C3 /* text_test_2.riv */; };
E57798AF2A73264100FF25C3 /* text_test_2.riv in Resources */ = {isa = PBXBuildFile; fileRef = E57798AD2A73264100FF25C3 /* text_test_2.riv */; };
E5A7874A27E115170056F24B /* energy_bar_example.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5A7874727E115170056F24B /* energy_bar_example.riv */; };
E5A7874C27E1158E0056F24B /* prop_example.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5A7874B27E1158E0056F24B /* prop_example.riv */; };
E5CD7D7127DC331900BFE5E2 /* SwiftMeshAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5CD7D7027DC331900BFE5E2 /* SwiftMeshAnimation.swift */; };
@@ -254,6 +259,9 @@
C9D3DE5A264F3B51001BA265 /* liquid.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = liquid.riv; sourceTree = "<group>"; };
C9D3DE67264F49F4001BA265 /* life_bar.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = life_bar.riv; sourceTree = "<group>"; };
C9E040A8264DFCFD009ABC7C /* switch.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = switch.riv; sourceTree = "<group>"; };
E577989D2A72C77900FF25C3 /* testtext.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = testtext.riv; sourceTree = "<group>"; };
E57798A82A730A9B00FF25C3 /* SwiftTestText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftTestText.swift; sourceTree = "<group>"; };
E57798AD2A73264100FF25C3 /* text_test_2.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = text_test_2.riv; sourceTree = "<group>"; };
E5A7874727E115170056F24B /* energy_bar_example.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = energy_bar_example.riv; sourceTree = "<group>"; };
E5A7874B27E1158E0056F24B /* prop_example.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = prop_example.riv; sourceTree = "<group>"; };
E5CD7D7027DC331900BFE5E2 /* SwiftMeshAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftMeshAnimation.swift; sourceTree = "<group>"; };
@@ -365,6 +373,7 @@
C3ECAC28281837B300A81123 /* play_button_event_example.riv */,
C3ECAC2A281837B300A81123 /* switch_event_example.riv */,
C3ECAC222817BE1100A81123 /* magic_8-ball_v2.riv */,
E577989D2A72C77900FF25C3 /* testtext.riv */,
27108F2C282C96E700A99D81 /* light_switch.riv */,
C3D187F628075B6C008B739A /* riveslider.riv */,
C324DB5E280740FB0060589F /* rbutton.riv */,
@@ -382,6 +391,7 @@
E5A7874B27E1158E0056F24B /* prop_example.riv */,
04F1C80826A8442300CEE6BE /* two_bone_ik.riv */,
E5A7874727E115170056F24B /* energy_bar_example.riv */,
E57798AD2A73264100FF25C3 /* text_test_2.riv */,
C9D3DE5A264F3B51001BA265 /* liquid.riv */,
042C88D62644447500E7DBB2 /* loopy.riv */,
042C88CD2644447400E7DBB2 /* mascot.riv */,
@@ -415,6 +425,7 @@
C3ECAC262817BE4600A81123 /* SwiftTouchEvents.swift */,
C3E2B58B2833ECFE00A8651B /* SwiftCannonGame.swift */,
C3C074ED28414F4600E8EB33 /* SwiftTestParityAnimSM.swift */,
E57798A82A730A9B00FF25C3 /* SwiftTestText.swift */,
);
path = SwiftUI;
sourceTree = "<group>";
@@ -591,6 +602,7 @@
04E51C522A151C230075E473 /* constrained.riv in Resources */,
04E51C532A151C230075E473 /* nothing.riv in Resources */,
04E51C542A151C230075E473 /* artboard_animations.riv in Resources */,
E57798AC2A731C5800FF25C3 /* testtext.riv in Resources */,
04E51C552A151C230075E473 /* basketball.riv in Resources */,
04E51C562A151C230075E473 /* clean_icon_set.riv in Resources */,
04E51C572A151C230075E473 /* clipping.riv in Resources */,
@@ -609,6 +621,7 @@
04E51C642A151C230075E473 /* off_road_car_blog.riv in Resources */,
04E51C652A151C230075E473 /* progress.riv in Resources */,
04E51C662A151C230075E473 /* pull.riv in Resources */,
E57798AF2A73264100FF25C3 /* text_test_2.riv in Resources */,
04E51C672A151C230075E473 /* rope.riv in Resources */,
04E51C682A151C230075E473 /* skills.riv in Resources */,
04E51C692A151C230075E473 /* switch.riv in Resources */,
@@ -635,6 +648,7 @@
C9BD3926263B5FC700696C37 /* truck_v7.riv in Resources */,
042C88DC2644447500E7DBB2 /* pull.riv in Resources */,
042C88E32644447500E7DBB2 /* flux_capacitor.riv in Resources */,
E57798A02A72C77900FF25C3 /* testtext.riv in Resources */,
C9CE8266263B90E000F98DDB /* juice_v7.riv in Resources */,
C3ECAC2B281837B300A81123 /* play_button_event_example.riv in Resources */,
042C88E42644447500E7DBB2 /* neostream.riv in Resources */,
@@ -655,6 +669,7 @@
C3C074AC283FC75900E8EB33 /* teststatemachine.riv in Resources */,
C3ECAC2D281837B300A81123 /* switch_event_example.riv in Resources */,
0480028B2729AA4400F7132B /* clean_icon_set.riv in Resources */,
E57798AE2A73264100FF25C3 /* text_test_2.riv in Resources */,
04F1C80B26A8442300CEE6BE /* two_bone_ik.riv in Resources */,
C3745FCE282AE2320087F4AF /* hero_editor.riv in Resources */,
C3C074F2284161E400E8EB33 /* halloween.riv in Resources */,
@@ -718,6 +733,7 @@
04026DCA27CE3EF6002B3DBF /* SwiftMultipleAnimations.swift in Sources */,
042C88882643DB7100E7DBB2 /* SimpleAnimation.swift in Sources */,
04026DC427CE3ED6002B3DBF /* SwiftSimpleAnimation.swift in Sources */,
E57798A92A730A9B00FF25C3 /* SwiftTestText.swift in Sources */,
04026DCE27CE3F0F002B3DBF /* SwiftStateMachine.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@@ -0,0 +1,34 @@
//
// SwiftTestText.swift
// Example (iOS)
//
// Created by Zach Plata on 7/27/23.
// Copyright © 2023 Rive. All rights reserved.
//
import SwiftUI
import RiveRuntime
struct TextInputView: DismissableView {
var dismiss: () -> Void = {}
@State private var userInput: String = ""
@State private var rvm = RiveViewModel(fileName: "text_test_2")
var body: some View {
VStack(spacing: 20) {
Text("Enter text:")
.font(.headline)
TextField("Enter text...", text: $userInput)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onChange(of: userInput, perform: { newValue in
if (!newValue.isEmpty) {
try! rvm.setTextRunValue("MyRun", textValue: userInput)
}
})
rvm.view()
}
.padding()
}
}

View File

@@ -35,7 +35,8 @@ class ExamplesMasterTableViewController: UITableViewController {
("MultipleAnimations", typeErased(dismissableView: SwiftMultipleAnimations())),
("Cannon Game", typeErased(dismissableView: SwiftCannonGame())),
("State Machine", typeErased(dismissableView: SwiftStateMachine())),
("Mesh Animation", typeErased(dismissableView: SwiftMeshAnimation()))
("Mesh Animation", typeErased(dismissableView: SwiftMeshAnimation())),
("Playing with Text", typeErased(dismissableView: TextInputView()))
]

View File

@@ -4,7 +4,7 @@
# Rive iOS
![Rive hero image](https://rive-app.notion.site/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fff44ed5f-1eea-4154-81ef-84547e61c3fd%2Frive_notion.png?table=block&id=f198cab2-c0bc-4ce8-970c-42220379bcf3&spaceId=9c949665-9ad9-445f-b9c4-5ee204f8b60c&width=2000&userId=&cache=v2)
![Rive hero image](https://cdn.rive.app/rive_logo_dark_bg.png)
An iOS/macOS runtime library for [Rive](https://rive.app) that supports both UIKit, AppKit, and SwiftUI.

View File

@@ -62,6 +62,9 @@
C9C73EE224FC478900EF9516 /* RiveRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = C9C73ED424FC478800EF9516 /* RiveRuntime.h */; settings = {ATTRIBUTES = (Public, ); }; };
C9C741F424FC510200EF9516 /* Rive.h in Headers */ = {isa = PBXBuildFile; fileRef = C9C741F224FC510200EF9516 /* Rive.h */; settings = {ATTRIBUTES = (Public, ); }; };
C9C741F524FC510200EF9516 /* Rive.mm in Sources */ = {isa = PBXBuildFile; fileRef = C9C741F324FC510200EF9516 /* Rive.mm */; };
E57798A62A72C9C500FF25C3 /* RiveTextValueRun.mm in Sources */ = {isa = PBXBuildFile; fileRef = E57798A52A72C9C500FF25C3 /* RiveTextValueRun.mm */; };
E57798A72A72EEAD00FF25C3 /* RiveTextValueRun.h in Headers */ = {isa = PBXBuildFile; fileRef = E57798A12A72C81F00FF25C3 /* RiveTextValueRun.h */; settings = {ATTRIBUTES = (Public, ); }; };
E57798AB2A7310B700FF25C3 /* testtext.riv in Resources */ = {isa = PBXBuildFile; fileRef = E57798AA2A7310B700FF25C3 /* testtext.riv */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -135,6 +138,9 @@
C9C73EE124FC478900EF9516 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C9C741F224FC510200EF9516 /* Rive.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rive.h; sourceTree = "<group>"; };
C9C741F324FC510200EF9516 /* Rive.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Rive.mm; sourceTree = "<group>"; };
E57798A12A72C81F00FF25C3 /* RiveTextValueRun.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = RiveTextValueRun.h; sourceTree = "<group>"; };
E57798A52A72C9C500FF25C3 /* RiveTextValueRun.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RiveTextValueRun.mm; sourceTree = "<group>"; };
E57798AA2A7310B700FF25C3 /* testtext.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = testtext.riv; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -170,6 +176,7 @@
04BE542F264D1F4100427B39 /* LayerState.h */,
04BE5435264D2A7500427B39 /* RivePrivateHeaders.h */,
274175FB286DB9CE000A60D1 /* cg_skia_factory.hpp */,
E57798A12A72C81F00FF25C3 /* RiveTextValueRun.h */,
);
path = include;
sourceTree = "<group>";
@@ -183,6 +190,7 @@
04BE54012649403600427B39 /* junk.riv */,
04ED72F2299C115100E8DE53 /* empty_animation_state.riv */,
04BE53FF2649403600427B39 /* multiple_animations.riv */,
E57798AA2A7310B700FF25C3 /* testtext.riv */,
04BE54022649403600427B39 /* multiple_state_machines.riv */,
04BE54002649403600427B39 /* multipleartboards.riv */,
04BE53FE2649403600427B39 /* noanimation.riv */,
@@ -219,6 +227,7 @@
046FB7E8264EAA5F000129B1 /* RiveStateMachineInstance.mm */,
046FB7E5264EAA5F000129B1 /* RiveSMIInput.mm */,
C9601F2A250C25930032AA07 /* RiveRenderer.mm */,
E57798A52A72C9C500FF25C3 /* RiveTextValueRun.mm */,
04BE5431264D243D00427B39 /* LayerState.mm */,
274175FC286DB9CE000A60D1 /* cg_skia_factory.cpp */,
);
@@ -298,6 +307,7 @@
274175FD286DB9CE000A60D1 /* cg_skia_factory.hpp in Headers */,
046FB800264EAA61000129B1 /* RiveStateMachineInstance.h in Headers */,
046FB7FB264EAA61000129B1 /* RiveSMIInput.h in Headers */,
E57798A72A72EEAD00FF25C3 /* RiveTextValueRun.h in Headers */,
046FB7F1264EAA60000129B1 /* RiveLinearAnimationInstance.h in Headers */,
04BE5430264D1F4100427B39 /* LayerState.h in Headers */,
C9C741F424FC510200EF9516 /* Rive.h in Headers */,
@@ -403,6 +413,7 @@
04BE54082649403600427B39 /* flux_capacitor.riv in Resources */,
04BE54052649403600427B39 /* noartboard.riv in Resources */,
04BE540B2649403600427B39 /* multiple_animations.riv in Resources */,
E57798AB2A7310B700FF25C3 /* testtext.riv in Resources */,
04ED72F3299C115100E8DE53 /* empty_animation_state.riv in Resources */,
04BE54062649403600427B39 /* off_road_car_blog.riv in Resources */,
04BE540C2649403600427B39 /* multipleartboards.riv in Resources */,
@@ -437,6 +448,7 @@
046FB7FF264EAA61000129B1 /* RiveFile.mm in Sources */,
046FB7F2264EAA60000129B1 /* RiveArtboard.mm in Sources */,
046FB7F4264EAA60000129B1 /* RiveLinearAnimationInstance.mm in Sources */,
E57798A62A72C9C500FF25C3 /* RiveTextValueRun.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -217,4 +217,11 @@ static int artInstanceCount = 0;
return CGRectMake(aabb.minX, aabb.minY, aabb.width(), aabb.height());
}
- (RiveTextValueRun*)textRun:(NSString*)name
{
const std::string stdName = std::string([name UTF8String]);
auto riveTextRun = _artboardInstance->find<rive::TextValueRun>(stdName);
return [[RiveTextValueRun alloc] initWithTextValueRun:std::move(riveTextRun)];
}
@end

View File

@@ -0,0 +1,50 @@
//
// RiveTextValueRun.m
//
//
// Created by Zach Plata on 7/27/23.
//
#import <Rive.h>
#import <RivePrivateHeaders.h>
/*
* RiveTextValueRun
*/
@implementation RiveTextValueRun
{
const rive::TextValueRun* instance; // note: we do NOT own this, so don't delete it
}
- (const rive::TextValueRun*)getInstance
{
return instance;
}
// Creates a new RiveTextValueRun from a cpp TextValueRun
- (instancetype)initWithTextValueRun:(const rive::TextValueRun*)textRun
{
if (self = [super init])
{
instance = textRun;
return self;
}
else
{
return nil;
}
}
- (void)setText:(NSString*)textValue
{
std::string stdName = std::string([textValue UTF8String]);
((rive::TextValueRun*)[self getInstance])->text(stdName);
}
- (NSString*)text
{
std::string str = ((const rive::TextValueRun*)instance)->text();
return [NSString stringWithCString:str.c_str() encoding:[NSString defaultCStringEncoding]];
}
@end

View File

@@ -17,6 +17,7 @@
#import <RiveRuntime/RiveSMIInput.h>
#import <RiveRuntime/RiveLinearAnimationInstance.h>
#import <RiveRuntime/RiveStateMachineInstance.h>
#import <RiveRuntime/RiveTextValueRun.h>
#import <RiveRuntime/LayerState.h>
NS_ASSUME_NONNULL_BEGIN

View File

@@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
@class RiveLinearAnimationInstance;
@class RiveStateMachineInstance;
@class RiveRenderer;
@class RiveTextValueRun;
// MARK: - RiveArtboard
//
@@ -35,6 +36,8 @@ NS_ASSUME_NONNULL_BEGIN
- (RiveStateMachineInstance* __nullable)stateMachineFromName:(NSString*)name error:(NSError**)error;
- (RiveStateMachineInstance* __nullable)defaultStateMachine;
- (RiveTextValueRun* __nullable)textRun:(NSString*)name;
- (void)advanceBy:(double)elapsedSeconds;
- (void)draw:(RiveRenderer*)renderer;

View File

@@ -26,6 +26,7 @@
#import "rive/animation/any_state.hpp"
#import "rive/animation/exit_state.hpp"
#import "rive/animation/animation_state.hpp"
#import "rive/text/text_value_run.hpp"
// MARK: - Feature Flags
@@ -65,6 +66,13 @@
@interface RiveSMIBool ()
@end
/*
* RiveTextValueRun interface
*/
@interface RiveTextValueRun ()
- (instancetype)initWithTextValueRun:(const rive::TextValueRun*)riveTextValueRun;
@end
/*
* RiveLinearAnimationInstance interface
*/

View File

@@ -0,0 +1,26 @@
//
// RiveTextValueRun.h
// RiveRuntime
//
// Created by Zach Plata on 7/27/23.
// Copyright © 2023 Rive. All rights reserved.
//
#ifndef rive_text_value_run_h
#define rive_text_value_run_h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/*
* TextValueRun
*/
@interface RiveTextValueRun : NSObject
- (NSString*)text;
- (void)setText:(NSString*)newValue;
@end
NS_ASSUME_NONNULL_END
#endif /* rive_text_value_run_h */

View File

@@ -316,6 +316,29 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
setInput(inputName, value: Float(value))
}
/// Get a text value from a specified text run
/// - Parameters:
/// - textRunName: The name of a `Text Run` on the active Artboard
/// - Returns: String text value of the specified text run if applicable
open func getTextRunValue(_ textRunName: String) -> String? {
if let textRun = riveModel?.artboard?.textRun(textRunName) {
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
/// - value: A String value for the text run
open func setTextRunValue(_ textRunName: String, textValue: String) throws {
if let textRun = riveModel?.artboard?.textRun(textRunName) {
textRun.setText(textValue)
} 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
open func artboardNames() -> [String] {
return riveModel?.riveFile.artboardNames() ?? []
@@ -410,6 +433,10 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
open func player(loopedWithModel riveModel: RiveModel?, type: Int) { }
open func player(stoppedWithModel riveModel: RiveModel?) { }
open func player(didAdvanceby seconds: Double, riveModel: RiveModel?) { }
enum RiveError: Error {
case textValueRunError(_ message: String)
}
}
#if os(iOS)

View File

@@ -24,4 +24,5 @@ FOUNDATION_EXPORT const unsigned char RiveRuntimeVersionString[];
#import <RiveRuntime/RiveLinearAnimationInstance.h>
#import <RiveRuntime/RiveStateMachineInstance.h>
#import <RiveRuntime/LayerState.h>
#import <RiveRuntime/RiveTextValueRun.h>
#import <RiveRuntime/rive_renderer_view.hh>

BIN
Tests/Assets/testtext.riv Normal file

Binary file not shown.

View File

@@ -113,4 +113,32 @@
[file artboardFromName:@"bone" error:nil];
}
/*
* Test getting a RiveTextValueRun
*/
- (void)testGettingTextRunValue
{
RiveFile* file = [Util loadTestFile:@"testtext" error:nil];
NSError* error = nil;
RiveArtboard* artboard = [file artboardFromName:@"New Artboard" error:&error];
RiveTextValueRun* textRun = [artboard textRun:@"MyRun"];
XCTAssertTrue([[textRun text] isEqualToString:@"Hello there"]);
}
/*
* Test setting a RiveTextValueRun text value
*/
- (void)testSettingTextRunValue
{
RiveFile* file = [Util loadTestFile:@"testtext" error:nil];
NSError* error = nil;
RiveArtboard* artboard = [file artboardFromName:@"New Artboard" error:&error];
RiveTextValueRun* textRun = [artboard textRun:@"MyRun"];
XCTAssertTrue([[textRun text] isEqualToString:@"Hello there"]);
[textRun setText:@"Hello text"];
XCTAssertTrue([[textRun text] isEqualToString:@"Hello text"]);
}
@end

View File

@@ -22,4 +22,15 @@ class RiveViewModelTest: XCTestCase {
view.advance(delta: 0.1)
}
func testChangingTextRun() throws {
let file = try RiveFile(testfileName: "testtext")
let model = RiveModel(riveFile: file)
let viewModel = RiveViewModel(model, autoPlay: false)
let view = viewModel.createRiveView()
XCTAssertEqual(viewModel.getTextRunValue("MyRun"), "Hello there")
try viewModel.setTextRunValue("MyRun", textValue: "Hello test")
XCTAssertEqual(viewModel.getTextRunValue("MyRun"), "Hello test")
}
}