Skip to content

Commit

Permalink
feat: implement useBottomTabBarHeight (#158)
Browse files Browse the repository at this point in the history
* feat: implement useBottomTabBarHeight

* docs(changeset): feat: add useBottomTabBarHeight() hook

* feat: add events for old arch

* feat: add docs
  • Loading branch information
okwasniewski authored Nov 23, 2024
1 parent 153f5f7 commit 51c523b
Show file tree
Hide file tree
Showing 17 changed files with 308 additions and 84 deletions.
5 changes: 5 additions & 0 deletions .changeset/perfect-onions-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'react-native-bottom-tabs': patch
---

feat: add useBottomTabBarHeight() hook
12 changes: 6 additions & 6 deletions apps/example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1209,7 +1209,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-bottom-tabs (0.5.2):
- react-native-bottom-tabs (0.6.0):
- DoubleConversion
- glog
- RCT-Folly (= 2024.01.01.00)
Expand All @@ -1222,7 +1222,7 @@ PODS:
- React-graphics
- React-ImageManager
- React-jsi
- react-native-bottom-tabs/common (= 0.5.2)
- react-native-bottom-tabs/common (= 0.6.0)
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
Expand All @@ -1234,7 +1234,7 @@ PODS:
- SDWebImageSVGCoder (>= 1.7.0)
- SwiftUIIntrospect (~> 1.0)
- Yoga
- react-native-bottom-tabs/common (0.5.2):
- react-native-bottom-tabs/common (0.6.0):
- DoubleConversion
- glog
- RCT-Folly (= 2024.01.01.00)
Expand Down Expand Up @@ -1945,7 +1945,7 @@ SPEC CHECKSUMS:
React-logger: d79b704bf215af194f5213a6b7deec50ba8e6a9b
React-Mapbuffer: b982d5bba94a8bc073bda48f0d27c9b28417fae3
React-microtasksnativemodule: 8fa285fed833a04a754bf575f8ded65fc240b88d
react-native-bottom-tabs: 402d19b4a55e6e17c78736a9dd2592cd9864881e
react-native-bottom-tabs: c17ddaf86c160134349c6325e020c1f38ee5f743
react-native-safe-area-context: 73505107f7c673cd550a561aeb6271f152c483b6
React-nativeconfig: 8c83d992b9cc7d75b5abe262069eaeea4349f794
React-NativeModulesApple: b8465afc883f5bf3fe8bac3767e394d581a5f123
Expand Down Expand Up @@ -1982,8 +1982,8 @@ SPEC CHECKSUMS:
SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
SwiftUIIntrospect: fee9aa07293ee280373a591e1824e8ddc869ba5d
Yoga: 055f92ad73f8c8600a93f0e25ac0b2344c3b07e6
Yoga: aa3df615739504eebb91925fc9c58b4922ea9a08

PODFILE CHECKSUM: 1c1dbca3e400ef935aa9a150cb2dcb58fb8c4536

COCOAPODS: 1.15.2
COCOAPODS: 1.14.3
75 changes: 75 additions & 0 deletions apps/example/src/Components/MusicControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import * as React from 'react';
import { View, Text, StyleSheet } from 'react-native';

type MusicControlProps = {
bottomOffset: number;
};

export const MusicControl: React.FC<MusicControlProps> = ({ bottomOffset }) => {
return (
<View
style={[
styles.musicControlContainer,
{
bottom: bottomOffset + 10,
},
]}
>
<View style={styles.musicControlContent}>
<View style={styles.songInfo}>
<Text style={styles.songTitle} numberOfLines={1}>
Currently Playing Song
</Text>
</View>
<View style={styles.controls}>
<Text style={styles.controlButton}></Text>
<Text style={styles.controlButton}></Text>
</View>
</View>
</View>
);
};

const styles = StyleSheet.create({
musicControlContainer: {
position: 'absolute',
left: 15,
right: 15,
borderRadius: 18,
height: 55,
backgroundColor: '#fff',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 4,
},
shadowOpacity: 0.25,
shadowRadius: 5,
elevation: 8,
},
musicControlContent: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 16,
paddingVertical: 8,
},
songInfo: {
flex: 1,
marginRight: 16,
},
songTitle: {
fontSize: 14,
fontWeight: '600',
color: '#000',
},
controls: {
flexDirection: 'row',
alignItems: 'center',
},
controlButton: {
fontSize: 24,
paddingHorizontal: 12,
color: '#000',
},
});
4 changes: 4 additions & 0 deletions apps/example/src/Screens/Contacts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
Text,
View,
} from 'react-native';
import { useBottomTabBarHeight } from 'react-native-bottom-tabs';
import { MusicControl } from '../Components/MusicControl';

type Item = { name: string; number: number };

Expand Down Expand Up @@ -97,6 +99,7 @@ export function Contacts({ query, ...rest }: Props) {
console.log(Platform.OS, ' Rendering Contacts');
const renderItem = ({ item }: { item: Item }) => <ContactItem item={item} />;

const tabBarHeight = useBottomTabBarHeight();
const ref = React.useRef<FlatList>(null);
useScrollToTop(ref);

Expand All @@ -117,6 +120,7 @@ export function Contacts({ query, ...rest }: Props) {
renderItem={renderItem}
ItemSeparatorComponent={ItemSeparator}
/>
<MusicControl bottomOffset={tabBarHeight} />
</SafeAreaView>
);
}
Expand Down
35 changes: 35 additions & 0 deletions docs/docs/docs/guides/usage-with-react-navigation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,38 @@ React.useEffect(() => {
return unsubscribe;
}, [navigation]);
```

### Hooks

`useBottomTabBarHeight`

This hook returns the height of the bottom tab bar. This is useful when you want to place a component above the tab bar on iOS. It's not needed to offset the content of the screen as the navigator does it automatically.

```tsx
import { useBottomTabBarHeight } from 'react-native-bottom-tabs';

