I need to set width and height of ListView. While setting width works as expected, setting height has no effect and ListView is always stretching into almost bottom of the screen (there is only margin between bootom of screen and bottom of ListView). I am creating ListView in render method this way:
<ListView ref={component => this._stationsListFrom = component} style={styles.stationsList} dataSource={this.state.dataSource} renderRow={(rowData) => <Text>{rowData}</Text>} />
This is its style:
stationsList: {
backgroundColor: 'white',
height: 0,
}
I have also tried to set its height in a method by this command:
this._stationsListFrom.setNativeProps({height: 200});
When I have tried to set width using this command, it worked. But setting height does nothing.
How can I set height of ListView (for example, in the case of TextInput its not a problem) to desired value? Only way I wound is to use bottom margin, but that is not the way I want to use.
I am testing on iOS only (for the case it works differently on Android).
My code:
import React, { Component } from 'react';
import Dimensions from 'Dimensions';
import {
AppRegistry,
StyleSheet,
Text,
TextInput,
ListView,
Button,
View
} from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-start',
alignItems: 'flex-start',
backgroundColor: '#D8CD36',
padding: 25
},
label: {
textAlign: 'left',
color: '#333333',
fontSize: 20,
margin: 5,
},
textInput: {
height: 40,
borderColor: 'black',
borderWidth: 2,
padding: 7,
},
stationsList: {
backgroundColor: 'white',
height: 0,
},
separator: {
flex: 1,
height: StyleSheet.hairlineWidth,
backgroundColor: '#8E8E8E',
},
menuButton: {
},
},
);
export default class TestApp extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); this.state = { dataSource: ds.cloneWithRows(['Žilina', 'Košice', 'Vrútky']), };
}
render() {
return (
<View style={styles.container}>
<Text style={styles.label}>
Z
</Text>
<TextInput ref={component => this._textInputFrom = component} style={styles.textInput} placeholder="Východzia stanica" onChangeText={this.fromTextChange.bind(this)} onLayout={(event) => { this.correctMenuFromWidth(event.nativeEvent.layout) }} renderSeparator={(sectionId, rowId) => <View key={rowId} style={styles.separator}/>} />
<Text style={styles.label}>
Do
</Text>
<TextInput style={styles.textInput} placeholder="Cieľová stanica"/>
<ListView ref={component => this._stationsListFrom = component} style={styles.stationsList} dataSource={this.state.dataSource} renderRow={(rowData) => <Button onPress={this.menuFromButtonPressed} style={styles.menuButton} title={rowData} />} />
</View>
);
}
correctMenuFromWidth(layout) {
const {x, y, width, height} = layout;
this._stationsListFrom.setNativeProps({marginTop: -74, width: width});
}
menuFromButtonPressed() {
};
fromTextChange() {
this._textInputFrom.setNativeProps({text: 'Kraľovany'});
this._stationsListFrom.setNativeProps({height: 200});
};
}
AppRegistry.registerComponent('TestApp', () => TestApp);
Move ListView inside wrapper and set height to wrapper:
<View style={{height: 200}}>
<ListView .../>
</View>
From ScrollView docs (ListView uses ScrollView):
Keep in mind that ScrollViews must have a bounded height in order to work, since they contain unbounded-height children into a bounded container (via a scroll interaction). In order to bound the height of a ScrollView, either set the height of the view directly (discouraged) or make sure all parent views have bounded height.
Related
I put a button in absolute position to on WebView.
This button works on any simulators and my iPhone 5s real device, but not work on iPhone Plus real device.
The button reacts when tapping, but doesn't fire onPress. It works when set position as relative.
All simulators and devices are same iOS version (12.2).
Does anyone know about this issue?
Environments
React Native 0.56
iOS 12.2
import React, { Component } from 'react';
import { ActivityIndicator, BackHandler, Keyboard, NetInfo, Platform, ScrollView, StatusBar, StyleSheet, Text, TouchableOpacity, View, WebView } from 'react-native';
import { Button, Icon } from 'react-native-elements'
export default class SignInScreen extends React.Component {
constructor(props) {
super(props)
}
handleBackPress() {
this.refs['WEBVIEW_REF'].goBack()
return true
}
render() {
return (
<View style={styles.container}>
<StatusBar backgroundColor="white" barStyle="dark-content" />
<WebView
ref="WEBVIEW_REF"
bounces={false}
source={{ uri: 'https://example.com' }}
style={styles.container}
/>
<Button
onPress={this.handleBackPress.bind(this) }
buttonStyle={styles.buttonBack}
title=""
icon={{
name: 'chevron-left',
type: 'material-community',
iconStyle: { color: global.color.normal }
}}
/>
</View>
);
}
}
const styles = StyleSheet.create({
buttonBack: {
width: 45,
height: 45,
position: 'absolute',
left: 10,
bottom: 60,
backgroundColor: '#bfbfbf',
opacity: 0.9
},
container: {
flex: 1,
paddingTop: 25,
backgroundColor: '#fff'
},
});
Position absolute does not work properly in some cases at ios. you should wrap in a :
<View style={{position:'absolute',...other styles}}>
<Button/> // without position style
<View>
Use padding on your button,it will solve.
You need to use Dimensions in this case. For example in your case you can use it like this:
buttonBack: {
width: Dimensions.get('window').width/100*10,
height: Dimensions.get('window').height/100*10,
position: 'absolute',
left: Dimensions.get('window').width/100*2,
bottom: Dimensions.get('window').height/100*5,
backgroundColor: '#bfbfbf',
opacity: 0.9
},
Dimensions.get('window').width/100*10 means 10 percent of total device
screen width and same for height.
Dimensions work as device specific. So it will take the same position according to Device Screen Height and Width.
Fixed by wrapping absolute position View.
render() {
return (
<View style={styles.container}>
<StatusBar backgroundColor="white" barStyle="dark-content" />
<WebView
ref="WEBVIEW_REF"
bounces={false}
source={{ uri: 'https://example.com' }}
style={styles.container}
/>
<View style={styles.navContainer}>
<Button
onPress={this.handleBackPress.bind(this) }
buttonStyle={styles.buttonBack}
title=""
icon={{
name: 'chevron-left',
type: 'material-community',
iconStyle: { color: global.color.normal }
}}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
buttonBack: {
width: 45,
height: 45,
left: 10,
backgroundColor: '#bfbfbf',
opacity: 0.9
},
// moved from buttonBack style
navContainer: {
position: 'absolute',
bottom: 60
},
//
container: {
flex: 1,
paddingTop: 25,
backgroundColor: '#fff'
},
});
When the View mounts, I would like the secondary view to be off the screen to the right of the main view.
When the user pan swipes left, the secondary view should follow the pan and eventually cover the main view. (like Instagram camera)
I have a basic structure set up, but I'm not sure how to finalize the <Animated.View> part. For example, do I put the pan handler on the main view or on the secondary view? I don't want the secondary view to cover any interactions on the main view.
import React from 'react';
import { PanResponder, Animated, Dimensions, StyleSheet, Text, View } from 'react-native';
const winWidth = Dimensions.get('window').width
const winHeight = Dimensions.get('window').height
class Main extends React.Component {
constructor(props){
super(props);
}
translateX = new Animated.Value(0);
panResponder = PanResponder.create({
onMoveShouldSetPanResponderCapture: (e, gs) => {
return gs.dx < 0 //allow left only
},
onPanResponderMove: (e, gs) => {
if(gs.dx < 0) {
Animated.event([null, {dx: this.translateX}])(e, gs)
}
},
onPanResponderRelease: (e, {vx, dx}) => {
//Secondary View should now cover the main View
},
onPanResponderTerminationRequest: (e, gs) => {
return false
},
onPanResponderTerminate: (e, {vx, dx} ) => {
},
})
render(){
return(
<View style={{styles.main}}>
<View style={{styles.secondary}}></View>
<View><Text>Other components will be displayed</Text></View>
</View>
)
}
}
const styles = StyleSheet.create({
main:{
flex:1,
flexDirection:'column',
backgroundColor: '#ffffff'
},
secondary:{
width: winWidth,
height: winHeight,
backgroundColor: 'red',
position: 'absolute',
top:0,
left:winWidth, //start off screen
},
})
I am not sure if this is what you are after but the way I did it was nesting two View inside the Animated.View Component which cover the full screen.
<Animated.View style={[this.position.getLayout(), {display: 'flex', flex: 1, flexDirection: 'row'}]} {...this.panResponder.panHandlers}>
<View style={{width: '100%', height: '100%', backgroundColor: 'yellow'}}>
<Text>
This is the Main view
</Text>
</View>
<View style={{ height: '100%', width: '100%', backgroundColor: 'red'}}>
<Text>
This is the invisible View
</Text>
</View>
</Animated.View>
The styling in the Views is to make each cover the full width
inside the Animated.View I used the array notation to apply two styles
this.position.getLayout() is the one that gives the coordinates/position of where the Animated.View component should be
The other styling object is so we can render the child views next to each other using flexbox particularly setting
Also attach the panHandlers to the Animated.View
constructor(props){
super(props)
position = new Animated.ValueXY()
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: ()=> true,
onPanResponderMove: (evt, gesture)=>{
//Need to set theshhold and state.isOpen to determine direction here
position.setValue({x: gesture.dx, y: 0})
},
onPanResponderGrant: ()=>{
this.position.setOffset({x: this.position.x._value, y: 0})
this.position.setValue({x: 0, y: 0})
},
onPanResponderRelease: ()=>{
this.openOrClose()
}
})
this.state = {isOpen: false}
this.panResponder = panResponder
this.position = position
}
I used state to monitor where the View Should move to this.state = {isOpen: false} depending on which view was visible.
The functions to move are mainly position.setValue({x: gesture.dx, y: 0}) which tracks the movement while the touch/pan is still active and this.openOrClose() which is called when the touch/pan is released.
The openOrClose function determines how to handle the movement and the main logic should be here. I just did a simple case and did not include any threshHold in this example.
To understand panHandlers I suggest you read this article as it explains the reasons for onPanResponderGrant best.
Below is the code for the working Component
import React, {Component} from 'react'
import {View, Text, Animated, PanResponder, Dimensions} from 'react-native'
const ScreenWidth = Dimensions.get('window').width
//Logic to flattenOffset required
export class Swipable extends Component{
constructor(props){
super(props)
position = new Animated.ValueXY()
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: ()=> true,
onPanResponderMove: (evt, gesture)=>{
//Need to set theshhold and state.isOpen to determine direction here
position.setValue({x: gesture.dx, y: 0})
},
onPanResponderGrant: ()=>{
this.position.setOffset({x: this.position.x._value, y: 0})
this.position.setValue({x: 0, y: 0})
},
onPanResponderRelease: ()=>{
this.openOrClose()
}
})
this.state = {isOpen: false}
this.panResponder = panResponder
this.position = position
}
openOrClose = ()=>{
this.position.flattenOffset()
//determine where to move depending onState
direction = this.state.isOpen ? 0 : -ScreenWidth
Animated.spring(this.position, {
toValue: {x: direction, y: 0}
}).start(()=>{
//Callback when animation is complete to show if open or closed
this.setState((prevState)=>{
return {isOpen: !prevState.isOpen}
})
})
}
render(){
return(
<Animated.View style={[this.position.getLayout(), {display: 'flex', flex: 1, flexDirection: 'row'}]} {...this.panResponder.panHandlers}>
<View style={{width: '100%', height: '100%', backgroundColor: 'yellow'}}>
<Text>
This is the Main view
</Text>
</View>
<View style={{ height: '100%', width: '100%', backgroundColor: 'red'}}>
<Text>
This is the invisible View
</Text>
</View>
</Animated.View>
)
}
}
I ran the above code on an Android emulator, I am sure it will work on ios. If there is anything I need to clarify or improve please do tell me
I wanna to place a ScrollView and Button in a row, but It doesn't work, neither paddingLeft or width.
Without the Button the NaviScrollView will fill the hole view even I had set the width; with the Button and the Button will fill the hole view. Why?
export class Home extends Component {
render() {
return (
<View style={ styles.container }>
<NaviScrollView data= { fakeData }/>
<Button title="添加" color='gray' style={{right:0, width:20}}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'red',
top: 20,
height: 44
},
})
class NaviScrollView extends Component {
render(){
return (
<ScrollView horizontal={true}>
{
this.props.data.map((item, index) =>
<Button key={ index } title={ item.title } onPress={ () => this._buttonPressed(item.title + item.url)}
color = 'black'/>)
}
</ScrollView>
);
}
}
Using style property : flexDirection : 'row' for View tag to organize childrens in a row or flexDirection : 'column' to organize childrens in a column.
I make a simple example base on your project. I didn't know its similar with your project or not but just take a look.
export default class Home extends Component {
render(){
return(
<View style={ styles.container }>
<ScrollView>
<Text>ABC</Text>
<Text>DEF</Text>
<Text>GHI</Text>
<Text>JKL</Text>
<Text>NMO</Text>
</ScrollView>
<TouchableOpacity style={styles.press}>
<Text>Press Me</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
button : {
width : 50,
height : 50
},
container: {
flexDirection : 'row',
backgroundColor: 'red',
top: 20,
height: 44
},
press: {
width: 150,
padding: 10,
backgroundColor : 'yellow'
}
})
I'm building an iOS app with React Native and am implementing a TabBarIOS. The content on the tabs seems to flow behind and be obscured by the bar. In xcode I would have just unchecked the "extend edges" boxes but am not sure how to do this with React Native.
Here's an abbreviated version of what I'm trying to do. The <View> from CreateUser flows behind the tab bar. Is there an easy way to make sure content doesn't get obscured by the tab bar?
import React from 'react'
import {
StyleSheet,
Text,
TextInput,
View,
TouchableHighlight,
} from 'react-native'
export default class TabBar extends React.Component {
state = {
selectedTab: 'list'
}
render() {
return (
<TabBarIOS selectedTab={this.state.selectedTab}
unselectedTintColor="#ffffff"
tintColor="#ffe429"
barTintColor="#294163">
<TabBarIOS.Item
title="My List"
systemIcon="bookmarks"
selected={this.state.selectedTab==='list'}
onPress={() => {
this.setState({
selectedTab: 'list',
});
}}
>
<CreateUser />
</TabBarIOS.Item>
</TabBarIOS>
);
}
}
var styles = StyleSheet.create({
tabContent: {
flex: 1,
alignItems: 'center',
},
tabText: {
color: 'darkslategrey',
margin: 50,
},
});
export default class CreateUser extends React.Component{
render(){
return (
<View style={styles.container}>
<TouchableHighlight style={styles.button}>
<Text style={styles.buttonText}>LOG IN</Text>
</TouchableHighlight>
</View>
)
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column",
justifyContent: "flex-end",
alignItems: 'center',
},
button: {
backgroundColor: "#ffe429",
borderRadius: 3,
height: 60,
width: 200,
margin: 7,
//flex: 1,
alignItems: "center",
justifyContent: "center",
},
buttonText: {
color: "#294163",
}
})
This was a problem for me when using a NavigatorIOS component that then rendered it's initialRoute component which contained a TabBarIOS component.
If this is the case for your scenario, you can fix it by using a ScrollView:
Add the flex layout style to your NavigatorIOS:
<NavigatorIOS
initialRoute={{
component: MyView,
title: 'My View',
}}
style={{flex: 1}}
/>
Use a ScrollView:
<TabBarIOS>
<TabBarIOS.Item
systemIcon="history"
title="A Tab">
<ScrollView>
<Text>
Hello World
</Text>
</ScrollView>
</TabBarIOS.Item>
</TabBarIOS>
In my case I needed to add flex: 1 to the style props for the top-level View of the screen.
RN Navigation docs
//MyScreen.tsx
export default () => (
<View style={{ flex: 1 }}>
...
</View>
)
I want to let the red view keep ratio 16:9. I try but failed. I know React Native use Flexbox (Reimplement in Javascript), but I don't know how to do this. Thanks.
Here is my Javascript:
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
View,
} = React;
var AwesomeProject = React.createClass({
render: function() {
return (
<View style={styles.container}>
<View style={styles.banner}>
</View>
<View style={styles.items}>
</View>
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
},
banner: {
backgroundColor: 'red',
flex: 1,
},
items: {
backgroundColor: 'blue',
flex: 3,
},
});
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
Here is document about Flexbox in React Native:
http://facebook.github.io/react-native/docs/flexbox.html#content
Here is valid style props:
Valid style props: [
"width",
"height",
"top",
"left",
"right",
"bottom",
"margin",
"marginVertical",
"marginHorizontal",
"marginTop",
"marginBottom",
"marginLeft",
"marginRight",
"borderWidth",
"borderTopWidth",
"borderRightWidth",
"borderBottomWidth",
"borderLeftWidth",
"position",
"flexDirection",
"flexWrap",
"justifyContent",
"alignItems",
"alignSelf",
"flex",
"resizeMode",
"backgroundColor",
"borderColor",
"borderRadius",
"tintColor",
"opacity",
"fontFamily",
"fontSize",
"fontWeight",
"fontStyle",
"lineHeight",
"color",
"containerBackgroundColor",
"textAlign",
"writingDirection",
"padding",
"paddingVertical",
"paddingHorizontal",
"paddingTop",
"paddingBottom",
"paddingLeft",
"paddingRight",
"borderTopColor",
"borderRightColor",
"borderBottomColor",
"borderLeftColor",
"overflow",
"shadowColor",
"shadowOffset",
"shadowOpacity",
"shadowRadius",
"transformMatrix",
"rotation",
"scaleX",
"scaleY",
"translateX",
"translateY"
]"
React Native (since 0.40) supports the aspectRatio prop.
You can do:
style={{ aspectRatio: 16/9 }}
See Maintain aspect ratio of image with full width in React Native
You can use on layout function.
class AwesomeProject = extends React.Component<{}> {
constructor(props) {
super(props)
this.state = {width: 0,height:0}
}
onPageLayout = (event) => {
const {width, height} = event.nativeEvent.layout;
this.setState({
width,
height
})
};
render(){
return (
<View style={styles.container}>
<View style={[
styles.banner,
{
height:this.state.width*9/16
}
]}>
</View>
<View style={styles.items}>
</View>
</View>
);
}
});