React-Native wrong dimension length in Android? - react-native

I'm facing an issue where apparently, when using useWindowDimensions() on React-Native, the length of the shortest dimension is incorrect.
Minimum reproducible example: Create an app from scratch using npx react-native run-android and paste the following on App.js:
export default function App() {
return (
<View style={{flex: 1}}>
<View
style={{
height: 100,
width: 100,
backgroundColor: 'red',
position: 'absolute',
top: useWindowDimensions().height - 20,
}}>
</View>
</View>
);
}
Explanation: there's a red-colored view of size 100 x 100 positioned absolutely on the screen. By doing top: useWindowDimensions().height - 20, I'm telling the view to move all the way to the bottom, and just to "peek" 20 dps.
On portrait orientation, it works:
But on landscape, it doesn't work. The view doesn't peek from the bottom
In fact, the view is still hidden at the bottom. Subtracting 20 from the height of the screen was not enough. I played around and found that I need to subtract at least 28 to make it visible:
What's going on? I would have expected that useWindowDimensions().height returned 28 less than what it did. Is this a bug? Or am I missing/not considering something else?
Note: at the beginning I thought it could be because of Android's status bar or navigation/action bar. But that doesn't make sense...because IT WORKS with a Portrait Orientation.

Related

React Native Samsung One UI 3.0 Dimensions screen width not returning correct value

I'm using react native 0.63.4 and I use <View style={{flex: 1}}> for the root element of every page.
I use Dimensions.get("screen").width to make the elements responsive. For some reason the value of screen width is not correct but a larger value. Thus every element on the screen gets larger.
Expected behavior:
Expected image.
Issue: issue image
I've tested with many devices. I see this problem only on Galaxy S20 Ultra and S10 Lite. The issue occurred after installing the Android 11 & OneUi 3.0 update.
Code:
const {height: screen_height, width: screen_width} = Dimensions.get('screen');
<View style={{ flex: 1,justifyContent:"center",alignItems:"center" }}>
<Text style={{fontSize:screen_width/15}}>text</Text>
</View>;
What could cause the problem?
UPDATE: The issue appears only when the device's screen resolution is set to 3200x1440. The app works as expected on lower resolutions.
Replace this code:
const {height: screen_height, width: screen_width} = Dimensions.get('screen');
With this:
let scale = Dimensions.get('screen').scale / Dimensions.get('window').scale;
const screen_height = Dimensions.get('window').height * scale;
const screen_width = Dimensions.get('window').width * scale;
try Dimensions.get("window").height and Dimensions.get("window").width can resolve your case.
I think your text components are overlapping the actual viewport. Could you add the code which renders the actual (text) content?
And if it causes the issue, then you could try to wrap it with flexWrap: 'wrap' style property.

How can I detect when the user gets a View and do something after getting this position on React Native?

I have a ScrollView with some Views inside it, but I want to start an animation when the user gets to specific View and after it starts my animation.
Example:
<ScrollView style={{ width: DeviceWidth, height: DeviceWidth * 0.5 }}>
{/* When user position scroll is here,
is where I want to start the animation,
not since the page is loaded */}
<View style={{
width: DeviceWidth * 0.4,
height: DeviceWidth * 0.4,
resizeMode: "contain",
}}>
<ProgressBarAnimated
width={barWidth}
value={65}
backgroundColor="#F68D29"
height={40}
barAnimationDuration={6000}
/>
</View>
</ScrollView>
Instead of using a ScrollView you can instead use a FlatList, this gives you a lot more support for the type of indexing you're trying to do.
To detect when a user reaches a specific point you can use onViewableItemsChanged and the define which viewable items need to be on screen in order to trigger your animation.
FlatLists also support a number of build in animations like scrollToEnd or ScrollToIndex which may be helpful.

Difference between assigning dimensions and percentage attribute in layout props

When i try to run the app, my app behaves differently to the below two methods
height: 15%(Dimensions.get('window').height);
// above is not program just my sum up
and
height:'15%'
when I try to set the height to my text input with two methods my apps behave differently, is there any reasons behind it ?
I am in react-native 0.61.4
Giving from Dimension always takes the screen window's height/width. However, giving with the string % percentage would not take the whole screen window's height, width. If there is a wrapper around the View (or element object) it will calculate the wrapper's height/width. For example:
<View style={{ height: 100, width: 300 }}>
<View style={{ height: "10%", width: "10%" }} />
</View>
Inside of the View has 10 height & 30 width it will calculate from parent view. However, if you will take it from Dimension, it will calculate from the whole screen window's height/width.

