I have a component that renders a WebView along with a Text component at the top and bottom of the screen:
return (
<Container>
<Text>TEXT TOP</Text>
<WebView source={{uri: url}} />
<Text>TEXT BOTTOM</Text>
</Container>
);
The issue that I am facing is that the "TEXT TOP" does not appear the first time I navigate to the specific component, while the "TEXT BOTTOM" appears just fine. In order to make the top text appear, I need to do one of the following:
Navigate to another component and come back
Change the phone's orientation
Interact with the WebView (scroll or press on an input field)
I have tried with both the WebView component from 'react-native' and also from 'react-native-webview' without managing to change the behaviour.
I have this example it renders both text on top and bottom along with webview in
middle.Hope it helps.Run this code on expo snack and view it on your device.
import * as React from 'react';
import { Text, View, StyleSheet, WebView } from 'react-native';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
Change code in the editor and watch it change on your phone! Save to
get a shareable url.
</Text>
<WebView source={{ uri: 'https://github.com/facebook/react-native' }} />
<Text style={styles.paragraph}>
Change code in the editor and watch it change on your phone! Save to
get a shareable url.
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
I had a similar issue. Any components above the WebView would not render and couldn't be forced to render until the WebView had finished loading.
I was able to work around this by having the WebView load a local html resource (source = require('xxx')). Then implement the WebView's onLoadEnd to flip a component state variable to load the real URI you want to show.
It seems that the layout of the screen is delayed or otherwise impeded by the first load of the WebView. I don't know if the first page being local causes the engine to compute the layout inline since it doesn't need to wait on an asynchronous fetch result.
Why the layout of the screen would need to change based on the contents of the WebView is unclear to me. Perhaps it just looks like that's what's happening.
Place all other content below the WebView and position using absolute.
I have been trying to resolve this issue for a while and found that this solution works like a charm!
<WebView source={{uri: url}} style={{ marginTop: 60 }} />
<Text style={{ position: 'absolute', top: 0, height: 60 }}>TEXT TOP</Text>
<Text>TEXT BOTTOM</Text>
Related
I am using Expo for my app. I have a horizontal Flatlist where I render my data react native paper's Card. I saw Card have onPress function. I used that to navigate the another page. But onPress function does not trigger on Android device. I know React native's touchable-opacity have positioning issue on Android. I tried hitSlop and inline styling zIndex but still does not work. I also wrap my card with react-native's touchable-opacity and play with positioning still did not help me, only it works when i used react-native-gesture-handler's touchable-opacity but then it does not work on IOS. Hope anyone can help me...
import React from 'react';
import { Card } from 'react-native-paper';
import { useNavigation } from '#react-navigation/native';
interface Iprops {
item: string;
}
export default function RenderCard({ item }: Iprops) {
const navigation = useNavigation();
return (
<Card
hitSlop={{ "bottom": 30, "top": 30, "right": 30, "left": 30 }}
onPress={() => {
navigation.navigate(`detail`, { // THIS DOES NOT TRIGGER ON ANDROID
"id": `${item.pk}`
});
}}
style={{ "marginBottom": 20 }}>
<Card.Cover
source={{ "uri": `${item.img_url}` }} />
<Card.Actions>
<Card.Title title={item.name} subtitle="Card Subtitle" />
</Card.Actions>
</Card>
);
}
I've noticed that using onPressIn or onPressOut does work on Android within an absolute positioned flatlist, but onPress does not work. I hope this might be of help to someone out there looking for an answer.
You'll need to use the TouchableOpacity element from react-native-gesture-handler.
Like you told, you should add touchable opacity to the element you are rendering on the card. TouchableOpacity can be tricky. So, first give it a styling of borderWidth:1 and borderColor to see the actual touchable area on the screen. Then you start to bring them together with the icon or the image or whatever you are rendering. TouchableOpacity works but the positioning can be tricky. You have to understand it to use it better. Think touchableOpacity as a view with borders then it'll be easier to grasp. Also, if you dont give touchableOpacity an absolute position in the styling it will be out of the screen somewhere, I was never be able to bring it to somewhere that I can see to position it. So you can add 'position' as well.
I gave up on positioning and render my component's based on Platform.
Platform.OS === `ios` ?
<Card
onPress={() => {
navigation.navigate(`detail`, {
"id": `${item.pk}`
});
}}
style={{ "marginBottom": 20 }}>
<Card.Cover source={{ "uri": `${item.img_url}` }} />
<Card.Actions>
<Card.Title title={item.name} subtitle="Card Subtitle" right={Beer} />
</Card.Actions>
</Card> :
<TouchableOpacity
onPress={() => {
navigation.navigate(`detail`, {
"id": `${item.pk}`
});
}}
>
<Card style={{ "marginBottom": 20 }}>
<Card.Cover source={{ "uri": `${item.img_url}` }} />
<Card.Actions>
<Card.Title title={item.name} subtitle="Card Subtitle" right={Beer} />
</Card.Actions>
</Card>
</TouchableOpacity>;
As the question says my Webview component wont load its content. I have no errors poping up or any problem in the conosle related to WebView. I am using expo.
On the other hand when using Iframe I have no problem loading the content running test on web browser but wont work when running test on mobile device. I have tried every solution proposed by others in here but nothing seems to work. I even tried using the iframe inside the WebView using html instead of uri, also nesting the WebView component into a View and giving it flex:1 but still nothing comes up on the screen but a small red dot up in the right corner.
As you can see all the test I've done with the commented code. The first (commented) function is using the Iframe component and the second (not commented) is only WebView.
My code looks like this:
import React from 'react';
import { StyleSheet, Text, Button, View, Dimensions } from 'react-native';
import Iframe from 'react-iframe';
import Header from './Header';
import ButtonMenu from './ButtonMenu';
import { WebView } from 'react-native-webview';
const deviceHeight = Dimensions.get('window').height;
const deviceWidth = Dimensions.get('window').width;
// export default function NatureQuestGps({navigation}) {
// return (
// <View style={styles.pageNatureQuest}>
// <View style={styles.headerContainer}>
// <Header/>
// <ButtonMenu color='#49AC72' onPress={()=> navigation.toggleDrawer()} />
// </View>
// <Iframe url="https://www.google.com/maps/d/embed?mid=11fEhEZiv72kKKy7XbbQ3GIufEcTpWUa2"
// width="400px"
// height="400px"
// display="initial"
// position="relative"/>
// </View>
// );
// }
export default function NatureQuestGps() {
return (
<View style={{ flex: 1, alignItems: 'flex-end' }}>
<WebView
source= {{ uri: 'https://www.google.com/maps/d/embed?mid=11fEhEZiv72kKKy7XbbQ3GIufEcTpWUa2'}}
style={styles.webview}
javaScriptEnabled={true}
domStorageEnabled={true}
startInLoadingState={false}
scalesPageToFit={true}
/>
</View>
);
}
const styles = StyleSheet.create({
pageNatureQuest:{
flex:1,
flexDirection:'column',
alignItems:'center',
},
headerContainer: {
width:'100%',
backgroundColor:'#ededed',
},
webview: {
flex: 1,
backgroundColor: 'yellow',
width: deviceWidth,
height: deviceHeight
}
});
Thanks in advance for anyone taking the time to help
I found the solution and it turns out that WebView component works when testing in mobile with EXPO but naver loaded in my browser, and the Iframe that worked in my browser but was not working in my mobile. Anyways the answer is that if you want a google maps trail or parcour to work for mobile and also web you will need a library to create your own maps routes and trails. But if you are only making a mobile app go for the WebView component although it possibly wont work in your computer browser for tests.
I am trying to create a view post ui for a social media feature of our app. We have a post (the flatlist header component), the comments (the flatlist), and a fixed bottom text input that leverages zIndex to be placed over the flatlist to post comments. The problem is I cannot for the life of me figure out how to properly use a keyboard avoiding view to somehow push this fixed input up when the keyboard is shown. Is there any way to do this or maybe a simpler approach to this ui that I'm missing? I have tried using the keyboard height and adding it to the bottom positioning of the input but its slow and definitely isn't viable.
Code:
<View style={{flex: 1}}>
<View
style={{
...styles.container,
marginTop: 0,
zIndex: 1,
}}>
<FlatList
style={{minHeight: '100%'}}
data={new Array(15)}
renderItem={({item, index}) => <CommentRow index={index} />}
keyExtractor={(item, index) => `${index}`}
ListHeaderComponent={renderHeader}
contentContainerStyle={{paddingBottom: 70}}
/>
</View>
<View
style={{
position: 'absolute',
bottom: 0,
backgroundColor: 'white',
width: '100%',
zIndex: 2,
padding: 15,
borderTopWidth: 1,
borderColor: '#f2f2f2',
}}>
<View style={styles.inputContainer}>
<TextInput style={styles.input} placeholder="Add a comment.." />
<View style={styles.inputTextContainer}>
<Text style={styles.inputText}>Post</Text>
</View>
</View>
</View>
</View>
);
I'm not a fan of keyboard avoiding view because of some bad experiences I had. However, I'll give some tips on how to achieve the same effect with React Native core Animated Library.
Since your view is elevating (Style it as an absolute view) and when the keyboard appears, you can get the height of the keyboard with Keyboard addListener's callback.
componentDidMount(){
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
}
_keyboardDidShow(e) {
const keyboardHeight = e.endCoordinates.height,
}
Once you get the keyboard height, you can animate the absolute view using translateY to the position that aligns with the keyboard without any gaps. This will provide an nice animation similar to that you tried to accomplish with keyboard avoiding view. I've done that in few apps as well and it works great.
For anyone looking to do anything like this - what I ended up doing is using an useEffect hook to monitor when "keyboardWillShow" and then created a function to animate the view above the keyboard once its about to show. A lot of the answers I had seen used "keyboardDidShow" but it makes the UI slow and really ugly. Here's the code:
useEffect(() => {
Keyboard.addListener('keyboardWillShow', _keyboardWillShow);
Keyboard.addListener('keyboardWillHide', _keyboardWillHide);
// cleanup function
return () => {
Keyboard.removeListener('keyboardWillShow', _keyboardWillShow);
Keyboard.removeListener('keyboardWillHide', _keyboardWillHide);
};
}, []);
const _keyboardWillShow = (e) => {
const keyboardHeight = e.startCoordinates.height;
Animated.timing(topValue, {
toValue: -keyboardHeight,
duration: 50,
useNativeDriver: true,
}).start();
};
const _keyboardWillHide = (e) => {
Animated.timing(topValue, {
toValue: 0,
duration: 0,
useNativeDriver: true,
}).start();
};
Where topValue is a stateful Animated value. All you need to do after this is add the topValue variable to your animated view using transform and you're done!
I am working on a react native app which involves a video player (react-native-video), and some simple controls I set up myself. on iOS this works fine, but on Android the TouchableOpacity elements, which I use for controls and navigation, don't seem to detect touches. (Navigation is handles by react-native-fluid-transitions in my app). When I turn on the inspector, a screen-covering View seems to be on top of my controls. However, this is not the case on iOS and I have also not configured such a view.
I installed Atom to use it's inspector feature to see the actual order of my views. It looks as follows:
VideoView is the name of my component, Video is the actual video player and the TouchableOpacity I highlighted is the button I'm trying to get to work. In this view hierarchy, no views seem to be on top of anything. I have also compared this breakdown to other components where my buttons actually work and it looks the same.
My code looks as follows:
return (
<View style={internalStyles.container}>
<Video style={internalStyles.videoContainer}
ref={(ref) => {
this.props.player = ref
}}
source={{uri: url}}
controls={false}
onEnd={() => this.videoEnded()}
paused={this.state.paused}
muted={false}
repeat={false}
resizeMode={"contain"}
volume={1.0}
rate={1.0}
ignoreSilentSwitch={"obey"}
/>
{this.renderControls()}
{Renderer.getInstance().renderNavigationButton()}
</View>
);
where renderControls is a function that renders the pause button, and Renderer is a singleton component containing render function for items I use in more components of my app. This all works fine on iOS, but not on Android. react-native-video seems to be incompatible with react-native-fluid-transitions as everything works when I remove one of either.
Does anyone know what might cause this behavior? Any help would be highly appreciated.
Try removing the activeOpacity prop from TouchableOpacity component.
Or you can use platform specific code to set values for activeOpacity prop
import { Platform, TouchableOpacity } from 'react-native'
<TouchableOpacity
activeOpacity={Platform.OS==='android' ? 0 : 0.2}
>
<Text>submit</Text>
</TouchableOpacity>
Wrap the component in a view and disable pointer events.
<View pointerEvents="none">
<Video source={{ uri: source }} />
</View>
import {TouchableOpacity} from 'react-native';
<TouchableOpacity>some text</TouchableOpacity>
For me it was solved by putting zIndex:1000
<TouchableOpacity
onPress={this.handlePause}
style={{
position: "absolute",
alignItems: "center",
justifyContent: "center",
alignSelf: "center",
elevation: 2,
backgroundColor: "#FFF",
width: 60,
height: 60,
borderRadius: 30,
flex: 1,
zIndex: 1000,
}}
>
I am trying to build a ReactNative Application with an animated button. The problem is that this animation does not work correctly the first time after the App is started. There is some white flickering. But after the animation ran wrong the first time everything works as expected:
I have already tried to preload the image in several ways, but without any success.
This is my minimal working example, note that if there are several different images the flickering occurs if a new image is loaded (e.g. I have two blue buttons and after I tapped the first one, the second one will work fine, but if I then tap an orange button it once again flickers for the first time, at least if I have not tapped another orange button after app start.):
import React, { Component } from 'react';
import {StyleSheet, Text, TouchableWithoutFeedback, View, Image, ScrollView,
Button, BackHandler} from 'react-native';
export default class Touchables extends Component {
constructor(props) {
super(props);
this.state = {alarm1: (<Image source={require("./assets/alarmoff.png")}
style={styles.imageButton}/>),
}
}
componentWillMount(){
//trying to preload all Images, but it does not help.
(<Image source={require("./assets/alarmon.png")} style=
{styles.imageButton}/>)
}
render() {
return (
<ScrollView style={styles.contentContainer}>
<View style={{flex: 3, flexDirection: 'row'}}>
<View style={styles.container}>
<TouchableWithoutFeedback onPressIn={() => this.setState({alarm1:
<Image source={require("./assets/alarmon.png")} style={styles.imageButton}/>})} onPressOut={() => this.setState({alarm1: <Image source={require("./assets/alarmoff.png")} style={styles.imageButton}/>})}>
<View style={styles.button}>
{this.state.alarm1}
</View>
</TouchableWithoutFeedback>
<Text style={styles.text}>This button flickers on first click. Restart App completly to see the issue. Reloading is not enough.</Text>
</View>
</View>
<View>
<Button
onPress={() => BackHandler.exitApp()}
title="Exit App"
color="#841584"
accessibilityLabel="Android only I guess."
/>
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 2,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 30
},
button: {
backgroundColor: '#fff',
borderRadius: 20,
padding: 10,
marginBottom: 20,
shadowColor: '#303838',
shadowOffset: { width: 0, height: 5 },
shadowRadius: 10,
shadowOpacity: 0
},
contentContainer: {
paddingVertical: 20,
flex: 1,
backgroundColor: '#fff',
},
text:{
color: '#000',
marginBottom: 30
},
imageButton: {
flex: 1,
width: 240,
height: 200,
marginBottom: -15,
marginTop: 10,
resizeMode: 'cover'
}
});
So my question is how can I stop the image from flickering after app start?
The full version of the little demo app I have build to show my problem is available on my Github Repository
There may be a performance issue while loading different resolution images. You can use https://github.com/DylanVann/react-native-fast-image module to load images.
you can add and link it as below
# Install
yarn add react-native-fast-image
# Automatic linking. (other linking methods listed below)
react-native link react-native-fast-image
after that u can import it and use it using like below example
import FastImage from 'react-native-fast-image'
const YourImage = () =>
<FastImage
style={styles.image}
source={{
uri: 'https://unsplash.it/400/400?image=1',
headers:{ Authorization: 'someAuthToken' },
priority: FastImage.priority.normal,
}}
resizeMode={FastImage.resizeMode.contain}
/>
I copied this example from that repo. you can find documentation also there. Try it. It will increase image loading performance. Then most probably flickering issue will be resolved.
For me, it was causing flickering issues when I put the Image component in a FlatList ListHeaderComponent component. So,
Code causing flickering:
ListHeaderComponent={HeadComponent}
The HeadComponent was basically inside render and had the code const HeadComponent = () => { return (<Image...
Code that fixed flickering:
ListHeaderComponent={this.renderHeader}
The renderHeader is a function that returned the same thing as HeadComponent using code renderHeader () { return (<Image...
Hope this helps someone.
It was causing flickering issues when I put the Image component in a FlatList ListHeaderComponent component
In order to solve the issue I added useCallBack hook
const ListComponent = useCallBack(() => {
// your code
}, [])
ListHeaderComponent={ListComponent}
For me it solved the flickering issue
Well I have a workaround (sort of..).
In my componentDidMount() I do now set the button to its pressed state, wait for some time until the image is displayed and scaled, and then I set the state to off again, like so:
componentDidMount(){
this.setState({alarm1: <Image source={require("./assets/alarmon.png")} style={styles.imageButton}/>})
setTimeout(()=>{this.setState({alarm1: <Image source={require("./assets/alarmoff.png")} style={styles.imageButton}/>})}, 1000);
}
I tried to lower the timeout to less than a second, but then on my old (and slow) phone the flickering started again on first press after app load.
This obviously leads to the button state beeing changed after the app loaded, but if all buttons flicker once after app start, that is better than every button flickering on first press in my opinion.
I would however be glad if anybody could tell me the real way, how to resolve this.
For anyone who still have this problem : this is another way to fix
<Image
source={{ uri: your_path }}
defaultSource={{ uri: your_path }}
resizeMode="cover"
style={{width: 100,height: 100}} />
A stupid way:
<ImageBackground
defaultSource={require('./ui/pay0.png')}
source={require('./ui/pay0.png')}
style={{flex: 1, position: 'relative', zIndex: 9999, elevation: 5}}
/>
{/* Cache the remaining pictures to prevent flickering */}
<ImageBackground
source={require('./ui/pay1.png')}
style={{position: 'absolute', width: 1, height: 1}}
/>
If you are using expo, you can use Asset.loadAsync. See: https://docs.expo.io/versions/latest/sdk/asset.
In App.js, I like to wait for all static assets to be loaded before showing any screen.