-
Couldn't load subscription status.
- Fork 70
feat(iOS): implement bottom accessory view #446
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(iOS): implement bottom accessory view #446
Conversation
🦋 Changeset detectedLatest commit: d02504c The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@okwasniewski any chance you could take a look on this? Thank you! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Adds iOS bottom accessory view support by exposing a renderBottomAccessoryView prop and wiring it to a new native component and SwiftUI integration.
- Introduces renderBottomAccessoryView prop to render a bottom accessory view on iOS.
- Adds a new native component (BottomAccessoryView) and iOS component view (RCTBottomAccessoryComponentView) with SwiftUI modifier to attach as TabView bottom accessory.
- Updates docs and example app to demonstrate usage.
Reviewed Changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/react-native-bottom-tabs/src/TabView.tsx | Exposes renderBottomAccessoryView prop and conditionally renders BottomAccessoryView on iOS. |
| packages/react-native-bottom-tabs/src/BottomAccessoryViewNativeComponent.ts | Declares the native component interface via codegen. |
| packages/react-native-bottom-tabs/src/BottomAccessoryView.tsx | Wraps the native component, tracks layout and placement, and renders user content. |
| packages/react-native-bottom-tabs/package.json | Registers BottomAccessoryView iOS component provider. |
| packages/react-native-bottom-tabs/ios/TabView/NewTabView.swift | Integrates SwiftUI tabViewBottomAccessory and emits placement changes. |
| packages/react-native-bottom-tabs/ios/RCTBottomAccessoryComponentView.mm | Implements the Fabric view and event emission for layout and placement. |
| packages/react-native-bottom-tabs/ios/RCTBottomAccessoryComponentView.h | Declares the Fabric view interface. |
| docs/docs/docs/guides/usage-with-react-navigation.mdx | Documents the new renderBottomAccessoryView prop. |
| docs/docs/docs/guides/standalone-usage.md | Documents the new renderBottomAccessoryView prop. |
| apps/example/src/Examples/BottomAccessoryView.tsx | Example demonstrating bottom accessory usage. |
| apps/example/src/App.tsx | Adds the new example to the example app menu. |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| /** | ||
| * A function that returns a React element to display as bottom accessory view. | ||
| * iOS 26+ only. | ||
| * | ||
| * @platform ios | ||
| */ | ||
| renderBottomAccessoryView?: BottomAccessoryViewProps['renderBottomAccessoryView']; |
Copilot
AI
Oct 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring states 'iOS 26+ only', which doesn't exist. Update to the actual minimum supported version (e.g., 'iOS 18+ only') to match platform availability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for contributing this! This is awesome 🚀
I've tested it and it works great, just few small comments
| ); | ||
| })} | ||
| {Platform.OS === 'ios' && | ||
| parseFloat(Platform.Version) >= 26 && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this version check? I guess it might not be enough anyways as an app can run on iOS 26 compiled with old Xcode or with a flag that's opting out of new design.
We should probably render it anyways and handle it on the native side
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it would be better not to render the view on the JS side at all on <iOS26 and Android because it wont be visible at all on those platforms?
| var placementValue = "none" | ||
| if (tabViewBottomAccessoryPlacement == .inline) { | ||
| placementValue = "inline" | ||
| } else if (tabViewBottomAccessoryPlacement == .expanded) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| } else if (tabViewBottomAccessoryPlacement == .expanded) { | |
| } else if tabViewBottomAccessoryPlacement == .expanded { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| let selectorString = "emitOnPlacementChanged:" | ||
| let selector = NSSelectorFromString(selectorString) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be solved by a shared protocol between RCTBottomAccessoryView and NewTabView this way we would have type safety
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure! I will do that asap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commit above introduced a bug making it impossible to press pressable items in the accessory view. So I need to look into that. Do you know what could have caused this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is adding the BottomAccessoryProvider as contentView to RCTBottomAccessoryComponentView that causes this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Solved it in 8dfd717
Glad you liked it and thanks for the review! Responded to some of your comments and will commit fixes to the others asap |
|
@okwasniewski I think I've made all the changes you requested or responded to your comments. Would be awesome if you could have a look and review it again. Thank you! |
|
@johankasperi Thanks for applying my review comments. My biggest concern now is the asynchronous nature of how we emit size events. For views that get sized once I think it's fine to do it but this one gets frequently resized. I think we would need to use shadow nodes for this and update the state using the newly introduced flag (https://github.com/facebook/react-native/blob/b8463053d4cac0c36d6bd54359280be827250aed/packages/react-native/ReactCommon/react/renderer/core/EventQueue.h#L32-L37). Maybe we can land it as unstable (or just don't mention it in docs) for now and improve it before making it a public API. |
Ok! I see what you mean but unfortunately I'm not that familiar with this flag or building shadow nodes, could you assist me with that? Feel free to commit to this PR or as you say we could improve it in future PRs |
|
|
||
| - Type: `ColorValue` | ||
|
|
||
| #### `renderBottomAccessoryView` <Badge text="iOS" type="info" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add <Badge text="experimental" type="danger"/> here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| ); | ||
| } | ||
| ``` | ||
| #### `renderBottomAccessoryView` <Badge text="iOS" type="info" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here let's add here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| eventEmitter->onNativeLayout(BottomAccessoryViewEventEmitter::OnNativeLayout { | ||
| .height = frame.size.height, | ||
| .width = frame.size.width | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a TODO here, TODO: Rewrite this to emit synchronous layout events using shadow nodes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps | ||
| { | ||
| [super updateProps:props oldProps:oldProps]; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
Alright, let's land it as experimental - I'll work on the shadow node part |
Thanks for the review! I've made the changes requested. And thank you for looking into refactor to a shadow node |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on it! 🙏
Thank you for the review and support! |
PR Description
Adds support for https://developer.apple.com/documentation/uikit/uitabbarcontroller/bottomaccessory by implementing this prop:
How to test?
Use the new example in the example app named "Bottom Accessory View".
Screenshots
bottom-accessory-view.mov