React Native: Perfectly align Text in circle

I have a circle button (made with borderRadius) in React Native. The text in the component should be centered both vertically and horizonatlly.
Horyzontally it's fine, but the vertical alignment seems to fail whatever I do. Even if it looks good on large cicles with small fontSize, the small circles proof it wrong!
<View style = {{
alignItems:'center',
justifyContent:'center',
backgroundColor:'yellow',
borderColor: this.props.color,
width:size, height:size,
borderRadius:size,
borderWidth:borderWidth,
}}>
<Text style = {{
textAlign: 'center',
backgroundColor:'none',
fontSize:fontSize,
lineHeight:fontSize,
}}>
{this.props.title}
</Text>
</View>
Although already answered elsewhere, I'm unable to center text (in this case) in a circle properly.
As one can see on the image with the green background of the <Text>-Component, the text is just not centered perfectly. Even though the itself is perfecttly aligned...
Here is a snack for Expo with the whole code reduced to the necessary and with different example sizes: https://repl.it/#PaulHuchner/Centered-Text-in-Circles
I have tried the previous answer with only Text and calculating line-height. which looks like a little overkill and didn't work for me. So here's my answer.
I am using View as the container with justifyContent:center
<View style={{
width: 40,
height: 40,
borderRadius: 20,
borderWidth: 1,
borderColor: 'black',
borderStyle: 'solid',
justifyContent: 'center'}}>
<Text style={{fontSize: 20,textAlign: 'center'}}>20</Text></View>
You're trying to set the same fontSize and lineHeight as the circle's diameter, which has borderWidth of 10 included to it.
Due to the borderWidth, the text is being cut and overlayed over the circle. The lineHeight assigned to the cut Text is more than required, hence it is displayed misaligned.
Therefore you need to reduce the fontSize and the lineHeight based on the borderRadius of the circle, to function properly for all dimensions.
<Text style = {{
textAlign: 'center',
backgroundColor:'green',
fontSize:fontSize - 2 * borderWidth, //... One for top and one for bottom alignment
lineHeight:fontSize - (Platform.OS === 'ios' ? 2 * borderWidth : borderWidth), //... One for top and one for bottom alignment
}}>
Here's a snack link
The solution that worked the best for me was instead of using a Text Element, instead, use a plus Icon. The difference is that the viewBox of "+" as a character isn't centered.
If that is confusing look at these three letters
A+a
Notice that "A" is taller than "+" and also "a". So instead, use a PLUS icon instead and it will be perfectly centered such as 24x24 px. This drove me mad!

Change parent of react native video component without unmounting

I am attempting to create picture in picture functionality on iPhone in react native using the react-native-video component. I have two elements, one is the video player, and the other is an image absolutely positioned on top of the player in the lower right corner.
<View style={{ position: 'relative' }}>
<View>
{ this.props.swapPip ? renderVideoPlayer() : renderPip() }
</View>
<View style={{ position: 'absolute', bottom: 16, right: 16 }}>
{ this.props.swapPip ? renderPip() : renderVideoPlayer() }
</View>
</View>
I would like to play the video, and then mid-play, and be able to switch the swapPip property so that the video is now the pip and the image is in the main view. The catch is the video must continue to play smoothly through the transition. Currently it is re-mounting each time the swap happens, losing the video and causing an annoying reload and other unwanted side effects. Is this possible to achieve?
Here is a rough image of the idea. The gray boxes represent the video player, the pink represents the image. Clicking on the smaller box (whatever color) will swap the two back and forth. Swapping should be allowed at any time, and if a video happens to be playing during a swap, playback should not be interrupted in any way.
I guess this turned out to be a dumb question, because zIndex is actually a thing in react-native now. Its in the docs and everything, and works exactly like you would expect:
const pipPosition = { position: 'absolute', bottom: 16, right: 16, zIndex: 99 };
const sourceAPosition = this.props.swapPip ? pipPosition : {};
const sourceBPosition = !this.props.swapPip ? pipPosition : {};
<View style={{ position: 'relative' }}>
<View style={sourceAPosition}>
{ renderSourceA() }
</View>
<View style={sourceBPosition}>
{ renderSourceB() }
</View>
</View>
For some reason I was under the impression zIndex did not exist/work in react-native, and that the ordering of your elements was the only way to accomplish layering...