Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add responsive layouts #273

Merged
merged 8 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ dependencies {
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1'
implementation 'app.rive:rive-android:9.9.5'
implementation 'app.rive:rive-android:9.10.1'
implementation "androidx.startup:startup-runtime:1.1.1"
implementation 'com.android.volley:volley:1.2.0'
}
4 changes: 3 additions & 1 deletion android/src/main/java/com/rivereactnative/RNFit.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ enum class RNFit(private val mValue: String) {
FitWidth("fitWidth"),
FitHeight("fitHeight"),
None("none"),
ScaleDown("scaleDown");
ScaleDown("scaleDown"),
Layout("layout");

override fun toString(): String {
return mValue
Expand All @@ -30,6 +31,7 @@ enum class RNFit(private val mValue: String) {
FitHeight -> Fit.FIT_HEIGHT
None -> Fit.NONE
ScaleDown -> Fit.SCALE_DOWN
Layout -> Fit.LAYOUT
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout
private var stateMachineName: String? = null
private var artboardName: String? = null
private var fit: Fit = Fit.CONTAIN
private var layoutScaleFactor: Float? = null
private var alignment: Alignment = Alignment.CENTER
private var autoplay: Boolean = false;
private var shouldBeReloaded = true
Expand Down Expand Up @@ -339,6 +340,11 @@ class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout
riveAnimationView.fit = riveFit
}

fun setLayoutScaleFactor(layoutScaleFactor: Float?) {
this.layoutScaleFactor = layoutScaleFactor
riveAnimationView.layoutScaleFactor = layoutScaleFactor
}

fun setAlignment(rnAlignment: RNAlignment) {
val riveAlignment = RNAlignment.mapToRiveAlignment(rnAlignment)
this.alignment = riveAlignment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,17 @@ class RiveReactNativeViewManager : SimpleViewManager<RiveReactNativeView>() {
view.setFit(RNFit.mapToRNFit(fit))
}

@ReactProp(name = "layoutScaleFactor")
fun setLayoutScaleFactor(view: RiveReactNativeView, layoutScaleFactor: Double) {
if (!layoutScaleFactor.isNaN() && layoutScaleFactor > 0) {
// Only set layoutScaleFactor if it's a valid positive float
view.setLayoutScaleFactor(layoutScaleFactor.toFloat())
} else {
// Handle other cases, e.g., NaN, -1, or other non-float-like values
view.setLayoutScaleFactor(null)
}
}

@ReactProp(name = "alignment")
fun setAlignment(view: RiveReactNativeView, alignment: String) {
view.setAlignment(RNAlignment.mapToRNAlignment(alignment))
Expand Down
1 change: 1 addition & 0 deletions docs/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export enum Fit {
FitHeight = 'fitHeight',
None = 'none',
ScaleDown = 'scaleDown',
Layout = 'layout',
}
```

Expand Down
Binary file not shown.
Binary file added example/ios/Assets/layout_test.riv
Binary file not shown.
10 changes: 5 additions & 5 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -487,10 +487,10 @@ PODS:
- React-jsi (= 0.72.7)
- React-logger (= 0.72.7)
- React-perflogger (= 0.72.7)
- rive-react-native (8.0.0):
- rive-react-native (8.1.0):
- React-Core
- RiveRuntime (= 6.2.1)
- RiveRuntime (6.2.1)
- RiveRuntime (= 6.3.3)
- RiveRuntime (6.3.3)
- RNCMaskedView (0.2.9):
- React-Core
- RNCPicker (1.16.8):
Expand Down Expand Up @@ -744,8 +744,8 @@ SPEC CHECKSUMS:
React-runtimescheduler: 7649c3b46c8dee1853691ecf60146a16ae59253c
React-utils: 56838edeaaf651220d1e53cd0b8934fb8ce68415
ReactCommon: 5f704096ccf7733b390f59043b6fa9cc180ee4f6
rive-react-native: 555505d7a84182ea159d5856bcda8ba01057f256
RiveRuntime: 23b2f53b5439c38f716b2f1a5d2a9e3a24683877
rive-react-native: c0cf3d1b9e5443b36b91f14d515dea0c24477707
RiveRuntime: 4555cc58cf39f41c84b4152f2272b18068da43a1
RNCMaskedView: 949696f25ec596bfc697fc88e6f95cf0c79669b6
RNCPicker: 0991c56da7815c0cf946d6f63cf920b25296e5f6
RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211
Expand Down
15 changes: 13 additions & 2 deletions example/ios/RiveReactNativeExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
9D879D0B26578A5E00D01424 /* artboard_animations.riv in Resources */ = {isa = PBXBuildFile; fileRef = 9D879D0A26578A5E00D01424 /* artboard_animations.riv */; };
9D879D0D265BF2A400D01424 /* ui_swipe_left_to_delete.riv in Resources */ = {isa = PBXBuildFile; fileRef = 9D879D0C265BF2A400D01424 /* ui_swipe_left_to_delete.riv */; };
9DBF1CC52684937E0008391A /* v6_file.riv in Resources */ = {isa = PBXBuildFile; fileRef = 9DBF1CC42684937E0008391A /* v6_file.riv */; };
9EBE42F22CDD459200014668 /* layout_test.riv in Resources */ = {isa = PBXBuildFile; fileRef = 9EBE42F12CDD459200014668 /* layout_test.riv */; };
C3C07472283BE07300E8EB33 /* hero_editor.riv in Resources */ = {isa = PBXBuildFile; fileRef = C3C07471283BE07300E8EB33 /* hero_editor.riv */; };
E554409B2A79DC8100D550DE /* hello_world_text.riv in Resources */ = {isa = PBXBuildFile; fileRef = E554409A2A79DC8100D550DE /* hello_world_text.riv */; };
E5637D7A292BD27F000CBC1E /* skills_listener.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5637D79292BD26D000CBC1E /* skills_listener.riv */; };
Expand Down Expand Up @@ -87,6 +88,8 @@
9D879D0A26578A5E00D01424 /* artboard_animations.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = artboard_animations.riv; sourceTree = "<group>"; };
9D879D0C265BF2A400D01424 /* ui_swipe_left_to_delete.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = ui_swipe_left_to_delete.riv; sourceTree = "<group>"; };
9DBF1CC42684937E0008391A /* v6_file.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = v6_file.riv; sourceTree = "<group>"; };
9EBE42EF2CDD293B00014668 /* layout_test.riv */ = {isa = PBXFileReference; lastKnownFileType = file; name = layout_test.riv; path = Assets/layout_test.riv; sourceTree = "<group>"; };
9EBE42F12CDD459200014668 /* layout_test.riv */ = {isa = PBXFileReference; lastKnownFileType = file; name = layout_test.riv; path = Assets/layout_test.riv; sourceTree = "<group>"; };
B4756AFE35A048338799A25D /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = "<group>"; };
B67E7AEF25D549FDAA873610 /* FontAwesome5_Brands.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome5_Brands.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf"; sourceTree = "<group>"; };
C3C07471283BE07300E8EB33 /* hero_editor.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = hero_editor.riv; sourceTree = "<group>"; };
Expand All @@ -104,6 +107,7 @@
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
F8AA4CA32C0F3FDB00C1A5FF /* runtime_nested_inputs.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = runtime_nested_inputs.riv; sourceTree = "<group>"; };
F8E2789D2CDCD6A200FAA8EF /* layouts_demo.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = layouts_demo.riv; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -222,6 +226,8 @@
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
9EBE42EF2CDD293B00014668 /* layout_test.riv */,
9EBE42F12CDD459200014668 /* layout_test.riv */,
9D4FE60A26493B460098BF6A /* Assets */,
13B07FAE1A68108700A75B9A /* RiveReactNativeExample */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
Expand Down Expand Up @@ -250,6 +256,7 @@
9D4FE60A26493B460098BF6A /* Assets */ = {
isa = PBXGroup;
children = (
F8E2789D2CDCD6A200FAA8EF /* layouts_demo.riv */,
C3C07471283BE07300E8EB33 /* hero_editor.riv */,
E554409A2A79DC8100D550DE /* hello_world_text.riv */,
042FD22626B81BD1004556A3 /* constrained.riv */,
Expand Down Expand Up @@ -363,6 +370,7 @@
TestTargetID = 13B07F861A680F5B00A75B9A;
};
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = V7DMK4826J;
LastSwiftMigration = 1120;
};
2D02E47A1E0B4A5D006451C7 = {
Expand Down Expand Up @@ -416,6 +424,7 @@
9D879D0B26578A5E00D01424 /* artboard_animations.riv in Resources */,
F8AA4CA42C0F3FDB00C1A5FF /* runtime_nested_inputs.riv in Resources */,
E5637D7A292BD27F000CBC1E /* skills_listener.riv in Resources */,
9EBE42F22CDD459200014668 /* layout_test.riv in Resources */,
04A886F326A990050078530A /* two_bone_ik.riv in Resources */,
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
9DBF1CC52684937E0008391A /* v6_file.riv in Resources */,
Expand Down Expand Up @@ -716,6 +725,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = V7DMK4826J;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = RiveReactNativeExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
Expand All @@ -724,7 +734,7 @@
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.rivereactnative;
PRODUCT_BUNDLE_IDENTIFIER = gordon.example.rivereactnative;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably fine, just calling this out as a thing that's been committed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I forgot this, will remove

PRODUCT_NAME = RiveReactNativeExample;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
Expand All @@ -739,14 +749,15 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = V7DMK4826J;
INFOPLIST_FILE = RiveReactNativeExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.rivereactnative;
PRODUCT_BUNDLE_IDENTIFIER = gordon.example.rivereactnative;
PRODUCT_NAME = RiveReactNativeExample;
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
Expand Down
28 changes: 14 additions & 14 deletions example/ios/RiveReactNativeExample/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,6 @@
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIAppFonts</key>
<array>
<string>AntDesign.ttf</string>
Expand All @@ -72,5 +58,19 @@
<string>Zocial.ttf</string>
<string>Fontisto.ttf</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
2 changes: 2 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
RiveRendererAndroid,
RiveRendererIOS,
} from 'rive-react-native';
import ResponsiveLayout from './ResponsiveLayout';

const Stack = createStackNavigator();

Expand Down Expand Up @@ -57,6 +58,7 @@ function App() {
<Stack.Screen name="Simple" component={Simple} />
<Stack.Screen name="SimpleControls" component={SimpleControls} />
<Stack.Screen name="Layout" component={Layout} />
<Stack.Screen name="ResponsiveLayout" component={ResponsiveLayout} />
<Stack.Screen name="Http" component={Http} />
<Stack.Screen name="MeshExample" component={MeshExample} />
<Stack.Screen name="StateMachine" component={StateMachine} />
Expand Down
8 changes: 8 additions & 0 deletions example/src/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ export default function Home({ navigation }) {
Layout
</Button>

<Button
mode="contained"
onPress={() => navigation.navigate('ResponsiveLayout')}
style={styles.buttonStyle}
>
Responsive Layout
</Button>

<Button
mode="contained"
onPress={() => navigation.navigate('SimpleControls')}
Expand Down
82 changes: 82 additions & 0 deletions example/src/ResponsiveLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as React from 'react';
import { SafeAreaView, StyleSheet, View, Button, Text } from 'react-native';
import Rive, { Fit } from 'rive-react-native';

const resourceName = 'layout_test';

export default function ResponsiveLayout() {
const [scaleFactor, setScaleFactor] = React.useState(4.0);

const increaseScale = () => setScaleFactor((prev) => prev + 0.5);
const decreaseScale = () =>
setScaleFactor((prev) => Math.max(0.5, prev - 0.5));

return (
<SafeAreaView style={styles.safeAreaViewContainer}>
<Rive
autoplay={true}
style={styles.animation}
fit={Fit.Layout}
layoutScaleFactor={scaleFactor} // If you do not set this (or set equal to "-1.0"), Rive will automatically scale the layout to match the device pixel ratio
resourceName={resourceName}
artboardName={'Artboard'}
stateMachineName={'State Machine 1'}
/>
<View style={styles.controls}>
<Text style={styles.label}>Layout Scale Factor</Text>
<View style={styles.scaleControls}>
<Button title="-" onPress={decreaseScale} />
<View style={styles.scaleText}>
<Text>{scaleFactor.toFixed(1)}x</Text>
</View>
<Button title="+" onPress={increaseScale} />
</View>
</View>
</SafeAreaView>
);
}

const styles = StyleSheet.create({
safeAreaViewContainer: {
flex: 1,
},
animation: {
width: '100%',
flex: 1,
},
controls: {
padding: 16,
alignItems: 'center',
},
picker: {
flex: 1,
width: '100%',
},
pickerWrapper: {
borderWidth: 1,
borderColor: 'black',
borderRadius: 5,
alignItems: 'center',
margin: 16,
},
pickersWrapper: {
flex: 1,
padding: 16,
alignSelf: 'stretch',
},
scaleControls: {
flexDirection: 'row',
alignItems: 'center',
marginVertical: 16,
gap: 16,
},
scaleText: {
minWidth: 50,
alignItems: 'center',
},
label: {
fontSize: 16,
fontWeight: '500',
marginTop: 16,
},
});
3 changes: 3 additions & 0 deletions ios/RNFit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ enum RNFit: String {
case FitHeight = "fitHeight"
case None = "none"
case ScaleDown = "scaleDown"
case Layout = "layout"

static func mapToRNFit(value: String) -> RNFit {
if let rnEnum = RNFit(rawValue: value) {
Expand All @@ -34,6 +35,8 @@ enum RNFit: String {
return RiveFit.noFit
case .ScaleDown:
return RiveFit.scaleDown
case .Layout:
return RiveFit.layout
}
}
}
10 changes: 10 additions & 0 deletions ios/RiveReactNativeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate

@objc var fit: String?

@objc var layoutScaleFactor: NSNumber = -1.0 // -1.0 will inform the iOS runtime to determine the correct scale factor automatically
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type is a Double in Swift (and likely ObjC by interop). What's the decision behind using NSNumber?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double doesn't work for nullable(optional) values in these bindings: https://reactnative.dev/docs/legacy/native-modules-ios#argument-types


@objc var alignment: String?

@objc var autoplay: Bool
Expand Down Expand Up @@ -99,6 +101,10 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
if (changedProps.contains("alignment")) {
viewModel?.alignment = convertAlignment(alignment)
}

if (changedProps.contains("layoutScaleFactor")) {
viewModel?.layoutScaleFactor = layoutScaleFactor.doubleValue
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, usage is here as a double value. But, still curious!

}
}

private func convertFit(_ fit: String? = nil) -> RiveFit {
Expand Down Expand Up @@ -143,6 +149,8 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
updatedViewModel = RiveViewModel(fileName: name, fit: convertFit(fit), alignment: convertAlignment(alignment), autoPlay: autoplay, artboardName: artboardName)
}

updatedViewModel.layoutScaleFactor = layoutScaleFactor.doubleValue

createNewView(updatedViewModel: updatedViewModel)
requiresLocalResourceReconfigure = false
}
Expand All @@ -161,6 +169,8 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
} else {
updatedViewModel = RiveViewModel(webURL: url, fit: convertFit(fit), alignment: convertAlignment(alignment), autoPlay: autoplay, artboardName: artboardName)
}

updatedViewModel.layoutScaleFactor = layoutScaleFactor.doubleValue

createNewView(updatedViewModel: updatedViewModel)
}
Expand Down
Loading
Loading