Skip to content

Commit

Permalink
Merge pull request #56 from rive-app/updateReadme
Browse files Browse the repository at this point in the history
Update readme
  • Loading branch information
mjtalbot committed May 27, 2021
2 parents 3044c18 + 9a0884d commit 4adeba6
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 54 deletions.
21 changes: 18 additions & 3 deletions Example-iOS/Source/UIkit/SimpleAnimation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,31 @@
import UIKit
import RiveRuntime

func getResourceBytes(resourceName: String, resourceExt: String=".riv") -> [UInt8] {
guard let url = Bundle.main.url(forResource: resourceName, withExtension: resourceExt) else {
fatalError("Failed to locate \(resourceName) in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(url) from bundle.")
}

// Import the data into a RiveFile
return [UInt8](data)
}


class SimpleAnimationViewController: UIViewController {
let resourceName = "truck_v7"


override public func loadView() {
super.loadView()
// Wire up an instance of RiveView to the controller

let view = RiveView()
guard let riveFile = RiveFile(byteArray: getResourceBytes(resourceName: resourceName)) else {
fatalError("Failed to load RiveFile")
}
view.configure(riveFile)

self.view = view
(self.view! as! RiveView).configure(getRiveFile(resourceName: resourceName))
}
}
24 changes: 10 additions & 14 deletions Example-iOS/Source/lib/utility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,22 @@
import Foundation
import RiveRuntime

