From 10322147190777d00c9a2ba56dbacd0fecacd059 Mon Sep 17 00:00:00 2001 From: jensvansteen Date: Tue, 14 Apr 2026 11:23:48 +0700 Subject: [PATCH 1/5] feat: switch the example with expo --- README.md | 160 +- .../scrolledgebar/example/MainActivity.kt | 2 +- example/app.json | 13 +- example/app/_layout.tsx | 110 + example/app/app-store.tsx | 1 + example/app/calendar.tsx | 1 + example/app/index.tsx | 1 + example/app/pr-detail.tsx | 1 + example/app/pull-requests.tsx | 1 + example/app/search-bar.tsx | 1 + example/app/tab-accessory.tsx | 1 + example/app/toolbar.tsx | 1 + example/app/transition-showcase.tsx | 1 + example/babel.config.js | 2 +- example/index.js | 6 +- example/ios/Podfile | 8 + example/ios/Podfile.lock | 422 ++- .../project.pbxproj | 46 + .../ScrollEdgeBarExample/AppDelegate.swift | 4 +- .../PrivacyInfo.xcprivacy | 15 +- example/metro.config.js | 2 +- example/package.json | 28 +- example/src/App.tsx | 186 -- example/src/components/ExampleMenuScreen.tsx | 7 +- example/src/data.ts | 20 +- example/src/screens/ToolbarScreen.tsx | 10 +- .../src/screens/TransitionShowcaseScreen.tsx | 53 +- example/src/styles/shared.ts | 8 +- example/src/types.ts | 30 +- example/tsconfig.json | 4 + ios/HybridScrollEdgeBar.swift | 40 +- yarn.lock | 2773 ++++++++++++++++- 32 files changed, 3592 insertions(+), 366 deletions(-) create mode 100644 example/app/_layout.tsx create mode 100644 example/app/app-store.tsx create mode 100644 example/app/calendar.tsx create mode 100644 example/app/index.tsx create mode 100644 example/app/pr-detail.tsx create mode 100644 example/app/pull-requests.tsx create mode 100644 example/app/search-bar.tsx create mode 100644 example/app/tab-accessory.tsx create mode 100644 example/app/toolbar.tsx create mode 100644 example/app/transition-showcase.tsx delete mode 100644 example/src/App.tsx create mode 100644 example/tsconfig.json diff --git a/README.md b/README.md index 781c45e..a8ef36d 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,9 @@ export function Example() { estimatedTopBarHeight={48} estimatedBottomBarHeight={56} > - + @@ -76,7 +78,9 @@ export function Example() { ))} - + Bottom Bar @@ -87,10 +91,7 @@ export function Example() { If your navigation header or tab bar is external to the scroll-edge-bar container, you can provide explicit offsets: ```tsx - + ... ``` @@ -113,15 +114,160 @@ pod install The example app in `example/` currently demonstrates: - a native stack header via React Navigation native stack -- a native bottom tab bar via React Navigation native bottom tabs - a `ScrollEdgeBar.TopBar` with a segmented control - a `ScrollEdgeBar.BottomBar` with a label and switch +## Adaptive Bar Content + +`ScrollEdgeBar` uses the native iOS scroll-edge bar surface, so the bar material/blur itself follows the content underneath the scroll view. + +There is one important distinction between the bar surface and the content you render inside it: + +- Native UIKit/SwiftUI controls participate in the system scroll-edge appearance environment. +- Arbitrary React Native children are hosted as Fabric/RN views inside that native bar surface. + +This means RN-rendered content, such as `Text` or `View` with JS-defined colors, may not automatically adapt its foreground and fill colors during the local scroll-edge transition in the same way native UIKit/SwiftUI controls do. The bar material can transition correctly while RN child colors remain as styled by React Native. + +This behavior is context-sensitive: + +- When a bar merges with an existing system `UINavigationBar` or `UITabBar`, iOS already provides a stronger system scroll-edge context. In those cases, RN child content can appear to integrate better with the system bar’s material transition. +- In a standalone bottom `safeAreaBar`, with no system `UITabBar` to merge into, arbitrary RN children may not receive the same adaptive foreground/fill treatment. +- SwiftUI-backed content does adapt correctly in the standalone bottom bar case. + +This is why the same visual example can behave differently depending on whether it is attached to a navigation bar, attached to a tab bar, or rendered as a standalone bottom safe-area bar. + +We investigated whether the standalone bottom-bar behavior could be fixed by adding hidden native/SwiftUI content internally. In local testing, the following did **not** reproduce the adaptive behavior for RN-rendered labels/buttons: + +- a hidden SwiftUI view inside the bottom safe-area bar +- a layout-participating SwiftUI sibling inside the bar +- a nested `UIHostingController` probe +- visible native SwiftUI controls inserted as UIKit subviews inside the RN bottom-bar marker + +That suggests the behavior is not triggered simply by “some SwiftUI view exists nearby.” It appears to depend on the actual rendering/hosting model of the bar content. For example, SwiftUI-backed Expo UI content has been observed to adapt correctly because Expo UI mounts SwiftUI content through its own Fabric/SwiftUI virtual-view host, while regular RN/Fabric text and views remain ordinary UIKit views with JS-defined styling. + +### RN children vs SwiftUI-backed children + +The following example shows both approaches. The first bottom bar uses regular React Native primitives. The second uses SwiftUI-backed controls from `@expo/ui/swift-ui`. + +Regular RN children are flexible and require no extra dependency: + +```tsx +import { + PlatformColor, + ScrollView, + StyleSheet, + Switch, + Text, + View, +} from 'react-native'; +import { ScrollEdgeBar } from 'react-native-scroll-edge-bar'; + +export function RNBottomBarExample() { + return ( + + {/* content */} + + + Test + + + + Reset + + + + ); +} + +const styles = StyleSheet.create({ + bottomBar: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + paddingHorizontal: 16, + paddingVertical: 12, + backgroundColor: 'transparent', + }, + label: { + fontSize: 15, + fontWeight: '600', + color: PlatformColor('label'), + }, + spacer: { + flex: 1, + }, + button: { + paddingHorizontal: 14, + paddingVertical: 8, + borderRadius: 10, + backgroundColor: PlatformColor('secondarySystemFill'), + }, + buttonText: { + fontSize: 13, + fontWeight: '600', + color: PlatformColor('label'), + }, +}); +``` + +SwiftUI-backed controls can participate in the standalone scroll-edge transition more like native UIKit/SwiftUI controls: + +```tsx +import { ScrollView } from 'react-native'; +import { Button, HStack, Host, Spacer, Text, Toggle } from '@expo/ui/swift-ui'; +import { + buttonStyle, + controlSize, + font, + foregroundStyle, + padding, +} from '@expo/ui/swift-ui/modifiers'; +import { ScrollEdgeBar } from 'react-native-scroll-edge-bar'; + +export function SwiftUIBottomBarExample() { + return ( + + {/* content */} + + + + + + Test + + + +