How to range ScollView and Button in a row in react native? - ios

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'
}
})

Related

any Scroll events such as onScroll do not work with FlatList omponent

So I have a project using React-Native on iOS using <Flatlist/> component that is being rendered conditionally using a function renderContent() if a cart data is empty under a view like so:
<SafeAreaView style={styles.container}>
<Navigator.Config
onAppear={this.handleGetData}
onDisappear={this.handleOnDisappear}
rightButtons={deleteButton}
onRightPress={this.handleDeleteButton}
>
{this.renderContent()}
</Navigator.Config>
</SafeAreaView>
where the renderContent() function is like so :
renderContent = () => {
const { cart, cart: { cartObject } } = this.props
const { shop_group: shopGroup } = cartObject
if (
(cart.isNoResult && !cart.isLoadingCartList) ||
!this.state.isUserLogin ||
(shopGroup && shopGroup.length < 1 && !cart.isLoadingCartList)
) {
return this.renderNoResult()
}
return this.renderData()
}
the <NoResultView/> component
<View style={{ flex: 1, backgroundColor: 'white' }}>
<View>
<View style={{ paddingTop: noLogin ? 50 : 30, alignItems: 'center' }}>
...
{
cartData.isLoadingRecommended == false ?
<SuggestionsView title="Rekomendasi"
data={cartData.recommended ? cartData.recommended.recommendation : []}
renderItem={renderItemRecommended}
onScroll={handleScroll}
onEndReached={(info)=>{
// if(!cartData.isLoadingRecommended && cartData.recommended){
console.log('----------** END **------------', info, cartData.isLoadingRecommended)
// }
}}
/>
:
<ActivityIndicator animating size="small" />
}
</View>
and the <FlatList/> component is rendered inside `
import { View, Text, Image, FlatList, TouchableOpacity } from 'react-native'
import React, { Component } from 'react'
import Icon from 'react-native-vector-icons/EvilIcons'
import { ReactTPRoutes } from 'NativeModules'
class SuggestionsView extends Component {
constructor(props){
super(props)
this.state = {
isRefreshing: false,
isLoading: false,
isLoadingMorePost: true,
hasNextPage: false,
reachFeedEnd: false,
shouldDisplayErrorToaster: false,
isError: false,
}
}
handlePressViewAll = () => this.props.onPressViewAll()
keyExtractor = (item, index) => index;
render(){
return (
<View style={{
paddingLeft: 16,
paddingRight: 16,
}}>
</View>
<FlatList
style={{ flex: 1 }}
numColumns={2}
data={this.props.data}
keyExtractor={this.keyExtractor}
renderItem={this.props.renderItem}
onEndReachedThreshold={0.3}
scrollEventThrottle={16}
onEndReached={(info)=>{
if(this.props.onEndReached){
this.props.onEndReached(info)
}
}}
ref={x => {
this.flatist = x // tslint:disable-line
}}
onScroll={()=> console.log('test')}
/>
</View>
)}
}
export default SuggestionsView
Although for some reason the onScroll Method is not firing at all. The scrollToEnd method is also not firing for some reason. Any help is appreciated thank you.

React Native's React-Navigation: Adding a badge in TabNavigator's Tab

In React-Native with React-Navigation I have a Tabnavigator like so:
const testScreenNavigator = TabNavigator({
Tab1: { screen: Tab1Screen },
Tab2: { screen: Tab2Screen },
Tab3: { screen: Tab3Screen },
});
testScreenNavigator.navigationOptions = {
title: 'MY TITLE',
header: {
titleStyle:{
},
style:{
// how to set the options?
},
}
}
Now I want to add a badge next to Tab1: e.g.
Tab1 (2) | Tab2 | Tab3
In Android this can be done via:
static navigationOptions = {
tabBar: {
label: () => {
...
return (
<Text style={{ backgroundColor: '...', borderRadius: 10}}>
{badgeNumber}
</Text>
...
In iOS it displays the TabMenu at the bottom, which is ok, since it is the native behavior of iOS. But in iOS the circle of the badge does not show, but a rectangular background instead.
Why is that and/or how would a Badge be done in iOS?
Regards
There is actually a badge-package in RN:
https://github.com/react-native-component/react-native-smart-badge .
import React, {Component } from 'react';
import {Animated ,Text,View,AppRegistry,Button, StyleSheet,Image } from 'react-native';
// Badge
export default class App extends Component {
state = {
badgeScale : new Animated.Value(0),
textValue :0,
}
animatedBadge(){
this.state.badgeScale.setValue(0);
const newTextValue = ++this.state.textValue
this.setState({textValue: newTextValue})
Animated.timing(this.state.badgeScale , {
toValue : 1,
duration : 500
}).start()
}
render(){
const msize = 40;
return(
<View style={styles.container}>
<View style={{ width :100, height :100, borderRadius :50, margin:10,}}>
<View
style={{ width :100, height :100, backgroundColor:'green', borderRadius :50,}}
/>
{/* <Image
source={require('./circle.png')} // style={imageStyle}
style={{ width :100, height :100, borderRadius :50,}}
/> */}
<Animated.View style={{
position: 'absolute', width:msize, height:msize,
borderRadius:msize/2, backgroundColor:'black',
justifyContent:'center', alignContent:'center',
borderColor:'green',borderWidth:1,
// left:0, top:0,
left:70, top:0,
// using this change bedge position
transform:[
{
scale:this.state.badgeScale
}
]
}}>
<Text style={{backgroundColor :'transparent' ,
textAlign:'center',
color:'red'}}>
{this.state.textValue}
</Text>
</Animated.View>
<Button style={{ flex:1 , marginTop:50,justifyContent:'center',
alignContent:'center', }}
title='Add'
onPress={ () =>this.animatedBadge() }>
</Button>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems :'center',
// backgroundColor:'#F5FCFF'
},
imageStyle :{
width:200,
height:200,
},
viewTextStyle:{
position : 'absolute',
justifyContent:'center',
alignItems:'center',
},
textStyle:{
fontSize:23,
fontWeight:'bold',
color:'white'
}
})

Set height of ListView in React Native

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.

react-native: image won't rerender when state changes

I have an icon as an image and I want to change the icon when a state property changes. Here is the relevant code:
<TouchableHighlight underlayColor="rgba(0,0,0,0)" style={styles.playButton} onPress={this._handleStartPress}>
<Image source={(this.state.started) ? require('./Control-pause.png') : require('./Control-play.png')} resizeMode="contain" style={styles.icon}/>
</TouchableHighlight>
The state changes correctly as expected (verifed by some console logs), but somehow the Image won't re render and change when this.state.started changes. The path to the images is also correct.
Any Ideas what's the problem?
EDIT: The whole component:
import React, {
AppRegistry,
Component,
StyleSheet,
Text,
TouchableHighlight,
View,
ScrollView,
Vibration,
AlertIOS,
Image
} from 'react-native'
/*import Icon from 'react-native-vector-icons/FontAwesome';*/
const timer = require('react-native-timer');
const Button = require('./components/Button.js');
const PlayIcon = require('./Control-play.png');
const PauseIcon = require('./Control-pause.png');
class Project extends Component {
constructor(props) {
super(props);
this.state = {
timerValue: 25*60,
count: 0,
started: false,
};
this._tick = this._tick.bind(this);
this._runClock = this._runClock.bind(this);
this._stopClock = this._stopClock.bind(this);
this._handlePomodoroPress = this._handlePomodoroPress.bind(this);
this._handlePausePress = this._handlePausePress.bind(this);
this._getMinsSecs = this._getMinsSecs.bind(this);
this._finishedTimer = this._finishedTimer.bind(this);
this._handleStartPress = this._handleStartPress.bind(this);
}
_tick() {
if (this.state.timerValue > 0) {
this.setState({timerValue: this.state.timerValue - 1});
} else {
this._finishedTimer();
}
}
_finishedTimer() {
this.setState({started: false});
timer.clearInterval('timer');
Vibration.vibrate();
AlertIOS.alert("Time's up!");
}
_runClock() {
this.setState({started: true});
console.log("running: ", this.state.started);
timer.setInterval('timer', this._tick, 1000);
}
_stopClock() {
this.setState({started: false});
console.log("running: ", this.state.started);
timer.clearInterval('timer');
}
_getMinsSecs(seconds) {
let mins = Math.floor(seconds / 60);
let secs = seconds - mins * 60;
return (mins < 10 ? "0" : "") + mins + ":" + (secs <10 ? "0" : "") + secs;
}
_handleStartPress() {
if (!this.state.started) {
this._runClock();
} else {
this._stopClock();
}
}
_handlePomodoroPress() {
if (!this.state.started) {
this.setState({timerValue: 25*60});
}
}
_handlePausePress() {
if(!this.state.started) {
this.setState({ timerValue: 5*60 });
}
}
render() {
return (
<View style={styles.container}>
<View style={styles.timeWrapper}>
<View style={styles.line}/>
<Text style={styles.time}>{this._getMinsSecs(this.state.timerValue)}</Text>
<View style={styles.line}/>
</View>
<TouchableHighlight underlayColor="rgba(0,0,0,0)" style={styles.playButton} onPress={this._handleStartPress}>
<Image source={(this.state.started) ? require('./Control-pause.png') : require('./Control-play.png')} resizeMode="contain" style={styles.icon}/>
</TouchableHighlight>
<View style={styles.buttonWrapper}>
<Button
value="Pomodoro"
onPress={this._handlePomodoroPress}/>
<Button value="Pause" onPress={this._handlePausePress}/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "space-around",
alignItems: 'center',
backgroundColor: "#7CCF9E"
},
time: {
fontSize: 74,
color: '#fff',
fontWeight: '200'
},
buttonWrapper: {
justifyContent: 'center',
alignItems: 'center'
},
playButton: {
width: 79,
height: 79,
borderRadius: 100,
borderWidth: 3,
borderColor: '#fff',
alignItems: 'center',
justifyContent: 'center'
},
line: {
marginTop: 10,
height: 3,
width: 200,
backgroundColor: '#fff'
},
timeWrapper: {
alignItems: 'center'
},
icon: {
height: 42,
}
});
AppRegistry.registerComponent('Project', () => Project);
something like this works easily:
<TouchableHighlight underlayColor="rgba(0,0,0,0)" style={styles.playButton} onPress={this._handleStartPress}>
<Text>{this.state.started ? "started" : "stopped"}</Text>
</TouchableHighlight>
EDIT2:
I found what causes the picture not to rerender!!!!
When I style the size in the StyleSheet it won't rerender ... If it has no size style everything is fine!
require calls are not dynamic. They are statically analyzed and bundled. https://github.com/facebook/react-native/issues/2481 . As Andrew Axton suggested, load them in separate variables outside of render and using that in the conditional should work.

How to styles view to a ratio like 16:9 in React Native?

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>
);
}
});

Resources