Skip to content

Commit 24d88f7

Browse files
t0maborokmichalikk
authored andcommitted
feat(iOS, SplitView): Add support for preferredSplitBehavior prop (#241)
## Description Adding a basic setup for props for `SplitViewHost` and a support for the prop that's telling how columns will appear relatively to each other. The possible values for this prop are: - `tile` - the columns appears side-by-side - `overlay` - the sidebars are partially covering main column - `displace` - the main column is moved partially offscreen, instead of overlapping it ## Changes - Added prop definition to the native component on the JS side - Added basic logic for props updates in `SplitViewHostComponentView` - Added necessary enums and conversions ## Test code and steps to reproduce New prop was added to the `SplitViewBaseApp` ## Checklist - [x] Included code example that can be used to test this change - [x] Ensured that CI passes Closes: #224 --------- Co-authored-by: kmichalikk <[email protected]>
1 parent 80f6d26 commit 24d88f7

File tree

9 files changed

+168
-12
lines changed

9 files changed

+168
-12
lines changed

apps/src/tests/TestSplitView/SplitViewBaseApp.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const SplitViewBaseApp = () => {
2323
const [buttonState4, setButtonState4] = React.useState('Initial');
2424

2525
return (
26-
<SplitViewHost>
26+
<SplitViewHost splitBehavior='tile' primaryEdge='leading'>
2727
<SplitViewScreen>
2828
<View style={[styles.container, { backgroundColor: Colors.RedDark100 }]}>
2929
<TestButton setButtonState={setButtonState} />

ios/RNSEnums.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,20 @@ typedef NS_ENUM(NSInteger, RNSSearchBarPlacement) {
7373
RNSSearchBarPlacementStacked,
7474
};
7575

76+
#pragma mark - SplitView
77+
78+
typedef NS_ENUM(NSInteger, RNSSplitViewSplitBehavior) {
79+
RNSSplitViewSplitBehaviorAutomatic,
80+
RNSSplitViewSplitBehaviorDisplace,
81+
RNSSplitViewSplitBehaviorOverlay,
82+
RNSSplitViewSplitBehaviorTile,
83+
};
84+
85+
typedef NS_ENUM(NSInteger, RNSSplitViewPrimaryEdge) {
86+
RNSSplitViewPrimaryEdgeLeading,
87+
RNSSplitViewPrimaryEdgeTrailing
88+
};
89+
7690
// Redefinition of UIBlurEffectStyle. We need to represent additional case of `None`.
7791
typedef NS_ENUM(NSInteger, RNSBlurEffectStyle) {
7892
/// No blur effect should be visible
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#import "RNSConversions.h"
2+
3+
namespace rnscreens::conversion {
4+
5+
#pragma mark RN Codegen enum to internal enum conversions
6+
7+
RNSSplitViewSplitBehavior RNSSplitViewSplitBehaviorFromHostProp(
8+
facebook::react::RNSSplitViewHostSplitBehavior splitBehavior)
9+
{
10+
using enum facebook::react::RNSSplitViewHostSplitBehavior;
11+
12+
switch (splitBehavior) {
13+
case Displace:
14+
return RNSSplitViewSplitBehaviorDisplace;
15+
case Overlay:
16+
return RNSSplitViewSplitBehaviorOverlay;
17+
case Tile:
18+
return RNSSplitViewSplitBehaviorTile;
19+
case Automatic:
20+
default:
21+
return RNSSplitViewSplitBehaviorAutomatic;
22+
}
23+
}
24+
25+
RNSSplitViewPrimaryEdge RNSSplitViewPrimaryEdgeFromHostProp(
26+
facebook::react::RNSSplitViewHostPrimaryEdge primaryEdge)
27+
{
28+
using enum facebook::react::RNSSplitViewHostPrimaryEdge;
29+
30+
switch (primaryEdge) {
31+
case Trailing:
32+
return RNSSplitViewPrimaryEdgeTrailing;
33+
case Leading:
34+
default:
35+
return RNSSplitViewPrimaryEdgeLeading;
36+
}
37+
}
38+
39+
#pragma mark Internal enum to UISplitViewController enum conversions
40+
41+
UISplitViewControllerSplitBehavior RNSSplitBehaviorToUISplitViewControllerSplitBehavior(
42+
RNSSplitViewSplitBehavior behavior)
43+
{
44+
switch (behavior) {
45+
case RNSSplitViewSplitBehaviorDisplace:
46+
return UISplitViewControllerSplitBehaviorDisplace;
47+
case RNSSplitViewSplitBehaviorOverlay:
48+
return UISplitViewControllerSplitBehaviorOverlay;
49+
case RNSSplitViewSplitBehaviorTile:
50+
return UISplitViewControllerSplitBehaviorTile;
51+
case RNSSplitViewSplitBehaviorAutomatic:
52+
default:
53+
return UISplitViewControllerSplitBehaviorAutomatic;
54+
}
55+
}
56+
57+
UISplitViewControllerPrimaryEdge RNSPrimaryEdgeToUISplitViewControllerPrimaryEdge(
58+
RNSSplitViewPrimaryEdge primaryEdge)
59+
{
60+
switch (primaryEdge) {
61+
case RNSSplitViewPrimaryEdgeTrailing:
62+
return UISplitViewControllerPrimaryEdgeTrailing;
63+
case RNSSplitViewPrimaryEdgeLeading:
64+
default:
65+
return UISplitViewControllerPrimaryEdgeLeading;
66+
}
67+
}
68+
69+
}; // namespace rnscreens::conversion

ios/conversion/RNSConversions.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ UIOffset RNSBottomTabsScreenTabBarItemTitlePositionAdjustmentStruct(
3030
react::RNSBottomTabsScreenTabBarItemTitlePositionAdjustmentStruct
3131
titlePositionAdjustment);
3232

33+
#pragma mark SplitView
34+
35+
RNSSplitViewSplitBehavior RNSSplitViewSplitBehaviorFromHostProp(
36+
react::RNSSplitViewHostSplitBehavior);
37+
38+
RNSSplitViewPrimaryEdge RNSSplitViewPrimaryEdgeFromHostProp(
39+
react::RNSSplitViewHostPrimaryEdge);
40+
41+
UISplitViewControllerSplitBehavior
42+
RNSSplitBehaviorToUISplitViewControllerSplitBehavior(
43+
RNSSplitViewSplitBehavior behavior);
44+
45+
UISplitViewControllerPrimaryEdge
46+
RNSPrimaryEdgeToUISplitViewControllerPrimaryEdge(
47+
RNSSplitViewPrimaryEdge primaryEdge);
3348
}; // namespace rnscreens::conversion
3449

3550
#endif

ios/gamma/split-view/RNSSplitViewHostComponentView.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#import "RNSEnums.h"
12
#import "RNSReactBaseView.h"
23

34
NS_ASSUME_NONNULL_BEGIN
@@ -13,4 +14,13 @@ NS_ASSUME_NONNULL_BEGIN
1314

1415
@end
1516

17+
#pragma mark - Props
18+
19+
@interface RNSSplitViewHostComponentView ()
20+
21+
@property (nonatomic, readonly) RNSSplitViewSplitBehavior splitBehavior;
22+
@property (nonatomic, readonly) RNSSplitViewPrimaryEdge primaryEdge;
23+
24+
@end
25+
1626
NS_ASSUME_NONNULL_END

ios/gamma/split-view/RNSSplitViewHostComponentView.mm

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#import <React/UIView+React.h>
55
#import <react/renderer/components/rnscreens/ComponentDescriptors.h>
66

7+
#import "RNSConversions.h"
78
#import "RNSDefines.h"
89
#import "RNSSplitViewScreenComponentView.h"
910
#import "Swift-Bridging.h"
@@ -30,6 +31,7 @@ - (instancetype)initWithFrame:(CGRect)frame
3031

3132
- (void)initState
3233
{
34+
[self resetProps];
3335
// TODO: For now I'm hardcoding style in init, but style cannot be updated outside controller's constructor, thus
3436
// we'll need to delay the initialization unitl Screen components will be mounted
3537
_controller =
@@ -39,6 +41,15 @@ - (void)initState
3941
_reactSubviews = [NSMutableArray new];
4042
}
4143

44+
- (void)resetProps
45+
{
46+
static const auto defaultProps = std::make_shared<const react::RNSSplitViewHostProps>();
47+
_props = defaultProps;
48+
49+
_splitBehavior = RNSSplitViewSplitBehaviorAutomatic;
50+
_primaryEdge = RNSSplitViewPrimaryEdgeLeading;
51+
}
52+
4253
- (void)didMoveToWindow
4354
{
4455
RCTAssert(_controller != nil, @"[RNScreens] Controller must not be nil while attaching to window");
@@ -122,6 +133,27 @@ + (BOOL)shouldBeRecycled
122133
return NO;
123134
}
124135

136+
- (void)updateProps:(const facebook::react::Props::Shared &)props
137+
oldProps:(const facebook::react::Props::Shared &)oldProps
138+
{
139+
const auto &oldComponentProps = *std::static_pointer_cast<const react::RNSSplitViewHostProps>(_props);
140+
const auto &newComponentProps = *std::static_pointer_cast<const react::RNSSplitViewHostProps>(props);
141+
142+
if (oldComponentProps.splitBehavior != newComponentProps.splitBehavior) {
143+
_splitBehavior = rnscreens::conversion::RNSSplitViewSplitBehaviorFromHostProp(newComponentProps.splitBehavior);
144+
_controller.preferredSplitBehavior =
145+
rnscreens::conversion::RNSSplitBehaviorToUISplitViewControllerSplitBehavior(_splitBehavior);
146+
}
147+
148+
if (oldComponentProps.primaryEdge != newComponentProps.primaryEdge) {
149+
_primaryEdge = rnscreens::conversion::RNSSplitViewPrimaryEdgeFromHostProp(newComponentProps.primaryEdge);
150+
_controller.primaryEdge =
151+
rnscreens::conversion::RNSPrimaryEdgeToUISplitViewControllerPrimaryEdge(_primaryEdge);
152+
}
153+
154+
[super updateProps:props oldProps:oldProps];
155+
}
156+
125157
#pragma mark - RCTMountingTransactionObserving
126158

127159
- (void)mountingTransactionWillMount:(const facebook::react::MountingTransaction &)transaction

ios/gamma/split-view/RNSSplitViewHostController.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,6 @@ public class RNSSplitViewHostController: UISplitViewController, ReactMountingTra
4747

4848
viewControllers = currentViewControllers
4949

50-
// TODO: to be removed - only for testing purposes for inspector column
51-
// if #available(iOS 26.0, *) {
52-
// setViewController(currentViewControllers.last, for: .inspector)
53-
// }
54-
5550
for controller in currentViewControllers {
5651
controller.viewFrameOriginChangeObserver = self
5752
}

src/components/gamma/SplitViewHost.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,27 @@ import React from 'react';
22
import { StyleSheet } from 'react-native';
33
import type { ViewProps } from 'react-native';
44
import SplitViewHostNativeComponent from '../../fabric/gamma/SplitViewHostNativeComponent';
5-
import type { NativeProps } from '../../fabric/gamma/SplitViewHostNativeComponent';
5+
import type {
6+
NativeProps,
7+
SplitViewSplitBehavior,
8+
} from '../../fabric/gamma/SplitViewHostNativeComponent';
69

710
export type SplitViewNativeProps = NativeProps & {
8-
// Overrides
11+
// SplitView appearance
12+
13+
splitBehavior?: SplitViewSplitBehavior;
914
};
1015

1116
type SplitViewHostProps = {
1217
children?: ViewProps['children'];
1318
} & SplitViewNativeProps;
1419

15-
function ScreenStackHost({ children }: SplitViewHostProps) {
20+
function SplitViewHost({ children, splitBehavior, primaryEdge }: SplitViewHostProps) {
1621
return (
17-
<SplitViewHostNativeComponent style={styles.container}>
22+
<SplitViewHostNativeComponent
23+
style={styles.container}
24+
splitBehavior={splitBehavior}
25+
primaryEdge={primaryEdge}>
1826
{children}
1927
</SplitViewHostNativeComponent>
2028
);
@@ -26,4 +34,4 @@ const styles = StyleSheet.create({
2634
},
2735
});
2836

29-
export default ScreenStackHost;
37+
export default SplitViewHost;

src/fabric/gamma/SplitViewHostNativeComponent.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,20 @@
22

33
import type { ViewProps } from 'react-native';
44
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
5+
import type { WithDefault } from 'react-native/Libraries/Types/CodegenTypes';
56

6-
export interface NativeProps extends ViewProps {}
7+
export type SplitViewSplitBehavior =
8+
| 'automatic'
9+
| 'displace'
10+
| 'overlay'
11+
| 'tile';
12+
13+
export type SplitViewPrimaryEdge = 'leading' | 'trailing';
14+
15+
export interface NativeProps extends ViewProps {
16+
// Appearance
17+
splitBehavior?: WithDefault<SplitViewSplitBehavior, 'automatic'>;
18+
primaryEdge?: WithDefault<SplitViewPrimaryEdge, 'leading'>;
19+
}
720

821
export default codegenNativeComponent<NativeProps>('RNSSplitViewHost', {});

0 commit comments

Comments
 (0)