func getRiveFile(resourceName: String, resourceExt: String=".riv") -> RiveFile {
func getBytes(resourceName: String, resourceExt: String=".riv") -> [UInt8] {
guard let url = Bundle.main.url(forResource: resourceName, withExtension: resourceExt) else {
fatalError("Failed to locate \(resourceName) in bundle.")
}
guard var data = try? Data(contentsOf: url) else {
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(url) from bundle.")
}

// Import the data into a RiveFile
let bytes = [UInt8](data)

return data.withUnsafeMutableBytes{(riveBytes:UnsafeMutableRawBufferPointer)->RiveFile in
guard let rawPointer = riveBytes.baseAddress else {
fatalError("File pointer is messed up")
}
let pointer = rawPointer.bindMemory(to: UInt8.self, capacity: bytes.count)

guard let riveFile = RiveFile(bytes:pointer, byteLength: UInt64(bytes.count)) else {
fatalError("Failed to import \(url).")
}
return riveFile
return [UInt8](data)
}

func getRiveFile(resourceName: String, resourceExt: String=".riv") -> RiveFile{
let byteArray = getBytes(resourceName: resourceName, resourceExt: resourceExt)
guard let riveFile = RiveFile(byteArray: byteArray) else {
fatalError("Failed to import Rive File.")
}
return riveFile
}
187 changes: 179 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ iOS runtime for [Rive](https://rive.app/)

This is the Android runtime for [Rive](https://rive.app), currently in beta. The api is subject to change as we continue to improve it. Please file issues and PRs for anything busted, missing, or just wrong.


# Installing rive-ios

## Via github
Expand All @@ -19,13 +18,13 @@ You can clone this repository and include the RiveRuntime.xcodeproj to build a d

When pulling down this repo, you'll need to make sure to also pull down the submodule which contains the C++ runtime that our iOS runtime is built upon. The easiest way to do this is to run this:

```
```sh
git clone --recurse-submodules git@github.com:rive-app/rive-ios
```

When updating, remember to also update the submodule with the same command.

```
```sh
git submodule update --init
```

Expand All @@ -35,16 +34,188 @@ We are in the process of getting a pod available in [cocoapods](https://cocoapod

While we are working out any kinks, we are publishing our pod to a temporary github repo, which you can install by including placeholder while we finalize any kinks.

```
```ruby
pod 'RiveRuntime', :git => 'git@github.com:rive-app/test-ios.git'
```

Once you have installed the pod, you can run `import RiveRuntime` to have access to our higher level views or build on top of our bindings to control your own animation loop.
Once you have installed the pod, you can run

```swift
import RiveRuntime
```

to have access to our higher level views or build on top of our bindings to control your own animation loop.

# Examples

There is an example project next to the runtimes.
There is an example project next to the runtimes.

The examples show simple ways of adding animated views into your app, how to add buttons & slider controls, how to use state machines & how to navigate the contents of a rive file programatically.

To run the example, open the `Rive.xcworkspace` in Xcode and run the `RiveExample` project.

# Overview

We have provided high level Swift controller and a UIkit view to easily add rive into your application. All of this is built ontop of an objective c layer that allows for fine grained granular animation control.

## UIKit

### RiveView

The simplest way of adding a riveView to a controller is probably to just set it as the controllers view when it is loaded.

```swift
class SimpleAnimationViewController: UIViewController {
let resourceName = "truck_v7"

override public func loadView() {
super.loadView()

guard let riveFile = RiveFile(byteArray: getResourceBytes(resourceName: resourceName)) else {
fatalError("Failed to load RiveFile")
}
let view = RiveView(riveFile:riveFile)
self.view = view
}
}
```

The RiveView will autoplay the first animation found in the riveFile, our code for loading the local resource as UInt8 data is here.

```swift
func getResourceBytes(resourceName: String, resourceExt: String=".riv") -> [UInt8] {
guard let url = Bundle.main.url(forResource: resourceName, withExtension: resourceExt) else {
fatalError("Failed to locate \(resourceName) in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(url) from bundle.")
}

// Import the data into a RiveFile
return [UInt8](data)
}
```

The riveView can be further customized to select which animation to play, or how to fit the animation into the view space. A lot of configuration is possible on the RiveView, playback controls are added as functions on the view, and to change which file or artboard is being displayed, use `.configure`.

### Layout

The rive view can be further customized as part of specifying layout attributes.

fit can be specified to determine how the animation should be resized to fit its container. The available choices are `.fitFill` , `.fitContain` , `.fitCover` , `.fitFitWidth` , `.fitFitHeight` , `.fitNone` , `.fitScaleDown`

alignment informs how it should be aligned within the container. The available choices are `alignmentTopLeft`, `alignmentTopCenter`, `alignmentTopRight`, `alignmentCenterLeft`, `alignmentCenter`, `alignmentCenterRight`, `alignmentBottomLeft`, `alignmentBottomCenter`, `alignmentBottomRight`.

This can be specified when instantiating the view

```swift
let view = RiveView(
riveFile:riveFile,
fit: .fitFill,
alignment: .alignmentBottomLeft
)
```

or anytime afterwards.

```swift
view.fit = .fitCover
view.alignment = .alignmentCenter
```

### Playback Controls

The examples show simple ways of adding animated views into your app, how to add buttons & slider controls, how to use state machines & how to navigate the contents of a rive file programatically.
Animations can be controlled in many ways, by default loading a RiveView with a riveFile will autoplay the first animation on the first artboard. The artboard and animation can be specified by name here.

To run the example, open the `Rive.xcworkspace` in Xcode and run the `RiveExample` project.
```swift
let riveView = RiveView(
riveFile: riveFile,
fit: .fitContain,
alignment: .alignmentCenter,
artboard: "Square",
animation: "rollaround",
autoplay: true
)
```

furthermore animations can be controlled later too:

To play an animation named rollaround.

```swift
riveView.play(animationName: "rollaround")
```

multiple animations can play at the same time, and additional animations can be added at any time

```swift
riveView.play(
animationNames: ["bouncing", "windshield_wipers"]
)
```

When playing animations, the Loop Mode and direction of the animations can also be set per animation.

```swift
riveView.play(
animationNames: ["bouncing", "windshield_wipers"],
loop: .loopOneShot,
direction: .directionBackwards
)
```

Similarly animations can be paused, or stopped, either all at the same time, or one by one.

```swift
riveView.stop()
riveView.stop(animationName:"bouncing")
riveView.stop(animationNames:["bouncing", "windshield_wipers"])
```

```swift
riveView.pause()
riveView.pause(animationName:"bouncing")
riveView.pause(animationNames:["bouncing", "windshield_wipers"])
```

### Mixing

Mixing goes further than just playing multiple animations at the same time, animations can use a mix factor between 0 and 1, to allow multiple animations effects to blend together. The high level views do not expose this currently. but you can wrap your own render loop around the core libraries. The advance function is where you can specify a mix factor.

### Delegates & Events

The rive ios runtimes allow for delegates that can be provided to the RiveView. If provided these delegates will be fired whenever a matching event is triggered.

There are the following delegates `LoopDelegate`, `PlayDelegate`, `PauseDelegate`, `StopDelegate`, `StateChangeDelegate`

You can crete your own delegate like this, implementing as many protocols are are needed.

```swift
class MyDelegate: PlayDelegate, LoopDelegate {
func loop(_ animationName: String, type: Int) {
// do things when the animation loops playing.
}

func play(_ animationName: String, isStateMachine: Bool) {
// do things when the animation starts playing.
}
}
```

To use a delegate simply pass it to the view on instantiation

```swift
let delegate = MyDelegate()

let view = RiveView(
riveFile:riveFile,
loopDelegate: delegate,
playDelegate: delegate,
)
```

or attach it later

```swift
view.loopDelegate = delegate
```
64 changes: 51 additions & 13 deletions Source/Renderer/RiveFile.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,71 @@
#import <Rive.h>
#import <RivePrivateHeaders.h>

@interface RiveFile ()

- (rive::BinaryReader) getReader:(UInt8 *)bytes byteLength:(UInt64)length;
- (void) import:(rive::BinaryReader)reader;

@end

/*
* RiveFile
*/
@implementation RiveFile {
rive::File* riveFile;
}

- (rive::BinaryReader) getReader:(UInt8 *)bytes byteLength:(UInt64)length {
return rive::BinaryReader(bytes, length);
}


+ (uint)majorVersion { return UInt8(rive::File::majorVersion); }
+ (uint)minorVersion { return UInt8(rive::File::minorVersion); }

- (nullable instancetype)initWithBytes:(UInt8 *)bytes byteLength:(UInt64)length {
- (void) import:(rive::BinaryReader)reader {
rive::ImportResult result = rive::File::import(reader, &riveFile);
if (result == rive::ImportResult::success) {
return;
}
else if(result == rive::ImportResult::unsupportedVersion){
@throw [[RiveException alloc] initWithName:@"UnsupportedVersion" reason:@"Unsupported Rive File Version." userInfo:nil];

}
else if(result == rive::ImportResult::malformed){
@throw [[RiveException alloc] initWithName:@"Malformed" reason:@"Malformed Rive File." userInfo:nil];
}
else {
@throw [[RiveException alloc] initWithName:@"Unknown" reason:@"Unknown error loading file." userInfo:nil];
}
}

- (nullable instancetype)initWithByteArray:(NSArray *)array {
if (self = [super init]) {
rive::BinaryReader reader = rive::BinaryReader(bytes, length);
rive::ImportResult result = rive::File::import(reader, &riveFile);
if (result == rive::ImportResult::success) {
return self;
}
else if(result == rive::ImportResult::unsupportedVersion){
@throw [[RiveException alloc] initWithName:@"UnsupportedVersion" reason:@"Unsupported Rive File Version." userInfo:nil];
UInt8* bytes;
@try {
bytes = (UInt8*)calloc(array.count, sizeof(UInt64));

[array enumerateObjectsUsingBlock:^(NSNumber* number, NSUInteger index, BOOL* stop){
bytes[index] = number.unsignedIntValue;
}];
rive::BinaryReader reader = [self getReader:bytes byteLength:array.count];
[self import:reader];
}
else if(result == rive::ImportResult::malformed){
@throw [[RiveException alloc] initWithName:@"Malformed" reason:@"Malformed Rive File." userInfo:nil];
}
else {
@throw [[RiveException alloc] initWithName:@"Unknown" reason:@"Unknown error loading file." userInfo:nil];
@finally {
free(bytes);
}

return self;
}
return nil;
}

- (nullable instancetype)initWithBytes:(UInt8 *)bytes byteLength:(UInt64)length {
if (self = [super init]) {
rive::BinaryReader reader = [self getReader:bytes byteLength:length];
[self import:reader];
return self;
}
return nil;
}
Expand Down
1 change: 1 addition & 0 deletions Source/Renderer/include/RiveFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (class, readonly) uint majorVersion;
@property (class, readonly) uint minorVersion;

- (nullable instancetype)initWithByteArray:(NSArray *)bytes;
- (nullable instancetype)initWithBytes:(UInt8 *)bytes byteLength:(UInt64)length;

// Returns a reference to the default artboard
Expand Down
Loading

0 comments on commit 4adeba6

Please sign in to comment.