Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1748,7 +1748,7 @@ PODS:
- React-RCTFBReactNativeSpec
- ReactCommon/turbomodule/core
- SocketRocket
- react-native-bottom-tabs (1.0.5):
- react-native-bottom-tabs (1.1.0):
- boost
- DoubleConversion
- fast_float
Expand All @@ -1766,7 +1766,7 @@ PODS:
- React-graphics
- React-ImageManager
- React-jsi
- react-native-bottom-tabs/common (= 1.0.5)
- react-native-bottom-tabs/common (= 1.1.0)
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
Expand All @@ -1778,7 +1778,7 @@ PODS:
- SocketRocket
- SwiftUIIntrospect (~> 1.0)
- Yoga
- react-native-bottom-tabs/common (1.0.5):
- react-native-bottom-tabs/common (1.1.0):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -2842,7 +2842,7 @@ SPEC CHECKSUMS:
React-logger: a3cb5b29c32b8e447b5a96919340e89334062b48
React-Mapbuffer: 9d2434a42701d6144ca18f0ca1c4507808ca7696
React-microtasksnativemodule: 75b6604b667d297292345302cc5bfb6b6aeccc1b
react-native-bottom-tabs: 8e918142554e3878f043b23bdf93049b34a78ca6
react-native-bottom-tabs: e33312fc663d163f0be73d3474dfb448ba38dad8
react-native-safe-area-context: c6e2edd1c1da07bdce287fa9d9e60c5f7b514616
React-NativeModulesApple: 879fbdc5dcff7136abceb7880fe8a2022a1bd7c3
React-oscompat: 93b5535ea7f7dff46aaee4f78309a70979bdde9d
Expand Down
21 changes: 15 additions & 6 deletions apps/example/src/Examples/FourTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Article } from '../Screens/Article';
import { Albums } from '../Screens/Albums';
import { Contacts } from '../Screens/Contacts';
import { Chat } from '../Screens/Chat';
import type { ColorValue } from 'react-native';
import { Platform, type ColorValue } from 'react-native';

interface Props {
disablePageAnimations?: boolean;
Expand Down Expand Up @@ -48,22 +48,31 @@ export default function FourTabs({
badge: '5',
hidden: hideOneTab,
},

{
key: 'chat',
focusedIcon: require('../../assets/icons/chat_dark.png'),
title: 'Chat',
},
{
key: 'contacts',
focusedIcon: require('../../assets/icons/person_dark.png'),
title: 'Contacts',
badge: ' ',
},
{
key: 'chat',
focusedIcon: require('../../assets/icons/chat_dark.png'),
title: 'Chat',
role: 'search',
searchable: true,
navigationBarToolbarStyle:
Platform.Version === 26 || Platform.Version === '26.0'
? 'hidden'
: 'visible',
},
]);

return (
<TabView
onSearchFocusChange={(isFocused) => console.log('isFocused', isFocused)}
sidebarAdaptable
onSearchTextChange={(text) => console.log(text)}
disablePageAnimations={disablePageAnimations}
scrollEdgeAppearance={scrollEdgeAppearance}
navigationState={{ index, routes }}
Expand Down
27 changes: 19 additions & 8 deletions apps/example/src/Examples/NativeBottomTabsEmbeddedStacks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Contacts } from '../Screens/Contacts';
import { Chat } from '../Screens/Chat';
import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { Platform } from 'react-native';

const headerOptions = {
headerShown: true,
Expand Down Expand Up @@ -67,7 +68,10 @@ function ChatStackScreen() {

function NativeBottomTabsEmbeddedStacks() {
return (
<Tab.Navigator sidebarAdaptable>
<Tab.Navigator
onSearchTextChange={(text) => console.log(text)}
onSearchFocusChange={(isFocused) => console.log('isFocused', isFocused)}
>
<Tab.Screen
name="Article"
component={ArticleStackScreen}
Expand All @@ -83,13 +87,7 @@ function NativeBottomTabsEmbeddedStacks() {
tabBarIcon: () => require('../../assets/icons/grid_dark.png'),
}}
/>
<Tab.Screen
name="Contacts"
component={ContactsStackScreen}
options={{
tabBarIcon: () => require('../../assets/icons/person_dark.png'),
}}
/>

<Tab.Screen
name="Chat"
component={ChatStackScreen}
Expand All @@ -98,6 +96,19 @@ function NativeBottomTabsEmbeddedStacks() {
require('../../assets/icons/message-circle-code.svg'),
}}
/>
<Tab.Screen
name="Contacts"
component={ContactsStackScreen}
options={{
role: 'search',
tabBarIcon: () => require('../../assets/icons/person_dark.png'),
searchable: true,
navigationBarToolbarStyle:
Platform.Version === 26 || Platform.Version === '26.0'
? 'hidden'
: 'visible',
}}
/>
</Tab.Navigator>
);
}
Expand Down
25 changes: 23 additions & 2 deletions packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,9 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
hidden:item.hidden
testID:RCTNSStringFromStringNilIfEmpty(item.testID)
role:RCTNSStringFromStringNilIfEmpty(item.role)
preventsDefault:item.preventsDefault
preventsDefault:item.preventsDefault
searchable:item.searchable
navigationBarToolbarStyle:RCTNSStringFromStringNilIfEmpty(item.navigationBarToolbarStyle)
];

[result addObject:tabInfo];
Expand All @@ -210,7 +212,8 @@ - (void)updateState:(const facebook::react::State::Shared &)state oldState:(cons
}
}

// MARK: TabViewProviderDelegate

// MARK: TabViewProviderDelegate

- (void)onPageSelectedWithKey:(NSString *)key reactTag:(NSNumber *)reactTag {
auto eventEmitter = std::static_pointer_cast<const RNCTabViewEventEmitter>(_eventEmitter);
Expand All @@ -221,6 +224,24 @@ - (void)onPageSelectedWithKey:(NSString *)key reactTag:(NSNumber *)reactTag {
}
}

- (void)onSearchFocusChangeWithIsFocused:(BOOL)isFocused reactTag:(NSNumber *)reactTag{
auto eventEmitter = std::static_pointer_cast<const RNCTabViewEventEmitter>(_eventEmitter);
if (eventEmitter) {
eventEmitter->onSearchFocusChange(RNCTabViewEventEmitter::OnSearchFocusChange{
.isFocused = isFocused
});
}
}
- (void)onSearchTextChangeWithText:(NSString * _Nonnull)text reactTag:(NSNumber * _Nullable)reactTag {
auto eventEmitter = std::static_pointer_cast<const RNCTabViewEventEmitter>(_eventEmitter);
if (eventEmitter) {
eventEmitter->onSearchTextChange(RNCTabViewEventEmitter::OnSearchTextChange{
.text = [text cStringUsingEncoding:NSUTF8StringEncoding]
});
}
}


- (void)onLongPressWithKey:(NSString *)key reactTag:(NSNumber *)reactTag {
auto eventEmitter = std::static_pointer_cast<const RNCTabViewEventEmitter>(_eventEmitter);
if (eventEmitter) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Foundation
import SwiftUI

/**
Helper used to render UIViewController inside of SwiftUI.
This solves issues in some cases that can't found root UINavigationController.
*/
struct RepresentableViewController: UIViewControllerRepresentable {
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {

}


var view: PlatformView

#if os(macOS)

func makeNSView(context: Context) -> PlatformView {
let wrapper = NSView()
wrapper.addSubview(view)
return wrapper
}

func updateNSView(_ nsView: PlatformView, context: Context) {}

#else

func makeUIView(context: Context) -> PlatformView {
let wrapper = UIView()
wrapper.addSubview(view)
return wrapper
}
func makeUIViewController(context: Context) -> UIViewController {
let contentVC = UIViewController()
contentVC.view.backgroundColor = .clear
contentVC.view.addSubview(view)

return contentVC
}
func updateUIView(_ uiView: PlatformView, context: Context) {}

#endif
}
38 changes: 32 additions & 6 deletions packages/react-native-bottom-tabs/ios/TabView/NewTabView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import React
import SwiftUI

@available(iOS 18, macOS 15, visionOS 2, tvOS 18, *)
struct NewTabView: AnyTabView {
struct NewTabView: AnyTabView {
@ObservedObject var props: TabViewProps

var onLayout: (CGSize) -> Void
var onSelect: (String) -> Void
var onSearchTextChange: ((String) -> Void)
var onSearchFocusChange: ((Bool) -> Void)
var updateTabBarAppearance: () -> Void
@FocusState var focused: Bool
@State var query = ""

@ViewBuilder
var body: some View {
Expand All @@ -29,10 +32,33 @@ struct NewTabView: AnyTabView {
)

Tab(value: tabData.key, role: tabData.role?.convert()) {
RepresentableView(view: child.view)
.ignoresSafeArea(.container, edges: .all)
.tabAppear(using: context)
.hideTabBar(props.tabBarHidden)
//Have to wrap in NavigationView to use searchable
if(tabData.searchable){
NavigationView{
//If it is not wrapped in UIViewController, it will crash.
RepresentableViewController(view: child.view)
.ignoresSafeArea(.container, edges: .all)
.tabAppear(using: context)
.hideTabBar(props.tabBarHidden)
.toolbar(tabData.navigationBarToolbarStyle.convert(), for: .navigationBar)

.searchFocused($focused)
.onChange(of: focused){ newValue in
onSearchFocusChange(newValue)
}
.onChange(of: query) { newValue in
onSearchTextChange(newValue)
}

}.navigationViewStyle(StackNavigationViewStyle())
.searchable(text: $query)
}else{
RepresentableView(view: child.view)
.ignoresSafeArea(.container, edges: .all)
.tabAppear(using: context)
.hideTabBar(props.tabBarHidden)
}

} label: {
TabItem(
title: tabData.title,
Expand Down
8 changes: 6 additions & 2 deletions packages/react-native-bottom-tabs/ios/TabViewImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ struct TabViewImpl: View {
NewTabView(
props: props,
onLayout: onLayout,
onSelect: onSelect
onSelect: onSelect,
onSearchTextChange: onSearchTextChange,
onSearchFocusChange: onSearchFocusChange,
) {
#if !os(macOS)
updateTabBarAppearance(props: props, tabBar: tabBar)
Expand All @@ -36,11 +38,13 @@ struct TabViewImpl: View {
}
}
}

var onSelect: (_ key: String) -> Void
var onLongPress: (_ key: String) -> Void
var onLayout: (_ size: CGSize) -> Void
var onTabBarMeasured: (_ height: Int) -> Void
var onSearchTextChange: (_ text: String) -> Void
var onSearchFocusChange: (_ focused: Bool) -> Void

var body: some View {
tabContent
Expand Down
18 changes: 18 additions & 0 deletions packages/react-native-bottom-tabs/ios/TabViewProps.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ public enum TabBarRole: String {
}
}

public enum ToolbarStyle: String {
case automatic
case hidden
case visible

@available(iOS 18, macOS 15, visionOS 2, tvOS 18, *)
func convert() -> Visibility {
switch self {
case .automatic:
return .automatic
case .hidden:
return .hidden
case .visible:
return .visible
}
}
}

struct IdentifiablePlatformView: Identifiable, Equatable {
let id = UUID()
let view: PlatformView
Expand Down
Loading