function MyComponent() {
const tabBarHeight = useBottomTabBarHeight();

return (
<ScrollView>
{/* Content */}
<View style={{ bottom: tabBarHeight }} />
</ScrollView>
);
}
```

Alternatively, you can use the `BottomTabBarHeightContext` directly if you are using a class component or need it in a reusable component that can be used outside the bottom tab navigator:

```tsx
import { BottomTabBarHeightContext } from 'react-native-bottom-tabs';

// ...

<BottomTabBarHeightContext.Consumer>
{tabBarHeight => (
/* render something */
)}
</BottomTabBarHeightContext.Consumer>
```
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,26 @@ import React
private var key: NSString
@objc public var viewTag: NSNumber
@objc public var coalescingKey: UInt16

@objc public var eventName: String {
return "onPageSelected"
}

@objc public init(reactTag: NSNumber, key: NSString, coalescingKey: UInt16) {
self.viewTag = reactTag
self.key = key
self.coalescingKey = coalescingKey
super.init()
}

@objc public func canCoalesce() -> Bool {
return false
}

@objc public class func moduleDotMethod() -> String {
return "RCTEventEmitter.receiveEvent"
}

@objc public func arguments() -> [Any] {
return [
viewTag,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React

@objc public class TabBarMeasuredEvent: NSObject, RCTEvent {
private var height: NSInteger
@objc public var viewTag: NSNumber
@objc public var coalescingKey: UInt16

@objc public var eventName: String {
return "onTabBarMeasured"
}

@objc public init(reactTag: NSNumber, height: NSInteger, coalescingKey: UInt16) {
self.viewTag = reactTag
self.height = height
self.coalescingKey = coalescingKey
super.init()
}

@objc public func canCoalesce() -> Bool {
return false
}

@objc public class func moduleDotMethod() -> String {
return "RCTEventEmitter.receiveEvent"
}

@objc public func arguments() -> [Any] {
return [
viewTag,
RCTNormalizeInputEventName(eventName) ?? eventName,
[
"height": height
]
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,26 @@ protocol RCTEvent {}
private var key: NSString
@objc public var viewTag: NSNumber
@objc public var coalescingKey: UInt16

@objc public var eventName: String {
return "onTabLongPress"
}

@objc public init(reactTag: NSNumber, key: NSString, coalescingKey: UInt16) {
self.viewTag = reactTag
self.key = key
self.coalescingKey = coalescingKey
super.init()
}

@objc public func canCoalesce() -> Bool {
return false
}

@objc public class func moduleDotMethod() -> String {
return "RCTEventEmitter.receiveEvent"
}

@objc public func arguments() -> [Any] {
return [
viewTag,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,15 @@ - (void)onLongPressWithKey:(NSString *)key reactTag:(NSNumber *)reactTag {
}
}

- (void)onTabBarMeasuredWithHeight:(NSInteger)height reactTag:(NSNumber *)reactTag {
auto eventEmitter = std::static_pointer_cast<const RNCTabViewEventEmitter>(_eventEmitter);
if (eventEmitter) {
eventEmitter->onTabBarMeasured(RNCTabViewEventEmitter::OnTabBarMeasured {
.height = (int)height
});
}
}

@end

Class<RCTComponentViewProtocol> RNCTabViewCls(void)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ - (instancetype)init
RCT_EXPORT_VIEW_PROPERTY(items, NSArray)
RCT_EXPORT_VIEW_PROPERTY(onPageSelected, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onTabLongPress, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onTabBarMeasured, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(selectedPage, NSString)
RCT_EXPORT_VIEW_PROPERTY(tabViewStyle, NSString)
RCT_EXPORT_VIEW_PROPERTY(icons, NSArray<RCTImageSource *>);
Expand Down Expand Up @@ -59,6 +60,11 @@ - (void)onPageSelectedWithKey:(NSString *)key reactTag:(NSNumber *)reactTag {
[self.bridge.eventDispatcher sendEvent:event];
}

- (void)onTabBarMeasuredWithHeight:(NSInteger)height reactTag:(NSNumber *)reactTag {
auto event = [[TabBarMeasuredEvent alloc] initWithReactTag:reactTag height:height coalescingKey:_coalescingKey++];
[self.bridge.eventDispatcher sendEvent:event];
}

- (UIView *)view
{
return [[TabViewProvider alloc] initWithDelegate:self];
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 @@ -55,10 +55,11 @@ struct RepresentableView: UIViewRepresentable {
*/
struct TabViewImpl: View {
@ObservedObject var props: TabViewProps
var onSelect: (_ key: String) -> Void
var onLongPress: (_ key: String) -> Void
@Weak var tabBar: UITabBar?

var onSelect: (_ key: String) -> Void
var onLongPress: (_ key: String) -> Void
var onTabBarMeasured: (_ height: Int) -> Void

var body: some View {
TabView(selection: $props.selectedPage) {
Expand All @@ -83,6 +84,9 @@ struct TabViewImpl: View {
#endif
.introspectTabView(closure: { tabController in
tabBar = tabController.tabBar
onTabBarMeasured(
Int(tabController.tabBar.frame.size.height)
)
})
.configureAppearance(props: props, tabBar: tabBar)
.tintColor(props.selectedActiveTintColor)
Expand Down
Loading

0 comments on commit 51c523b

Please sign in to comment.