From 9ba863a5090c636c47939a709c5b8e4e81767421 Mon Sep 17 00:00:00 2001 From: Frane Bandov Date: Sun, 26 Jul 2020 19:28:38 +0200 Subject: [PATCH] Add primaryBackgroundStyle option to splitView (#6419) This PR adds the primaryBackgroundStyle option in splitView to enable translucent sidebars when developing Mac Catalyst apps. Like splitView it's iOS only and requires iOS 13+, the moment it seems to only have an effect on Mac apps and not affect apps running on iOS, see: https://developer.apple.com/documentation/uikit/uisplitviewcontroller/3238075-primarybackgroundstyle The following layout ```typescript Navigation.events().registerAppLaunchedListener(() => { Navigation.setDefaultOptions({ layout: { backgroundColor: 'transparent', componentBackgroundColor: 'transparent', }, topBar: { background: {translucent: true, blur: true}, visible: false, }, }); Navigation.setRoot({ root: { splitView: { master: { stack: { children: [ { component: { name: 'Sidebar', }, }, ], }, }, detail: { stack: { children: [ { component: { name: 'Main', }, }, ], }, }, options: { displayMode: 'visible', splitView: { primaryBackgroundStyle: 'sidebar', } }, }, }, }); }); ``` will produce something like this: ![Screen Shot 2020-07-22 at 17 20 09](https://user-images.githubusercontent.com/35420/88197440-c8faaa00-cc42-11ea-8dec-eab33cbb48ea.png) --- lib/ios/RNNSplitViewControllerPresenter.m | 8 ++ lib/ios/RNNSplitViewOptions.h | 1 + lib/ios/RNNSplitViewOptions.m | 1 + lib/ios/UISplitViewController+RNNOptions.h | 2 + lib/ios/UISplitViewController+RNNOptions.m | 8 ++ lib/src/commands/LayoutTreeParser.test.ts | 103 +++++++++++---------- lib/src/interfaces/Options.ts | 5 + website/api/options-splitView.mdx | 10 ++ 8 files changed, 87 insertions(+), 51 deletions(-) diff --git a/lib/ios/RNNSplitViewControllerPresenter.m b/lib/ios/RNNSplitViewControllerPresenter.m index b9ee9859f04..a1d93490ffd 100644 --- a/lib/ios/RNNSplitViewControllerPresenter.m +++ b/lib/ios/RNNSplitViewControllerPresenter.m @@ -16,6 +16,8 @@ - (void)applyOptions:(RNNNavigationOptions *)options { [self.splitViewController rnn_setPrimaryEdge:options.splitView.primaryEdge]; [self.splitViewController rnn_setMinWidth:options.splitView.minWidth]; [self.splitViewController rnn_setMaxWidth:options.splitView.maxWidth]; + [self.splitViewController rnn_setPrimaryBackgroundStyle:options.splitView.primaryBackgroundStyle]; + } @@ -26,6 +28,8 @@ - (void)applyOptionsOnInit:(RNNNavigationOptions *)initialOptions { [self.splitViewController rnn_setPrimaryEdge:initialOptions.splitView.primaryEdge]; [self.splitViewController rnn_setMinWidth:initialOptions.splitView.minWidth]; [self.splitViewController rnn_setMaxWidth:initialOptions.splitView.maxWidth]; + [self.splitViewController rnn_setPrimaryBackgroundStyle:initialOptions.splitView.primaryBackgroundStyle]; + } - (void)mergeOptions:(RNNNavigationOptions *)options resolvedOptions:(RNNNavigationOptions *)currentOptions { @@ -43,6 +47,10 @@ - (void)mergeOptions:(RNNNavigationOptions *)options resolvedOptions:(RNNNavigat if (options.splitView.maxWidth) { [self.splitViewController rnn_setMaxWidth:options.splitView.maxWidth]; } + if (options.splitView.primaryBackgroundStyle) { + [self.splitViewController rnn_setPrimaryBackgroundStyle:options.splitView.primaryBackgroundStyle]; + } + } - (UISplitViewController *)splitViewController { diff --git a/lib/ios/RNNSplitViewOptions.h b/lib/ios/RNNSplitViewOptions.h index 78d929d51ff..e8221787958 100644 --- a/lib/ios/RNNSplitViewOptions.h +++ b/lib/ios/RNNSplitViewOptions.h @@ -6,5 +6,6 @@ @property (nonatomic, strong) NSString* primaryEdge; @property (nonatomic, strong) Number* minWidth; @property (nonatomic, strong) Number* maxWidth; +@property (nonatomic, strong) NSString* primaryBackgroundStyle; @end diff --git a/lib/ios/RNNSplitViewOptions.m b/lib/ios/RNNSplitViewOptions.m index 1043fd6c07d..a22823ddf6d 100644 --- a/lib/ios/RNNSplitViewOptions.m +++ b/lib/ios/RNNSplitViewOptions.m @@ -9,6 +9,7 @@ - (instancetype)initWithDict:(NSDictionary *)dict { self.primaryEdge = dict[@"primaryEdge"]; self.minWidth = [NumberParser parse:dict key:@"minWidth"]; self.maxWidth = [NumberParser parse:dict key:@"maxWidth"]; + self.primaryBackgroundStyle = dict[@"primaryBackgroundStyle"]; return self; } diff --git a/lib/ios/UISplitViewController+RNNOptions.h b/lib/ios/UISplitViewController+RNNOptions.h index 559aff2c900..9c4ec9c7dcb 100644 --- a/lib/ios/UISplitViewController+RNNOptions.h +++ b/lib/ios/UISplitViewController+RNNOptions.h @@ -11,4 +11,6 @@ - (void)rnn_setMaxWidth:(Number *)maxWidth; +- (void)rnn_setPrimaryBackgroundStyle:(NSString *)style; + @end diff --git a/lib/ios/UISplitViewController+RNNOptions.m b/lib/ios/UISplitViewController+RNNOptions.m index bc7ac43a1a4..885742379b1 100644 --- a/lib/ios/UISplitViewController+RNNOptions.m +++ b/lib/ios/UISplitViewController+RNNOptions.m @@ -37,5 +37,13 @@ - (void)rnn_setMaxWidth:(Number *)maxWidth { } } +- (void)rnn_setPrimaryBackgroundStyle:(NSString *)style { + if (@available(iOS 13.0, *)) { + if ([style isEqualToString:@"sidebar"]) { + [self setPrimaryBackgroundStyle:UISplitViewControllerBackgroundStyleSidebar]; + } + } +} + @end diff --git a/lib/src/commands/LayoutTreeParser.test.ts b/lib/src/commands/LayoutTreeParser.test.ts index 1469341d3a8..1b5e5c50fb2 100644 --- a/lib/src/commands/LayoutTreeParser.test.ts +++ b/lib/src/commands/LayoutTreeParser.test.ts @@ -28,9 +28,9 @@ describe('LayoutTreeParser', () => { data: { name: 'MyReactComponent', options: LayoutExamples.options, - passProps: LayoutExamples.passProps + passProps: LayoutExamples.passProps, }, - children: [] + children: [], }); }); @@ -41,9 +41,9 @@ describe('LayoutTreeParser', () => { data: { name: 'MyReactComponent', options: LayoutExamples.options, - passProps: LayoutExamples.passProps + passProps: LayoutExamples.passProps, }, - children: [] + children: [], }); }); @@ -51,14 +51,14 @@ describe('LayoutTreeParser', () => { const result = uut.parse({ component: { name: 'MyScreen', - passProps: LayoutExamples.passProps - } + passProps: LayoutExamples.passProps, + }, }); expect(result).toEqual({ id: 'myUniqueId', type: LayoutType.Component, data: { name: 'MyScreen', passProps: LayoutExamples.passProps }, - children: [] + children: [], }); expect(result.data.passProps).toBe(LayoutExamples.passProps); }); @@ -68,22 +68,22 @@ describe('LayoutTreeParser', () => { id: 'myUniqueId', type: LayoutType.Stack, data: { - options: LayoutExamples.options + options: LayoutExamples.options, }, children: [ { id: 'myUniqueId', type: LayoutType.Component, data: { name: 'MyReactComponent1' }, - children: [] + children: [], }, { id: 'myUniqueId', type: LayoutType.Component, data: { name: 'MyReactComponent2', options: LayoutExamples.options }, - children: [] - } - ] + children: [], + }, + ], }); }); @@ -182,45 +182,46 @@ const passProps = { strProp: 'string prop', numProp: 12345, objProp: { inner: { foo: 'bar' } }, - fnProp: () => 'Hello from a function' + fnProp: () => 'Hello from a function', }; const options: Options = { topBar: { title: { - text: 'Hello1' - } - } + text: 'Hello1', + }, + }, }; const optionsSplitView: Options = { topBar: { title: { text: 'Hello1', - } + }, }, splitView: { displayMode: 'auto', primaryEdge: 'leading', minWidth: 150, - maxWidth: 300 - } + maxWidth: 300, + primaryBackgroundStyle: 'sidebar', + }, }; const singleComponent = { component: { name: 'MyReactComponent', options, - passProps - } + passProps, + }, }; const externalComponent = { externalComponent: { name: 'MyReactComponent', options, - passProps - } + passProps, + }, }; const stackWithTopBar = { @@ -228,18 +229,18 @@ const stackWithTopBar = { children: [ { component: { - name: 'MyReactComponent1' - } + name: 'MyReactComponent1', + }, }, { component: { name: 'MyReactComponent2', - options - } - } + options, + }, + }, ], - options - } + options, + }, }; const bottomTabs = { @@ -249,26 +250,26 @@ const bottomTabs = { stackWithTopBar, { component: { - name: 'MyReactComponent1' - } - } - ] - } + name: 'MyReactComponent1', + }, + }, + ], + }, }; const sideMenu = { sideMenu: { left: singleComponent, center: stackWithTopBar, - right: singleComponent - } + right: singleComponent, + }, }; const topTabs = { topTabs: { children: [singleComponent, singleComponent, singleComponent, singleComponent, stackWithTopBar], - options - } + options, + }, }; const complexLayout: Layout = { @@ -281,13 +282,13 @@ const complexLayout: Layout = { stackWithTopBar, { stack: { - children: [singleComponent, singleComponent, singleComponent, singleComponent] - } - } - ] - } - } - } + children: [singleComponent, singleComponent, singleComponent, singleComponent], + }, + }, + ], + }, + }, + }, }; const splitView: Layout = { @@ -295,12 +296,12 @@ const splitView: Layout = { master: { stack: { children: [singleComponent], - options - } + options, + }, }, detail: stackWithTopBar, - options: optionsSplitView - } + options: optionsSplitView, + }, }; const LayoutExamples = { @@ -313,5 +314,5 @@ const LayoutExamples = { topTabs, complexLayout, externalComponent, - splitView + splitView, }; diff --git a/lib/src/interfaces/Options.ts b/lib/src/interfaces/Options.ts index 9f3e7657c36..a4b847d5973 100644 --- a/lib/src/interfaces/Options.ts +++ b/lib/src/interfaces/Options.ts @@ -65,6 +65,11 @@ export interface OptionsSplitView { * Set the maximum width of master view */ maxWidth?: number; + /** + * Set background style of sidebar. Currently works for Mac Catalyst apps only. + * @default 'none' + */ + primaryBackgroundStyle?: 'none' | 'sidebar'; } export interface OptionsStatusBar { diff --git a/website/api/options-splitView.mdx b/website/api/options-splitView.mdx index b30eaeebaee..85f43f41e43 100644 --- a/website/api/options-splitView.mdx +++ b/website/api/options-splitView.mdx @@ -35,3 +35,13 @@ Set the maximum width of master view. | Type | Required | Platform | | ------ | -------- | -------- | | number | No | iOS | + +### `primaryBackgroundStyle` + +Set background style of sidebar. Currently works for Mac Catalyst apps only. +See: [Optimizing Your iPad App for Mac +](https://developer.apple.com/documentation/uikit/mac_catalyst/optimizing_your_ipad_app_for_mac#3239145) + +| Type | Required | Default | Platform | +| --------------------------- | -------- | --------- | -------- | +| enum('none', 'sidebar') | No | 'none' | iOS |