Skip to content
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

Android throws an error #2

Closed
jsdomantas opened this issue Oct 18, 2021 · 9 comments
Closed

Android throws an error #2

jsdomantas opened this issue Oct 18, 2021 · 9 comments
Assignees
Labels
bug Something isn't working

Comments

@jsdomantas
Copy link

Hey, I've tried out this library and it seems to be working quite nicely for iOS, but when I run the Android version, I get an error from the measureLayout function saying that there was an error measuring the element. If I log the actual error, it says: Trying to measure a view using measureLayout/measureLayoutRelativeToParent relative to an ancestor that requires custom layout for it's children (RNSScreenContainer). Use measure instead..

@Charanor
Copy link
Owner

Charanor commented Oct 18, 2021

Interesting! I did all of my testing on Android. Can you try running the example project on your device or emulator?

git clone git@github.com:Charanor/react-native-highlight-overlay.git
cd react-native-highlight-overlay
yarn example android

(or yarn example start and then go to the Expo app in your Android device)

This looks to be an issue with react-native-screens (probably from react-navigation). I'm thinking that the measure function might not like the native screen. I'll try testing with react-navigation to the example project, in the meantime could you send some of your code?

(Also, thanks a lot for trying :)⭐)

@Charanor Charanor added the bug Something isn't working label Oct 18, 2021
@Charanor Charanor self-assigned this Oct 18, 2021
@jsdomantas
Copy link
Author

jsdomantas commented Oct 18, 2021

Some more details:

I have added the <HighlightableElementProvider> inside App.js

I'm using React-Navigation and I have a custom tab bar where I render each tab bar item and what I want to achieve is an explanation of each tab for new users in a modal (the modal is just a view with 'absolute' positioning), therefore I wrap the tab item with <HighlightableElement> like this:

return (
    <HighlightableElement id={route.name} key={route.name}>
      <TabButton
        route={route}
        onPress={onPress}
        focused={isFocused}
      />
    </HighlightableElement>
);

I want the current tab bar item to be highlighted and the modal, as well, therefore I place the <HighlightOverlay> inside my modal component like this:

return (
    <>
      <HighlightOverlay onDismiss={() => {}} highlightedElementId="highlightedElement" />
      <View style={{    
          backgroundColor: '#fff',
          borderRadius: 10,
          flex: 1,
          left: 15,
          position: 'absolute',
          right: 15,
          top: 10
      }}>
        <ScrollView>
          // content here
        </ScrollView>
      </View>
    </>
  );

@Charanor
Copy link
Owner

Thank you that's super helpful! I'll do some experimenting with react-navigation tab bar. Some questions:

  1. If you try highlighting a normal element on any screen, do you still get the same error? Or is it only when you try to highlight a tab bar button?
  2. If you change your tab button to something like this, does it work then or do you get the same error?
const TabButton = () => {
  return (
    <View>
      <HighlightableElement id={route.name} key={route.name}>
        <TabButton
          route={route}
          onPress={onPress}
          focused={isFocused}
        />
      </HighlightableElement>
    </View>
  )
}
  1. What if you try the same code as above, but you replace <TabButton ... /> with <View style={{ width: 30, height: 30, backgroundColor: "red" }} />?

@Charanor
Copy link
Owner

Alright I managed to reproduce it. I don't know exactly why it doesn't work yet but it's probably because of how react-navigation works under the hood. I'm working on a fix, or at least a workaround.

@jsdomantas
Copy link
Author

I appreciate it, will be waiting for an update!

@Charanor
Copy link
Owner

I have a fix up, the new version should be up on npm in a minute or so :) Note that you might have to set the style prop on the HighlightableElement if it looks weird after this.

@Charanor
Copy link
Owner

Fix is up, version 1.2.1 🎉. I hope it fixes your problem. My working code looked like this (the formatting is gonna be ugly, sorry).

("Root" is just any screen, nothing special there).

