I have a small problem with triggering rotation event in react-native.
I receive the error that this.setState is undefined. When I use this.onLayout.bind(this), still nothing happend.
EDITED
App
const win = Dimensions.get('window');
constructor(props) {
super(props);
this.state = {
height: win.height,
width: win.width
}
}
onLayout (e) {
console.log(e);
this.setState({
height: e.nativeEvent.layout.height,
width: e.nativeEvent.layout.width,
})
}
render() {
return <View style={[styles.container, {width: this.state.width, height: this.state.height }]} onLayout={this.onLayout}>
<WebView
source={webapp2}
originWhitelist={'["*"]'}
automaticallyAdjustContentInsets={true}
allowFileAccess={true}
javaScriptEnabled={true}
domStorageEnabled={true}
scalesPageToFit={true}
scrollEnabled={false}
mixedContentMode='always'
/>
</View>
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
}
});
index.html (webapp)
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0" />
<style media="screen" type="text/css">
#container, #test {
width:100%;
height:100%;
top:0;
left:0;
right:0;
bottom:0;
position:absolute;
user-select: none;
-webkit-user-select: none;
border:1px solid red;
}
</style>
</head>
<body>
<br><br><br><br><br><h1>aaaa</h1>
<div id="test"></div>
</body>
</html>
Also the event is not triggered when I use simple <Text>
if you use a method like
onLayout (e) {
console.log(e);
this.setState({
height: e.nativeEvent.layout.height,
width: e.nativeEvent.layout.width,
})
You have to bind it in constructor like :
this.onLayout = this.onLayout.bind(this)
I had recently similar issue and I know what cause it but not how to resolve.
Here is a snack.expo example: Link
Try to uncomment
/* {
width: width,
height: height,
}, */
an instantly onLayout stop running.
If you are using as style a value setted by an state and this state is updated by onLayout function, this is only triggered twice times.
I means, this looks incompatible or maybe somehow is possible manage it.
I'm begginer with React Native,
Do someone know something about this behavoir and how "to fix" it ?
Related
I was asked to add an horizontal scrollview inside of a drawer. (Think Instagram stories but in a drawer.) The animation works very inconsistent. The most annoying is swiping to the left, the scrollview often doesn't scroll and the drawer starts the closing animation instead.
The drawer already exists for awhile, so setup issues are not the problem.
Here is my code:
// Drawer.navigator
const DrawerContent = () => {
// UI of the drawer
return <Drawer activeRoute={activeRoute} />;
};
return (
<DrawerNav.Navigator
drawerContent={DrawerContent}
drawerStyle={{ width: 320 }}
initialRouteName="Home"
drawerContentOptions={{
someColor: theme.someColor,
}}
>
// other screens
</DrawerNav.Navigator>
import { ScrollView } from 'react-native';
const Container = styled.View`
height: 62.5px;
flex-direction: row;
align-items: center;
justify-content: center;
`;
const ThingWrapper = styled(Flex)`
height: 62.5px;
align-items: center;
justify-content: center;
.lastBubble {
margin-right: 16px;
}
`;
...
<Container>
<ScrollView
horizontal
vertical={false}
directionalLockEnabled
nestedScrollEnabled
showsHorizontalScrollIndicator={false}
pinchGestureEnabled={false}
contentContainerStyle={{
zIndex: 9999,
}}
>
{thingsToRender.map((thing, index) => {
return (
<ThingWrapper>
<AnyThing thingId={thing.id} />
</ThingWrapper>
);
})}
</ScrollView>
</Container>
I think element focus might be the main problem here, but I don't know how to fix this. Any help is greatly appreciated. Thanks!
I fixed it by creating this object and giving it as a param to every screen. As a con this disable the drawer swipe open/close.
const drawerScreenOptions = {
gestureEnabled: true,
swipeEnabled: false,
};
// Drawer.navigator
const DrawerContent = () => {
// UI of the drawer
return <Drawer activeRoute={activeRoute} />;
};
return (
<DrawerNav.Navigator
drawerContent={DrawerContent}
drawerStyle={{ width: 320 }}
initialRouteName="Home"
>
// !! Give options to all screens
<DrawerNav.Screen
name="screenX"
component={ScreenXNavigator}
options={drawerScreenOptions}
/>
</DrawerNav.Navigator>
I'm using a WebView to show an iframe of a twitch-stream in my react-native app, however at some points when it renders the WebView only shows up as a blank white screen until you scroll/move the UI and at other times it works as intended.
There are no errors emitted when the WebView is blank, it seems to load as intended so not really sure why it just shows a blank white screen.
Here is the WebView code:
<Animated.View
style={{
height: anim,
width: width,
overflow: 'hidden',
}}
>
<WebView
onLoadEnd={() => {
useAnimation()
}}
source={{
uri: `https://host.com/iframe/?channel=${channelName}`,
}}
style={{
height: heightByRatio,
width: width,
flex: 0,
}}
mediaPlaybackRequiresUserAction={false}
allowsInlineMediaPlayback={true}
/>
</Animated.View>
I had a very similar issue of webviews sometimes loading blank on android devices. I can't say I figured out why it was happening, but I solved it by delaying the load of the webview and putting a 2px border around the container wrapping the webview. Neither solution worked on its own, both were needed.
export default class WebviewExample extends Component<Props> {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
setTimeout(() => {
this.setState({loadWebview: true});
});
}
render() {
return (
<View style={styles.view}>
{this.state.loadWebview &&
<WebView />
}
</View>
);
}
}
const styles = StyleSheet.create({
view: {
borderWidth: 2,
borderColor: 'transparent',
}
});
I am using react-native-dynamic-fonts to inject dynamic fonts at run time.
I downloaded Spicy Rice from Google Fonts and I am using it like so:
export default class App extends Component<Props> {
componentWillMount() {
loadFonts([{name: 'SpicyRice-Regular', data: font, type: 'ttf'}], true).then(function(names) {
console.log('Loaded all fonts successfully. Font names are: ', names);
});
}
render() {
return (
<View style={{flex: 1, marginTop: 150}}>
<Text style={{fontFamily: 'SpicyRice-Regular', fontSize: 25, color: 'red'}}>Foo</Text>
<WebView style={{ flex: 1, }} source={content} />
</View>
);
}
}
I see my console log and it returns SpicyRice-Regular. However, when I try to use it in my WebView on Android, it doesn't seem to work.
My WebView simply uses:
p {
font-size: 150px;
font-family: 'SpicyRice-Regular';
font-weight: 400;
font-style: normal;
}
However, it renders on iOS but doesn't on Android.
iOS:
Android:
Font's don't waterfall through for Android Webview. Just like the Chrome browser, each instance of a Webview is sandboxed into its own environment.
You do have access to the filesystem though and you can to link directly to the font asset using url("file:///android_asset/fonts/<filename>") in the Webview CSS properties so something like this should work:
p {
font-size: 150px;
font-family: 'SpicyRice-Regular';
font-weight: 400;
font-style: normal;
src: url('file:///android_asset/fonts/SpicyRice-Regular.ttf')
}
This is my code, I'm trying to load a stream from my IP camera.
<View style={{flex:1,marginTop:70, flexDirection:'column', justifyContent:'space-between'}}>
<Hue/>
<View style={{flex:1}}>
<WebView
source={{uri: 'http://192.168.2.6:81/videostream.cgi?user=admin&pwd=XXXXX'}}
style={{/*marginTop: 20, flex: 1, width:450, height:100*/}}
javaScriptEnabled={false}
domStorageEnabled={false}
startInLoadingState={false}
scalesPageToFit={false}
scrollEnabled={true}
/>
</View>
<Text>Just some text</Text>
</View>
<Hue/> is a component to check if the WebView is still loading (because in a normal case, it won't load if it's not the only component).
The width property have an ambiguous behavior: reducing it increase the height of the webview. Leaving an empty scrolling space.
Moreover, modifying the height of the webview component does nothing at all.
I tried to modify the parent view applying height and width with no luck.
Also, I did not find any props to modify the webview content itself.
Is there any way, or a react-native component that can help me to integrate my IP camera stream in my application ?
Any suggestion is very appreciated.
EDIT
Updated the code according to Ashwin's comment and I still get this :
EDIT 2
I updated my code according to sfratini answer but if I set the scroll enabled and then scroll, I'm able so see that there is always a part of the image not displayed. Seems that react does not understand to resize to 100%.. It's strange...
<WebView
source={{
uri: this.props.url
}}
style={{ height: height, width, resizeMode: 'cover', flex: 1 }}
injectedJavaScript={`const meta = document.createElement('meta'); meta.setAttribute('content', 'width=width, initial-scale=0.5, maximum-scale=0.5, user-scalable=2.0'); meta.setAttribute('name', 'viewport'); document.getElementsByTagName('head')[0].appendChild(meta); `}
scalesPageToFit={false}
onLoadEnd={this._onLoadEnd}
/>
Managed to get the same behavior for ios and Android. Thanks Gowtham Palanisamy.
<WebView
source={{ uri: url }}
style={{ flex: 1 }}
injectedJavaScript={`
const iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
if (!iOS) {
const meta = document.createElement('meta');
let initialScale = 1;
if(screen.width <= 800) {
initialScale = ((screen.width / window.innerWidth) + 0.1).toFixed(2);
}
const content = 'width=device-width, initial-scale=' + initialScale ;
meta.setAttribute('name', 'viewport');
meta.setAttribute('content', content);
document.getElementsByTagName('head')[0].appendChild(meta);
}
`}
scalesPageToFit={Platform.OS === 'ios'}
/>
just a note that webview has standard style and also containerStyle see documentation. standard style i believe has background: white, and containerStyle has flex: 1.
To make an image responsive, i made my code like this (iframeWidth is just window width dimension minus some padding):
if (node.name === 'img') {
const imgHeight = Number(node.attribs.height);
const imgAlt = node.attribs.alt;
const imgSource = node.attribs.src;
const imageHtml = `<img src="${imgSource}" height="${
imgHeight * 2.3
}" alt="${imgAlt}" />`;
return (
<WebView
key={index}
source={{ html: imageHtml }}
startInLoadingState
scalesPageToFit
allowsInlineMediaPlayback
domStorageEnabled
style={{ backgroundColor: 'transparent' }}
containerStyle={[{ flex: 0, width: iFrameWidth, height: imgHeight }]}
/>
);
}
Hope this helps to someone!
Using Gowtham Palanisamy's approach, set the source content as an html input and set its viewport to render content inside parent container.
<View>
<WebView
javaScriptEnabled={false}
domStorageEnabled={false}
startInLoadingState={false}
scalesPageToFit={false}
scrollEnabled={true}
source={{
html: `
<head>
<meta content="width=width, initial-scale=1, maximum-scale=1" name="viewport"></meta>
</head>
<body style="background-image: url('${this.props.source}'); background-size:cover;"></body>
`,
}}
/>
</View>
This is is more better and accurate
style={{ height: 350, width: '100%', resizeMode: 'cover', flex: 1 }}
injectedJavaScript={`const meta = document.createElement('meta'); meta.setAttribute('content', 'width=width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'); meta.setAttribute('name', 'viewport'); document.getElementsByTagName('head')[0].appendChild(meta); `}
scalesPageToFit={true}
I believe you want this:
<WebView
source={this.props.source}
style={{
width: '100%',
height: 300
}}
scrollEnabled={false}
/>;
(Note: I'm using Expo for this app)
I'm attempting to render a FlatList that displays a list of printers. Here's the code:
<FlatList
data={printers}
keyExtractor={printer => printer.id}
renderItem={({ item }) => {
return (
<Printer
printerTitle={item.name}
selected={item.selected}
last={item === last(printers)}
/>
);
}}
/>
Here's the code for the <Printer /> component:
const Printer = props => {
const { last, printerTitle, selected } = props;
return (
<View style={[styles.container, last ? styles.lastContainer : null]}>
<View style={styles.innerContainer}>
<View style={styles.leftContainter}>
{selected ? (
<Image source={selected ? Images.checkedCircle : null} />
) : null}
</View>
<View style={styles.printerDetails}>
<Text style={styles.printerTitle}>{printerTitle}</Text>
</View>
</View>
</View>
);
};
...
export default Printer;
I can't seem to get the <Printer /> component to render. I have tried including a similar custom component (that has worked in a FlatList in another part of the app) in the renderItem prop, and it doesn't work either.
However, when I replace the <Printer /> component with <Text>{item.name}</Text> component, the printer name renders like I would expect it to.
Has anyone run into this issue before, or does anyone have a solution?
In my case, where I'm rendering a custom component for each item in the list, it wasn't rendering because I accidentally had {} surrounding the return part of the renderItem prop instead of ().
Changing:
<FlatList
data={array}
renderItem={({item, index}) => { <CustomItemComponent /> }}
/>
to this:
<FlatList
data={array}
renderItem={({item, index}) => ( <CustomItemComponent /> )}
/>
Solved my issues.
I suspect there are two issues at hand: one is that your FlatList is not filling the screen (namely its parent view) and the other is that your Printer component is not being sized correctly.
For the first issue, put a style with { flex: 1 } on your FlatList. This way it will fill its parent view.
For the second issue, try adding { borderColor: 'red', borderWidth: 1 } to your Printer components, just so that you can more easily see where they're being rendered. If they seem like they have no width, make sure you haven't overridden alignSelf on the Printer component's root view. If they seem like they have no height, add height: 100 temporarily just so you can see what the contents of the Printer components look like.
Within your Printer component, make sure to specify the width and height of your image on the Image component like { width: 40, height: 30 } or whatever the dimensions of your image is in logical pixels.
I have same problem.
Resolve with adding width to FlatList
render() {
const dimensions = Dimensions.get('window');
const screenWidth = dimensions.width;
return(
<FlatList
style={{
flex: 1,
width: screenWidth,
}}
... some code here
/>
)
}
You can't use the keyExtractor in this way, make this function like below. It might solve your problem.
_keyExtractor = (item, index) => index;
If you update your question with you printer component code we can help you better.
In my case I accidentally made it a pair tag: <FlatList></FlatList>, which for some reason breaks rendering of list items.
in my case Container was not having width of 100%:
const Container = styled.View`
border: 1px solid #ececec;
margin-top: 43px;
padding-top: 36px
padding-bottom: 112px;
width: 100%;
`;
const StyledFlatList = styled(
FlatList as new () => FlatList<SimilarRewards_viewer['merchants']['edges'][0]>
)`
width: 100%;
height: 150px;
flex: 1;
padding-left: 15px;
padding-right: 15px;
`;