Change parent of react native video component without unmounting - react-native

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...

Related

KeyboardView not working on android (React-Native)

I have a screen with header and content, and in the content section I have a search and I want when I open the keyboard to raise all the view. I tried keyboardAvoidingView but it's not working for me. What I am doing wrong here ?
return (
<View style={styles.content}>
<View style={styles.header}>
...
</View>
<KeyboardAvoidingView
keyboardVerticalOffset={hp(30)}
style={{flex: 1}}
behavior="padding">
...
</KeyboardAvoidingView>
</View>
);
and for the styling:
header: {
height: hp(30),
width: wp(100),
},
container: {
flexGrow: 1,
backgroundColor: Colors.white,
// borderTopLeftRadius: wp(7),
// borderTopRightRadius: wp(7),
},
and here is how it looks
On Android, when you style the root View to flex:1, the application height will automatically reduce by the keyboard height when keyboard is shown.
And for using of KeyboardAvoidingView, this component should be put to an as high as possible level. Formally, you shouldn't place any sized component outside it to prevent some display problems. And to adapt this component, please make sure to check the compatibility both iOS and Android. It has different behavior on the two systems.
You could try setting the behavior to be platform specific like so:
<KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : undefined}>
// ...
</KeyboardAvoidingView>
It behaves slightly different on iOS and Android and this fixed my issues.
I've also seen react-native-keyboard-aware-scroll-view recommended on many posts regarding the same topic. It might be a more reliable thing to use according to other users but I haven't tried it.

React-Native wrong dimension length in Android?

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.

How to set a video as a background on React Native

I want to set a video as a background but I just could set a video on the screen, not as a background, and I want to have some buttons on it.
I have seen some questions but using react-native video library, and I am using
import { Video } from 'expo';
I want to play a video as a background with expo library, hope somebody can help me, thanks in advance
You can achieve it using zIndex, following code will render video as background and play button on top of it. React native styling works same as web CSS, You should read more about css positioning here.
render() {
return (
<View style={styles.container}>
<TouchableOpacity style={styles.button}>
<Text>Play</Text>
</TouchableOpacity>
<Video style={styles.video} />
</View>
);
}
const styles = {
container: {
position: 'relative'
},
video: {
position: 'absolute',
top: 0,
right: 0,
left: 0,
bottom: 0,
zIndex: 1,
},
button: {
position: 'relative',
zIndex: 2,
}
};

react-native-blur in modal

I'm using react-native-blur to blur a view on react-native but it does not work inside a modal as this issue states.
I've tried the idea suggested in the comment by setting a timeout after the imageLoad but it still not blured.
Is there any workaround ? Why does it works outside a modal but not in the modal ? What is the difference between how a modal render in react-native ? That's unfortunately a bug part of my app and I must succeed.
Thanks
EDIT:
I did it differently. As blurRadius is working for images on android in modals, I use a combinations of images to show exactly what I want of the image.
Perhaps useful for other people, I tried the react-native-blur solution but got stuck as well. Eventually this worked just fine for me:
<Modal
animationType={'fade'}
transparent={true}
visible={this.state.modalVisible}
onRequestClose={() => {
this.setState({ modalVisible: false });
}}
>
<View
style={{
position: 'absolute',
width: '100%',
height: '100%',
justifyContent: 'center',
backgroundColor: 'rgba(100,100,100, 0.5)',
padding: 20,
}}
>
{this.props.modalContent}
</View>
</Modal>
The remaining part of the code can be found in the documentation of the react native modal: https://facebook.github.io/react-native/docs/modal

Clickable Background underneath a ScrollView [React Native]

So, I want to create a layout similar to whats below. [Refer the Image]
So the background has a full screen MapView (React Native Maps) with Markers over it, which needs to be clickable.
And there is a Scrollview with full screen height over the MapView which initially has some top-margin associated with its contents.
But the issue if I arrange my Views this way, the Markers on the map are not clickable in the initial state.
<View>
<MapView>
<Marker clickEventHere></Marker>
<Marker clickEventHere></Marker>
</MapView>
<ScrollView fullscreen>
<View marginTop></View>
</ScrollView>
<View>
I am unsure if its really possible to solve this out.
Initial State
After Scrolling
Solution Tried
yScrolled = event.nativeEvent.contentOffset.y;
yValue = this.state.yValue - yScrolled;
upwardScroll = yScrolled > 0;
if(upwardScroll && (yValue > 0)){
this.setState({
yValue: yValue
});
}
if(yScrolled === 0){
yScrolled = -10;
}
if(!upwardScroll && (yValue <= scrollViewMarginTop)){
yValue = this.state.yValue - yScrolled;
console.debug("UPDATE DOWNWARD");
this.setState({
yValue: yValue
});
}
I'd start with adding absolute positioning to your ScrollView, (position: absolute) starting at whatever y coordinate you would like. I would make this y value a state, eg
yValue: new Animated.Value(start_value) or simply yValue: start_value
I would then use ScrollView's prop onScroll event to handle the change in this y coordinate, for example for the first 100 pixels of a scroll it would simply change the yValue instead of scrolling the view.
This should enable you to press the markers you have in your parent view, and use the same component structure that you have provided.
Note: You would need to do this for collapsing the scrollview also.
Note2: if this doesn't give you the result you were looking for, i'd suggest looking into using some sort of collapsable component for this task, eg https://github.com/oblador/react-native-collapsible
Hope this helps
EDIT: To collapse the scrollview you could first identify if the scroll direction is downwards ( which i think youve done via upwardScroll ) and then..
1) Either simply add the content offset to your yValue
2) If you used Animated.Value for yValue, you can have an animate function that animates the scrollview downwards to your desired position. I think this would look nicer as the user would only need a simple downwards flick to collapse the view, which seems like the industry standard.
There is also a library that is called react-native-touch-through-view that can do this for you. This it how it works basically:
<View style={{ flex: 1 }}>
// Your map goes here
</View>
<TouchThroughWrapper style={{
position: 'absolute'',
top: 0,
left: 0,
width: '100%',
height: '100%',
}}>
<ScrollView
style={{
backgroundColor: 'transparent',
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
// Maybe in your case is flex: 1 enough
}}
bounces={false}
showsVerticalScrollIndicator={false}
>
<TouchThroughView
style={{
height: 400, // This is the "initial space" to the top
}}
/>
// The "slide up stuff" goes here
</ScrollView>
</TouchThroughWrapper>
For example, this then can look like this:
So the map is overlayed by the TouchThroughView that passes all the events right to the view behind. In my case this worked better than using pointer-events box-none.