import type { BottomTabBarProps } from "@react-navigation/bottom-tabs";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { NavigationContainer } from "@react-navigation/native";
import React, { useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";
import {
	HighlightableElement,
	HighlightableElementProvider,
	HighlightOverlay,
} from "react-native-highlight-overlay";

import Root from "./Root";

function MyTabBar({
	state,
	descriptors,
	navigation,
	setFocus,
}: BottomTabBarProps & { setFocus: (focus: string) => void }) {
	return (
		<View style={{ flexDirection: "row", justifyContent: "space-evenly" }}>
			{state.routes.map((route, index) => {
				const { options } = descriptors[route.key];
				const label =
					options.tabBarLabel !== undefined
						? options.tabBarLabel
						: options.title !== undefined
						? options.title
						: route.name;

				const isFocused = state.index === index;

				const onPress = () => {
					const event = navigation.emit({
						type: "tabPress",
						target: route.key,
						canPreventDefault: true,
					});

					if (!isFocused && !event.defaultPrevented) {
						// The `merge: true` option makes sure that the params inside the tab screen are preserved
						navigation.navigate({ name: route.name, merge: true });
					}
				};

				const onLongPress = () => {
					navigation.emit({
						type: "tabLongPress",
						target: route.key,
					});
				};

				return (
					<HighlightableElement
						id={label.toString()}
						key={label.toString()}
						style={{ flex: 1 }}
					>
						<TouchableOpacity
							accessibilityRole="button"
							accessibilityState={isFocused ? { selected: true } : {}}
							accessibilityLabel={options.tabBarAccessibilityLabel}
							testID={options.tabBarTestID}
							onPress={() => {
								setFocus(label.toString());
								onPress();
							}}
							onLongPress={onLongPress}
							style={{
								height: 55,
								justifyContent: "center",
								alignItems: "center",
								backgroundColor: ["red", "green", "blue", "yellow"][index],
							}}
						>
							<Text style={{ color: isFocused ? "white" : "#222" }}>{label}</Text>
						</TouchableOpacity>
					</HighlightableElement>
				);
			})}
		</View>
	);
}

const TabNavigator = createBottomTabNavigator();

function App() {
	const [focus, setFocus] = useState<string | null>("0");
	return (
		<HighlightableElementProvider>
			<NavigationContainer>
				<TabNavigator.Navigator
					tabBar={(props) => <MyTabBar setFocus={setFocus} {...props} />}
				>
					<TabNavigator.Screen name="1" component={Root} />
					<TabNavigator.Screen name="2" component={Root} />
					<TabNavigator.Screen name="3" component={Root} />
					<TabNavigator.Screen name="4" component={Root} />
				</TabNavigator.Navigator>
			</NavigationContainer>
			<HighlightOverlay highlightedElementId={focus} onDismiss={() => setFocus(null)} />
		</HighlightableElementProvider>
	);
}

export default App;

@jsdomantas
Copy link
Author

jsdomantas commented Oct 18, 2021

I've updated the package and it's better now, but the circle appears a bit too high:

iOS:
image

Android:
image

Also, I use a state variable for the highlightable element's id, but whenever I update it, the overlay with the highlighted element doesn't updated (although if I log the generated clipPath, I can see that it's changing). The iOS side works fine, I'm not sure why that wouldn't work on Android. Do you have any ideas? :/ I update the ID like this:

  const [index, setIndex] = useState(0);
  const [highlightedElementId, setHighlightedElementId] = useState('route1');

  const handlePressNext = () => {
    const newIndex = index + 1;
    setHighlightedElementId(`${steps[newIndex].type}`);
    ...
  }

...
  return (
    <>
        <HighlightOverlay onDismiss={() => {}} highlightedElementId={highlightedElementId} />
        .....
    </>
  )

Also, this was happening before, but forgot to mention it before. The iOS side also throws an error, although everything works fine. The error is: UIView base class does not support pointerEvent value: box-none. Android doesn't throw this one.

@Charanor
Copy link
Owner

Charanor commented Oct 18, 2021

Hmm that's weird. Perhaps something to do with the status bar; what happens if you add / remove it? Could also be the Android navigation bar (with the back button etc. at the bottom). If I can't find the solution to this I'll push a fix that allows you to manually specify an offset for the highlight, not the most elegant fix but at least it won't look weird.

As for the clip path not changing I'll have to look into it. Perhaps I missed a dependency on a hook so it doesn't re-render, but I think ESLint would've warned about that. It might be another edge-case when using react-navigation, I'll test some more.

The iOS message is a known bug with react-native-svg and the pointerEvent prop. Everything should work, but you might not be able to click the highlighted element on iOS and would just dismiss the overlay instead. I've forked react-native-svg to see if I can find a fix but I can't make any promises (it's an old bug so it's likely difficult to fix). I'll at least make a quick fix that removes the prop on iOS so you don't get the warning message.

Thank you for taking the time to report this :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants