Skip to content

Commit

Permalink
fix: remove touch disabling in header corners (#1157)
Browse files Browse the repository at this point in the history
PR fixing the behavior of cutting the hit area of the corners of the header when the headerLeft/headerRight components are provided. It overrides the default method and checks directly if the point is inside one of header subviews.
  • Loading branch information
WoLewicki authored Oct 5, 2021
1 parent 28fc906 commit 1ca4fff
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 0 deletions.
1 change: 1 addition & 0 deletions TestsExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import Test1072 from './src/Test1072';
import Test1084 from './src/Test1084';
import Test1091 from './src/Test1091';
import Test1096 from './src/Test1096';
import Test1157 from './src/Test1157';
import Test1162 from './src/Test1162';

export default function App() {
Expand Down
62 changes: 62 additions & 0 deletions TestsExample/src/Test1157.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as React from 'react';
import {Button, View, TouchableOpacity} from 'react-native';
import {NavigationContainer, ParamListBase} from '@react-navigation/native';
import {createNativeStackNavigator, NativeStackNavigationProp} from 'react-native-screens/native-stack';

function First({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) {
return (
<View style={{flex: 1, backgroundColor: 'red'}}>
<Button
title="Tap me for second screen"
onPress={() => navigation.navigate('Second')}
/>
</View>
);
}

function Second({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) {
return (
<View style={{flex: 1, backgroundColor: 'yellow'}}>
<Button title="Tap me for first screen" onPress={() => navigation.goBack()} />
</View>
);
}

const Stack = createNativeStackNavigator();

const ButtonWithBiggerChild = () => (
<TouchableOpacity style={{width: 30, height: 30, backgroundColor: 'red'}} onPress={() => console.log("hello")}>
<View style={{width: 30, height: 30, backgroundColor: 'yellow', marginLeft: -15}}/>
</TouchableOpacity>
);

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{stackPresentation: 'modal'}}>
<Stack.Screen name="First" component={First}
options={{headerShown: true,
// headerLeft: () => <TouchableOpacity onPress={() => console.log("hello")} style={{width: 30, height: 30, backgroundColor: 'red', marginLeft: -15}}/>,
headerLeft: () => <ButtonWithBiggerChild />,
headerRight: () => <TouchableOpacity onPress={() => console.log("there")} style={{width: 30, height: 30, backgroundColor: 'red', marginRight: -15}}/>,
}}
/>
<Stack.Screen
name="Second"
component={Second}
options={{
headerLeft: () => <TouchableOpacity onPress={() => console.log("hello")} style={{width: 50, height: 50, backgroundColor: 'red', marginLeft: -15}}/>
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
17 changes: 17 additions & 0 deletions ios/RNSScreenStack.m
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,23 @@ - (void)updateContainer
[self setModalViewControllers:modalControllers];
}

// By default, the header buttons that are not inside the native hit area
// cannot be clicked, so we check it by ourselves
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (CGRectContainsPoint(_controller.navigationBar.frame, point)) {
// headerConfig should be the first subview of the topmost screen
UIView *headerConfig = [[_reactSubviews.lastObject reactSubviews] firstObject];
if ([headerConfig isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
UIView *headerHitTestResult = [headerConfig hitTest:point withEvent:event];
if (headerHitTestResult != nil) {
return headerHitTestResult;
}
}
}
return [super hitTest:point withEvent:event];
}

- (void)layoutSubviews
{
[super layoutSubviews];
Expand Down
21 changes: 21 additions & 0 deletions ios/RNSScreenStackHeaderConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,27 @@ - (void)removeFromSuperview
_screenView = nil;
}

// this method is never invoked by the system since this view
// is not added to native view hierarchy so we can apply our logic
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
for (RNSScreenStackHeaderSubview *subview in _reactSubviews) {
if (subview.type == RNSScreenStackHeaderSubviewTypeLeft || subview.type == RNSScreenStackHeaderSubviewTypeRight) {
// we wrap the headerLeft/Right component in a UIBarButtonItem
// so we need to use the only subview of it to retrieve the correct view
UIView *headerComponent = subview.subviews.firstObject;
// we convert the point to RNSScreenStackView since it always contains the header inside it
CGPoint convertedPoint = [_screenView.reactSuperview convertPoint:point toView:headerComponent];

UIView *hitTestResult = [headerComponent hitTest:convertedPoint withEvent:event];
if (hitTestResult != nil) {
return hitTestResult;
}
}
}
return nil;
}

- (void)updateViewControllerIfNeeded
{
UIViewController *vc = _screenView.controller;
Expand Down

0 comments on commit 1ca4fff

Please sign in to comment.