Skip to content

Commit

Permalink
feat: add responsive layouts to iOS and JS
Browse files Browse the repository at this point in the history
  • Loading branch information
HayesGordon committed Nov 7, 2024
1 parent ba12e7a commit 5acf085
Show file tree
Hide file tree
Showing 15 changed files with 143 additions and 21 deletions.
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 added example/ios/Assets/layout_test.riv
Binary file not shown.
Binary file added example/ios/Assets/layouts_demo.riv
Binary file not shown.
8 changes: 4 additions & 4 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -489,8 +489,8 @@ PODS:
- React-perflogger (= 0.72.7)
- rive-react-native (8.0.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: ddcb4870e2cded3eab74b3b2a05303eedf94eec7
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 @@ -34,6 +34,8 @@
E5FC4EAA2ABB975100D98158 /* rating.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5FC4EA92ABB975100D98158 /* rating.riv */; };
F8AA4CA42C0F3FDB00C1A5FF /* runtime_nested_inputs.riv in Resources */ = {isa = PBXBuildFile; fileRef = F8AA4CA32C0F3FDB00C1A5FF /* runtime_nested_inputs.riv */; };
F8AA4CA52C0F3FDB00C1A5FF /* runtime_nested_inputs.riv in Resources */ = {isa = PBXBuildFile; fileRef = F8AA4CA32C0F3FDB00C1A5FF /* runtime_nested_inputs.riv */; };
F8E2789E2CDCD6A200FAA8EF /* layouts_demo.riv in Resources */ = {isa = PBXBuildFile; fileRef = F8E2789D2CDCD6A200FAA8EF /* layouts_demo.riv */; };
F8E278A22CDCDAFA00FAA8EF /* layout_test.riv in Resources */ = {isa = PBXBuildFile; fileRef = F8E278A12CDCDAFA00FAA8EF /* layout_test.riv */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -104,6 +106,8 @@
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>"; };
F8E278A12CDCDAFA00FAA8EF /* layout_test.riv */ = {isa = PBXFileReference; lastKnownFileType = file; name = layout_test.riv; path = ../../../../../../rive/rive/packages/runtime_wasm/js/examples/_frameworks/layout_example/assets/layout_test.riv; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -250,6 +254,8 @@
9D4FE60A26493B460098BF6A /* Assets */ = {
isa = PBXGroup;
children = (
F8E278A12CDCDAFA00FAA8EF /* layout_test.riv */,
F8E2789D2CDCD6A200FAA8EF /* layouts_demo.riv */,
C3C07471283BE07300E8EB33 /* hero_editor.riv */,
E554409A2A79DC8100D550DE /* hello_world_text.riv */,
042FD22626B81BD1004556A3 /* constrained.riv */,
Expand Down Expand Up @@ -363,6 +369,7 @@
TestTargetID = 13B07F861A680F5B00A75B9A;
};
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = V7DMK4826J;
LastSwiftMigration = 1120;
};
2D02E47A1E0B4A5D006451C7 = {
Expand Down Expand Up @@ -421,9 +428,11 @@
9DBF1CC52684937E0008391A /* v6_file.riv in Resources */,
9D4FE6122649427F0098BF6A /* bird.riv in Resources */,
E554409B2A79DC8100D550DE /* hello_world_text.riv in Resources */,
F8E278A22CDCDAFA00FAA8EF /* layout_test.riv in Resources */,
9D879D0D265BF2A400D01424 /* ui_swipe_left_to_delete.riv in Resources */,
E5A17A90299AA0F5008CC433 /* avatars.riv in Resources */,
9D879D0926578A5400D01424 /* loopy.riv in Resources */,
F8E2789E2CDCD6A200FAA8EF /* layouts_demo.riv in Resources */,
C3C07472283BE07300E8EB33 /* hero_editor.riv in Resources */,
E59C19102B07F6FE002F0CBA /* nested_menu.riv in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets 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;
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
81 changes: 81 additions & 0 deletions example/src/ResponsiveLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import * as React from 'react';
import { SafeAreaView, StyleSheet, View, Button, Text } from 'react-native';
import Rive, { Fit } from 'rive-react-native';

// const resourceName = 'layouts_demo';
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, Rive will automatically scale the layout to match the device pixel ratio
resourceName={resourceName}
/>
<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

@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
}
}

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
1 change: 1 addition & 0 deletions ios/RiveReactNativeViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ @interface RCT_EXTERN_MODULE(RiveReactNativeViewManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(resourceName, NSString)
RCT_EXPORT_VIEW_PROPERTY(url, NSString)
RCT_EXPORT_VIEW_PROPERTY(fit, NSString)
RCT_EXPORT_VIEW_PROPERTY(layoutScaleFactor, NSNumber)
RCT_EXPORT_VIEW_PROPERTY(alignment, NSString)
RCT_EXPORT_VIEW_PROPERTY(autoplay, BOOL)
RCT_EXPORT_VIEW_PROPERTY(artboardName, NSString)
Expand Down
2 changes: 1 addition & 1 deletion rive-react-native.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ Pod::Spec.new do |s|
s.swift_version = "5.0"

s.dependency "React-Core"
s.dependency "RiveRuntime", "6.2.1"
s.dependency "RiveRuntime", "6.3.3"
end
4 changes: 4 additions & 0 deletions src/Rive.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type RiveProps = {
isUserHandlingErrors: boolean;
autoplay?: boolean;
fit: Fit;
layoutScaleFactor?: number;
alignment: Alignment;
artboardName?: string;
animationName?: string;
Expand All @@ -98,6 +99,7 @@ type Props = {
onRiveEventReceived?: (event: RiveGeneralEvent | RiveOpenUrlEvent) => void;
onError?: (rnRiveError: RNRiveError) => void;
fit?: Fit;
layoutScaleFactor?: number;
style?: ViewStyle;
testID?: string;
alignment?: Alignment;
Expand Down Expand Up @@ -127,6 +129,7 @@ const RiveContainer = React.forwardRef<RiveRef, Props>(
url,
alignment = Alignment.Center,
fit = Fit.Contain,
layoutScaleFactor,
artboardName,
animationName,
stateMachineName,
Expand Down Expand Up @@ -416,6 +419,7 @@ const RiveContainer = React.forwardRef<RiveRef, Props>(
isUserHandlingErrors={isUserHandlingErrors}
autoplay={autoplay}
fit={fit}
layoutScaleFactor={layoutScaleFactor}
url={url}
style={styles.animation}
onPlay={onPlayHandler}
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export enum Fit {
FitHeight = 'fitHeight',
None = 'none',
ScaleDown = 'scaleDown',
Layout = 'layout',
}

export enum Alignment {
Expand Down

0 comments on commit 5acf085

Please sign in to comment.