Can create custom calendar in react native without any dependencies? - react-native

There is any way that can create custom calendar in react native without any dependencies like react-native-calendars etc.

here is the code for custom calendar in react-native without any dependencies.
import * as React from 'react';
import * as RN from 'react-native';
class App extends React.Component {
months = ["January", "February", "March", "April",
"May", "June", "July", "August", "September", "October",
"November", "December"
];
_onPress = (item) => {
this.setState(() => {
if (!item.match && item != -1) {
this.state.activeDate.setDate(item);
return this.state;
}
});
};
changeMonth = (n) => {
this.setState(() => {
this.state.activeDate.setMonth(
this.state.activeDate.getMonth() + n
)
return this.state;
});
}
weekDays = [
"Sun","Mon","Tue","Wed","Thu","Fri","Sat"
];
nDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
state = {
activeDate: new Date()
}
generateMatrix() {
var matrix = [];
// Create header
matrix[0] = this.weekDays;
var year = this.state.activeDate.getFullYear();
var month = this.state.activeDate.getMonth();
var firstDay = new Date(year, month, 1).getDay();
var maxDays = this.nDays[month];
if (month == 1) { // February
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
maxDays += 1;
}
}
var counter = 1;
for (var row = 1; row < 7; row++) {
matrix[row] = [];
for (var col = 0; col < 7; col++) {
matrix[row][col] = -1;
if (row == 1 && col >= firstDay) {
// Fill in rows only after the first day of the month
matrix[row][col] = counter++;
} else if (row > 1 && counter <= maxDays) {
// Fill in rows only if the counter's not greater than
// the number of days in the month
matrix[row][col] = counter++;
}
}
}
return matrix;
}
render() {
var matrix = this.generateMatrix();
var rows = [];
rows = matrix.map((row, rowIndex) => {
var rowItems = row.map((item, colIndex) => {
return (
<RN.Text
style={{
flex: 1,
height: 18,
textAlign: 'center',
// Highlight header
backgroundColor: rowIndex == 0 ? '#ddd' : '#fff',
// Highlight Sundays
color: colIndex == 0 ? '#a00' : '#000',
// Highlight current date
fontWeight: item == this.state.activeDate.getDate()
? 'bold': ''
}}
onPress={() => this._onPress(item)}>
{item != -1 ? item : ''}
</RN.Text>
);
});
return (
<RN.View
style={{
flex: 1,
flexDirection: 'row',
padding: 15,
justifyContent: 'space-around',
alignItems: 'center',
}}>
{rowItems}
</RN.View>
);
});
return (
<RN.View>
<RN.Text style={{
fontWeight: 'bold',
fontSize: 18,
textAlign: 'center'
}}>
{this.months[this.state.activeDate.getMonth()]}
{this.state.activeDate.getFullYear()}
</RN.Text>
{ rows }
<RN.Button title="Previous"
onPress={() => this.changeMonth(-1)}/>
<RN.Button title="Next"
onPress={() => this.changeMonth(+1)}/>
</RN.View>
);
}
}
// Export for now to get rid of error and see preview:
export default App

Related

Cant show alert while trying to make a simple react-native-app

Im trying to make a tic-tack-toe game as a simple react-native app. But I cant make the alert work while trying to get the game to alert that player 1 or two wins when getting "3 in row". Can somebody see what I did wrong or give me some advice.
Everything is working exept that the Alert and the this.initializeGame(); under the part where I want to get the winner.
If someone would know if there is a "better" practice to name the variables const or let im also wondering that. :)
Thank you!
Here is the code:
import * as React from 'react';
import { Text, View, StyleSheet, TouchableOpacity, Alert, } from 'react-native';
import { MaterialCommunityIcons as Icon } from 'react-native-vector-icons'
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
gameState: [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
],
currentPlayer: 1,
}
}
componentDidMount() {
this.initializeGame();
}
initializeGame = () => {
this.setState({gameState:
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
],
currentPlayer: 1,
});
}
getWinner = () => {
const NUM_TILES = 3;
var arr = this.state.gameState;
var sum;
var i = 0;
//rows
for (i; 1 < NUM_TILES; i++) {
sum = arr[i][0] + arr[i][1] + arr[i][2];
if (sum == 3) { return 1; }
else if (sum == -3) { return -1; }
}
//colums
for (i; 1 < NUM_TILES; i++) {
sum = arr[0][i] + arr[1][i] + arr[2][i];
if (sum == 3) { return 1; }
else if (sum == -3) { return -1; }
}
//diagonals
sum = arr[0][0] + arr[1][1] + arr[2][2];
if (sum == 3) { return 1; }
else if (sum == -3) { return -1; }
sum = arr[2][0] + arr[1][1] + arr[0][2];
if (sum == 3) { return 1; }
else if (sum == -3) { return -1; }
//If no winners
return 0;
}
onTilePress = (row, col) => {
//makes sure that the tiles dont change
var value = this.state.gameState[row][col];
if (value !== 0) { return; }
//sets currant player
var currentPlayer = this.state.currentPlayer;
//sets the correct tile
var arr = this.state.gameState.slice();
arr [row][col] = currentPlayer;
this.setState({gameState: arr});
//switches player
var nextPlayer = (currentPlayer == 1) ? -1 : 1;
this.setState({currentPlayer: nextPlayer});
//check for winners
var winner = this.getWinner();
if (winner == 1) {
Alert.alert("Player 1 is the winner");
this.initializeGame();
} else if (winner == -1) {
Alert.alert("Player 2 is the winner");
this.initializeGame();
}
}
renderIcon = (row, col) => {
var value = this.state.gameState[row][col];
switch(value)
{
case 1: return <Icon name="close" style={styles.tileX} />;
case -1: return <Icon name="circle-outline" style={styles.tileO} />;
default: return <View />;
}
}
render() {
return (
<View style={styles.container}>
<View style={{flexDirection: "row"}}>
<TouchableOpacity onPress={() => this.onTilePress(0, 0)} style={[styles.tile, { borderLeftWidth: 0, borderTopWidth: 0 }]}>
{this.renderIcon(0, 0)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(0, 1)} style={[styles.tile, { borderTopWidth: 0, }]}>
{this.renderIcon(0, 1)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(0, 2)} style={[styles.tile, { borderRightWidth: 0, borderTopWidth: 0 }]}>
{this.renderIcon(0, 2)}
</TouchableOpacity>
</View>
<View style={{flexDirection: "row"}}>
<TouchableOpacity onPress={() => this.onTilePress(1, 0)} style={[styles.tile, { borderLeftWidth: 0 }]}>
{this.renderIcon(1, 0)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(1, 1)} style={[styles.tile, { }]}>
{this.renderIcon(1, 1)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(1, 2)} style={[styles.tile, { borderRightWidth: 0 }]}>
{this.renderIcon(1, 2)}
</TouchableOpacity>
</View>
<View style={{flexDirection: "row"}}>
<TouchableOpacity onPress={() => this.onTilePress(2, 0)} style={[styles.tile, { borderBottomWidth: 0, borderLeftWidth: 0, }]}>
{this.renderIcon(2, 0)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(2, 1)} style={[styles.tile, { borderBottomWidth: 0, }]}>
{this.renderIcon(2, 1)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(2, 2)} style={[styles.tile, { borderBottomWidth: 0, borderRightWidth: 0,
}]}>
{this.renderIcon(2, 2)}
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#fff',
alignItems: 'center',
},
tile: {
borderWidth: 10,
width: 100,
height: 100,
},
tileX: {
color: "red",
fontSize: 60,
},
tileO: {
color: "green",
fontSize: 60,
}
});
Looks like everything you did is fine, except your algorithm for the getWinner() function. There were many things wrong with this function, for example, you had the for loop ending condition as 1 < NUM_OF_TILES where NUM_OF_TILES is 3. And also you have to reinitialize the i to 0 when moving from rows to columns because i is already 2 at the end of first for loop.
I have updated this function for you as follows:
getWinner = () => {
const NUM_TILES = 3;
var arr = this.state.gameState;
var sum;
var i = 0;
//rows
for (i = 0; i < NUM_TILES; i++) {
sum = arr[i][0] + arr[i][1] + arr[i][2];
if (sum == 3) {
return 1;
} else if (sum == -3) {
return -1;
}
}
//colums
for (i = 0; i < NUM_TILES; i++) {
sum = arr[0][i] + arr[1][i] + arr[2][i];
if (sum == 3) {
return 1;
} else if (sum == -3) {
return -1;
}
}
//diagonals
sum = arr[0][0] + arr[1][1] + arr[2][2];
if (sum == 3) {
return 1;
} else if (sum == -3) {
return -1;
}
sum = arr[2][0] + arr[1][1] + arr[0][2];
if (sum == 3) {
return 1;
} else if (sum == -3) {
return -1;
}
//If no winners
return 0;
};
You can find the working code at: https://codesandbox.io/s/react-native-4f2yu I have not tested all use-cases btw, but hopefully it puts you in the right direction.

Use componentDidUpdate to dynamically change selected date in React component

I need to change the selectedDay to the current day every time the user navigates to the calendar screen.
I set the selected date as today and can already detect when the user acesses the screen, but even when I change the state or force update, the calendar doesn't move to the date I set as selected.
componentDidUpdate = async (prevProps) => {
if (prevProps.isFocused !== this.props.isFocused && this.props.isFocused) {
this.forceUpdate()
}
}
and
<Agenda
items={events}
pastScrollRange={50}
futureScrollRange={50}
onDayPress={this.setCurrentDate}
loadItemsForMonth={this.loadItems}
renderItem={this.renderItem}
renderEmptyDate={this.renderEmptyDate}
rowHasChanged={this.rowHasChanged}
selected={this.state.today}
/>
With forceUpdate or changing some arbitrary state, the calendar stays in the currently selected date. I wanted it to go back to today.
You can set the ref for Agenda component
ref={ref => {
this.agenda = ref;
}}
Then on componentDidUpdate, change date to current date by calling function onDayChange of Agenda component
if (prevProps.isFocused !== this.props.isFocused) {
setTimeout(() => {
this.agenda.onDayChange(this.state.today);
}, 500);
Complete Code
import React, { Component } from "react";
import { Agenda } from "react-native-calendars";
import { Text, View, StyleSheet } from "react-native";
import { withNavigationFocus } from "react-navigation";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
items: {},
today: new Date().toISOString().split("T")[0]
};
}
componentDidUpdate(prevProps) {
if (prevProps.isFocused !== this.props.isFocused) {
setTimeout(() => {
this.agenda.onDayChange(this.state.today);
}, 500);
}
}
render() {
return (
<View style={{ flex: 1 }}>
<Text
style={{ padding: 30, fontWeight: "bold", textAlign: "center" }}
onPress={() => this.props.navigation.navigate("NewScreen")}
>
Go To Next Screen
</Text>
<Agenda
items={this.state.items}
loadItemsForMonth={this.loadItems.bind(this)}
selected={this.state.today}
renderItem={this.renderItem.bind(this)}
renderEmptyDate={this.renderEmptyDate.bind(this)}
rowHasChanged={this.rowHasChanged.bind(this)}
onDayPress={day => {
console.log("selected day", day);
}}
ref={ref => {
this.agenda = ref;
}}
/>
</View>
);
}
loadItems(day) {
setTimeout(() => {
for (let i = -15; i < 85; i++) {
const time = day.timestamp + i * 24 * 60 * 60 * 1000;
const strTime = this.timeToString(time);
if (!this.state.items[strTime]) {
this.state.items[strTime] = [];
const numItems = Math.floor(Math.random() * 5);
for (let j = 0; j < numItems; j++) {
this.state.items[strTime].push({
name: "Item for " + strTime,
height: Math.max(50, Math.floor(Math.random() * 150))
});
}
}
}
//console.log(this.state.items);
const newItems = {};
Object.keys(this.state.items).forEach(key => {
newItems[key] = this.state.items[key];
});
this.setState({
items: newItems
});
}, 1000);
// console.log(`Load Items for ${day.year}-${day.month}`);
}
renderItem(item) {
return (
<View style={[styles.item, { height: item.height }]}>
<Text>{item.name}</Text>
</View>
);
}
renderEmptyDate() {
return (
<View style={styles.emptyDate}>
<Text>This is empty date!</Text>
</View>
);
}
rowHasChanged(r1, r2) {
return r1.name !== r2.name;
}
timeToString(time) {
const date = new Date(time);
return date.toISOString().split("T")[0];
}
}
const styles = StyleSheet.create({
item: {
backgroundColor: "white",
flex: 1,
borderRadius: 5,
padding: 10,
marginRight: 10,
marginTop: 17
},
emptyDate: {
height: 15,
flex: 1,
paddingTop: 30
}
});
export default withNavigationFocus(Home);

Why ' i ' don't increase in the for loop?

My data in the imageUrl array . i don't load it with for loop .
loadImage() {
console.log('before', this.state.imagesUrl);
if (this.state.imagesUrl !== undefined && this.state.imagesUrl.length !== 0) {
console.log('after', this.state.imagesUrl);
const n = this.state.imagesUrl.length;
console.log('length' , n);
for ( let i = 0 ; i < n ; i++) {
// i = i + 1
console.log('element', i, this.state.imagesUrl[i]);
return (
<Image
source={{ uri: this.state.imagesUrl[i] }}
style={{ width: 45, height: 45, marginLeft: 2, marginTop: 2, marginRight: 2, borderRadius: 10 }}
/>
)
}
}
}
Use loadImage in ES6 format:
change:
loadImage(){
// ...
}
to:
loadImage = () => {
// ...
}
Then, you have to push to a array:
let arr = [] // empty array
for ( let i = 0 ; i < n ; i++) {
// ...
arr.push(
<Image
source={{ uri: this.state.imagesUrl[i] }}
style={{ styles.Image }} // your styles
/>
)
}
this.setState(arr)
and then in your jsx:
render(){
let {arr} = this.state
return(
<View>
{arr}
</View>
)
}

Using a ScrollView to scroll when ScrollView contains draggable cards - REACT NATIVE

Hopefully you can help me with a bug I'm having a bit of bother sorting out. I'm working on a bug in an app built using React Native. It is building to IOS and Android. I have a ScrollView in a component that contains cards that are draggable objects.
These cards are dragged from the ScrollView they are in, up to buckets at the top of the screen. They disappear from the ScrollView and the remaining ones get reorganised so they stay ordered and neat. That works fine, you press on a box in the list and drag it to the buckets.
There is a bit of whitespace above the list of cards in the ScrollView. The ScrollView functionality works when swiping within this whitespace above the boxes, but I can't swipe on the boxes themselves without it beginning to drag the card.
Here is the component itself:
import React, { Component } from 'react';
import { StyleSheet, Text, View, ScrollView, Dimensions, Alert } from 'react-native';
import { connect } from 'react-redux';
import * as ConstStyles from '../../Consts/styleConsts';
import Bucket from '../Partials/bucketContainers';
import BusyIndicator from 'react-native-busy-indicator';
import loaderHandler from 'react-native-busy-indicator/LoaderHandler';
import CatCard from '../Partials/categoryCard';
import * as FeedActions from '../../../Redux/Feeds/actions';
import * as AuthFunctions from '../../Auth/functions';
export class SetupLikes extends Component {
static navigatorStyle = ConstStyles.standardNav;
constructor(props) {
super(props);
this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
this.card = [];
let button = {
leftButtons: [
{
title: 'Reset',
id: 'reset'
}
],
rightButtons: [
{
title: this.props.newAccount ? 'Go' : 'Done',
id: this.props.newAccount ? '' : 'skip',
disabled: this.props.newAccount
}
]
};
this.props.navigator.setButtons(button);
}
state = {
xOffset: 0,
positions: [],
placedCards: [],
loves: [],
okays: [],
hates: []
};
onNavigatorEvent(event) {
setTimeout(async () => {
if (event.type === 'NavBarButtonPress') {
if (event.id === 'skip') {
this.props.navigator.dismissModal({
animationType: 'slide-down'
});
} else if (event.id === 'reset') {
await this.imgTap();
} else if (event.id === 'go') {
await this.setInterests();
}
}
}, 0);
}
async setInterests() {
loaderHandler.showLoader('Setting your interests...');
let newInterests = [];
this.state.loves.forEach(function(element) {
let cat = this.props.Feeds.categories[element];
let newItem = {
categoryid: cat.id,
sentimentid: 1
};
newInterests.push(newItem);
}, this);
this.state.okays.forEach(function(element) {
let cat = this.props.Feeds.categories[element];
let newItem = {
categoryid: cat.id,
sentimentid: 0
};
newInterests.push(newItem);
}, this);
this.state.hates.forEach(function(element) {
let cat = this.props.Feeds.categories[element];
let newItem = {
categoryid: cat.id,
sentimentid: -1
};
newInterests.push(newItem);
}, this);
let sesId = this.props.User.sessionId;
try {
await this.props.dispatch(FeedActions.setMyInterests(sesId, newInterests));
loaderHandler.hideLoader();
} catch (err) {
loaderHandler.hideLoader();
Alert.alert('Uh oh', 'Something went wrong. Please try again later');
return;
}
await AuthFunctions.setupAppLogin(this.props.dispatch, sesId);
}
async imgTap() {
await this.setState({ placedCards: [], loves: [], okays: [], hates: [], positions: [] });
setTimeout(() => {
let cntr = 0;
this.card.forEach(function(element) {
cntr++;
if (this.state.placedCards.includes(cntr - 1)) return;
if (element) element.snapTo({ index: 0 });
}, this);
}, 5);
this.props.navigator.setButtons({
rightButtons: [
{
title: 'Go',
id: '',
disabled: true
}
],
animated: true
});
}
cardPlaced(id, droppedIndex) {
let newList = this.state.placedCards;
newList.push(id);
let cntr = 0;
let offset = 0;
let newPosIndex = [];
this.props.Feeds.categories.forEach(cats => {
let posY = (offset % 2) * -120 - 20;
let xOffset = Math.floor(offset / 2);
let posX = xOffset * 105 + 10;
newPosIndex[cntr] = {
x: posX,
y: posY,
offset: offset % 2
};
if (!newList.includes(cntr)) offset++;
cntr++;
});
if (droppedIndex === 1) {
let newLoves = this.state.loves;
newLoves.push(id);
this.setState({
loves: newLoves,
placedCards: newList,
positions: newPosIndex
});
} else if (droppedIndex === 2) {
let newOkays = this.state.okays;
newOkays.push(id);
this.setState({
okays: newOkays,
placedCards: newList,
positions: newPosIndex
});
} else if (droppedIndex === 3) {
let newHates = this.state.hates;
newHates.push(id);
this.setState({
hates: newHates,
placedCards: newList,
positions: newPosIndex
});
}
}
reShuffle() {
let cntr = 0;
this.card.forEach(function(element) {
cntr++;
if (this.state.placedCards.includes(cntr - 1)) return;
if (element) element.snapTo({ index: 0 });
}, this);
}
setButton() {
this.props.navigator.setButtons({
rightButtons: [
{
title: this.props.newAccount ? 'Go' : 'Done',
id: 'go'
}
],
animated: true
});
}
onChangeSize(scrollWidth, scrollHeight) {
let { height, width } = Dimensions.get('window');
let farRight = this.state.xOffset + width;
if (farRight > scrollWidth && farRight > 0) {
let xOffset = scrollWidth - width;
this.setState({ xOffset });
}
}
onSnap(index, id) {
this.cardPlaced(id, index);
this.reShuffle();
this.setButton();
if (this.props.Feeds.categories.length === this.state.placedCards.length)
setTimeout(async () => {
await this.setInterests();
}, 1);
}
renderCats() {
let cntr = 0;
var { height, width } = Dimensions.get('window');
let res = this.props.Feeds.categories.map(item => {
let ptr = cntr;
let posY = (cntr % 2) * -120 - 20;
let xOffset = Math.floor(cntr / 2);
let posX = xOffset * 105 + 10;
let vertPos = posY - 200 + ((cntr + 1) % 2) * -120;
posX = this.state.positions[ptr] ? this.state.positions[ptr].x : posX;
posY = this.state.positions[ptr] ? this.state.positions[ptr].y : posY;
let off = this.state.positions[ptr] ? this.state.positions[ptr].offset : ptr % 2;
cntr++;
if (this.state.placedCards.includes(cntr - 1)) return null;
item.key = cntr;
return (
<CatCard
key={ptr}
item={item}
ptr={ptr}
cntr={cntr}
xOffset={this.state.xOffset}
odd={off}
posX={posX}
posY={posY}
yDrop={vertPos}
screenWidth={width}
onSnap={(res, id) => this.onSnap(res, id)}
gotRef={ref => (this.card[ptr] = ref)}
/>
);
});
cntr = 0;
res.forEach(ele => {
if (ele !== null) ele.key = cntr++;
});
let test = this.props.Feeds.categories[0];
return res;
}
onScroll(res) {
this.setState({ xOffset: res.nativeEvent.contentOffset.x });
}
render() {
let colWidth = Math.ceil((this.props.Feeds.categories.length - this.state.placedCards.length) / 2) * 106;
return (
<View style={styles.container}>
<View style={styles.bucketContainer1}>
<Bucket
type={'Love'}
imageToUse={require('../../../img/waveLove.png')}
height={this.state.loveHeight}
count={this.state.loves.length}
backgroundColor={'rgb(238, 136, 205)'}
/>
</View>
<View style={styles.bucketContainer2}>
<Bucket
type={'OK'}
imageToUse={require('../../../img/waveOkay.png')}
height={this.state.okayHeight}
count={this.state.okays.length}
backgroundColor={'rgb(250, 179, 39)'}
/>
</View>
<View style={styles.bucketContainer3}>
<Bucket
type={'Dislike'}
imageToUse={require('../../../img/waveHate.png')}
height={this.state.hateHeight}
count={this.state.hates.length}
backgroundColor={'rgb(112, 127, 208)'}
/>
</View>
<View style={styles.descriptionContainer}>
<Text style={styles.dragLikesTitle}>Drag Likes</Text>
<View style={styles.dividingLine} />
<View>
<Text style={styles.descriptionText}>Drag your likes and dislikes into the bucket above,</Text>
<Text style={styles.descriptionText}>so we can generate your profile!</Text>
</View>
</View>
<ScrollView
ref={ref => (this.scroller = ref)}
onMomentumScrollEnd={res => this.onScroll(res)}
style={styles.scroller}
horizontal={true}
onContentSizeChange={(width, height) => this.onChangeSize(width, height)}
>
<View style={[styles.insideView, { width: colWidth }]}>{this.renderCats()}</View>
</ScrollView>
<BusyIndicator size={'large'} overlayHeight={120} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignSelf: 'stretch',
alignItems: 'center',
backgroundColor: 'white'
},
bucketContainer1: {
position: 'absolute',
height: 130,
width: 95,
left: 10,
top: 5
},
bucketContainer2: {
position: 'absolute',
height: 130,
width: 95,
top: 5
},
bucketContainer3: {
position: 'absolute',
height: 130,
width: 95,
right: 10,
top: 5
},
insideView: {
width: 2500,
justifyContent: 'flex-end',
overflow: 'visible'
},
cardContainer: {
borderWidth: 1,
borderColor: 'rgb(200,200,200)',
borderRadius: 4,
alignItems: 'center',
width: 100,
backgroundColor: 'white'
},
catImage: {
height: 100,
width: 100,
borderTopRightRadius: 4,
borderTopLeftRadius: 4
},
button: {
backgroundColor: 'rgba(255,0,0,0.2)',
width: 90,
height: 50
},
scroller: {
height: '100%',
width: '100%',
overflow: 'visible'
},
card: {
position: 'absolute',
overflow: 'visible'
},
descriptionContainer: {
top: 140,
width: '100%',
alignItems: 'center',
position: 'absolute'
},
dividingLine: {
height: 1,
width: '100%',
borderWidth: 0.5,
borderColor: 'rgb(150,150,150)',
marginBottom: 5
},
dragLikesTitle: {
fontFamily: 'Slackey',
fontSize: 20,
color: 'rgb(100,100,100)'
},
descriptionText: {
fontSize: 12,
textAlign: 'center',
marginTop: 5
}
});
function mapStateToProps(state) {
return {
User: state.User,
Feeds: state.Feeds
};
}
export default connect(mapStateToProps)(SetupLikes);
Down at the bottom of the render function is where you'll see the ScrollView. It's rendering the categories via a function called renderCats.
It may be that because the cards I am rendering are draggable, that fixing this is an impossibility but I thought I would see if anyone has a better idea of how this may be fixed!
EDIT TO INCLUDE CatCard component...
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ScrollView,
TouchableOpacity,
FlatList,
Image,
Platform,
Animated,
Easing,
Dimensions
} from 'react-native';
import * as Consts from '../../Consts/colourConsts';
import * as ConstStyles from '../../Consts/styleConsts';
import PropTypes from 'prop-types';
import Interactable from 'react-native-interactable';
import { CachedImage } from 'react-native-img-cache';
class CatCard extends Component {
state = {
initX: this.props.posX,
initY: this.props.posY,
zIndex: 1
};
onSnap(res, point) {
if (res.nativeEvent.index === 0) {
return;
}
let index = res.nativeEvent.index;
setTimeout(() => {
this.props.onSnap(index, point);
let end = new Date();
}, 100);
Animated.timing(this.opacity, {
toValue: 0,
duration: 100,
useNativeDriver: true
}).start();
}
constructor(props) {
super(props);
this.opacity = new Animated.Value(1);
this.height = Dimensions.get('window').height;
}
gotRef(ref) {
this.props.gotRef(ref);
}
render() {
let upY = this.props.posY + this.height + (1 - this.props.odd) * -120;
upY = upY * -1;
upY += 50;
return (
<Interactable.View
ref={ref => {
this.gotRef(ref);
}}
onSnap={res => this.onSnap(res, this.props.ptr)}
style={[styles.card, { zIndex: this.state.zIndex }]}
animatedNativeDriver={true}
dragToss={0.01}
snapPoints={[
{
x: this.props.posX,
y: this.props.posY,
damping: 0.7,
tension: 300,
id: '0'
},
{
x: this.props.xOffset + 10,
y: upY,
tension: 30000,
damping: 0.1
},
{
x: this.props.xOffset + 10 + this.props.screenWidth * 0.33,
y: upY,
tension: 30000,
damping: 0.1
},
{
x: this.props.xOffset + 10 + this.props.screenWidth * 0.66,
y: upY,
tension: 30000,
damping: 0.1
}
]}
initialPosition={{ x: this.state.initX, y: this.state.initY }}
>
<Animated.View
style={[styles.cardContainer, { opacity: this.opacity }]}
>
<CachedImage
source={{ uri: this.props.item.imageUrl }}
style={styles.catImage}
/>
<Text style={styles.cardText}>{this.props.item.name}</Text>
</Animated.View>
</Interactable.View>
);
}
}
CatCard.PropTypes = {
count: PropTypes.any.isRequired,
type: PropTypes.string.isRequired,
imageToUse: PropTypes.any.isRequired,
height: PropTypes.object.isRequired,
backgroundColor: PropTypes.string.isRequired
};
const styles = StyleSheet.create({
card: {
position: 'absolute',
overflow: 'visible'
},
cardContainer: {
borderWidth: 1,
borderColor: 'rgb(200,200,200)',
borderRadius: 4,
alignItems: 'center',
width: 100,
backgroundColor: 'white'
},
cardText: {
fontFamily: 'Slackey',
fontSize: 10
},
catImage: {
height: 100,
width: 98,
borderTopRightRadius: 4,
borderTopLeftRadius: 4
}
});
export default CatCard;
Without seeing CatCard it is hard to know how the dragging is implemented. If you are doing raw PanResponder then you'll need to keep a something in state that keeps track of whether the ScrollView is scrolling and pass that down as a prop to CatCard which would then disallow the drag if the prop were true.
Alternatively, I'd suggest using react-native-interactable. It's a little to wrap your head around but it's a great abstraction from PanResponder. They have loads of examples and I have used it to make a swipeable list item that worked great, even with touchables inside the item.

i make a side menu,but PanResponder not work on phone

i make a side menu,it's ok on genymotion,but not work on my phone. it's response is delay more than 10s and most times not response on phone。please help me !
At the beginning ,I think it's flow reason:
1、position: 'absolute'
2、PanResponder wrapper a touchable
i had try clean this,but it's also not work。maybe its a bug ,are you ?
enter image description here
import React, {Component} from 'react'
import {
View,
Text,
StyleSheet,
ScrollView,
Alert,
PanResponder,
TouchableOpacity
} from 'react-native'
import {get_pointBaikeCate} from 'api'
import {Touchable} from 'basic'
export default class PointBaike extends Component {
static navigationOptions = {
title: '穴位百科',
header: null
}
constructor(props) {
super(props);
this.state = {
info: []
}
this.scrollY = []
}
async getInfo() {
let res = await get_pointBaikeCate();
console.log(res)
// let info = [];
// res.data.map((r)=> {
// r.content.map(c=> {
// let flag = info.find((item)=> {
// return item.jl == c.jl
// })
// if (flag) {
// flag.xw += `,${c.xw}`
// } else {
// info.push({
// jl: c.jl,
// xw: c.xw
// })
// }
// })
// });
//
// // 对info的xw进行过滤
// info = info.map((item)=> {
// let xw = item.xw.split(/[,|]/)
// item.xw = [...new Set(xw)].filter((x)=> {
// return x
// })
// return item
// });
this.setState({
info: res.data
})
}
goDetail(item) {
let {navigate} = this.props.navigation
console.log(item)
navigate('PointBaikeDetail', {
item
})
}
componentWillMount() {
this.getInfo()
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder.bind(this),
onStartShouldSetPanResponderCapture: this._handleStartShouldSetPanResponderCapture.bind(this),
onMoveShouldSetPanResponder: this._handlerMoveShouldSetPanResponder.bind(this),
onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture.bind(this),
onPanResponderTerminationRequest: this._handleMoveShouldSetPanResponderCapture,
onPanResponderMove: this._handlePanResponderMove.bind(this),
});
}
_handlerMoveShouldSetPanResponder(evt, gestureState){
if (gestureState.dx != 0 && gestureState.dy == 0) {
return true;
}
return false;
}
_handleMoveShouldSetPanResponderCapture(evt, gestureState) {
return gestureState.dx != 0 && gestureState.dy != 0
}
_handleStartShouldSetPanResponderCapture(evt, gestureState) {
return gestureState.dx != 0 && gestureState.dy != 0;
}
_handleMoveShouldSetPanResponderCapture(evt, gestureState) {
return gestureState.dx != 0 && gestureState.dy != 0;
}
_scrollTo(index) {
// Alert.alert('索引', `${index}`)
this._scrollView.scrollTo({y: this.scrollY[index]})
}
_handleStartShouldSetPanResponder() {
return true
}
_handlePanResponderMove(e, gestureState) {
// console.log('滑动', e.nativeEvent.pageY)
// 计算手指在那个元素上,得出index,然后根据index设置scrollTop
let y = e.nativeEvent.pageY - 100
let index = Math.ceil(y / 20) - 1
console.log(index, this)
this._scrollView.scrollTo({y: this.scrollY[index]})
}
_onLayout({nativeEvent}) {
this.scrollY.push(nativeEvent.layout.y)
}
render() {
let {info} = this.state
return (
<View style={styles.wrapper}>
<ScrollView style={{flex: 1, height: 300}}
showsVerticalScrollIndicator={false}
ref={(e)=> {
this._scrollView = e
}}
>
{
info.map((item, i)=> {
return (
<View key={i} style={styles.lists}
onLayout={this._onLayout.bind(this)}
>
<View>
<Text style={styles.title}>{item.title}</Text>
</View>
<View style={styles.listBox}>
{
item.list.map((x, xi)=> {
return (
<Touchable key={xi}
onPress={this.goDetail.bind(this, x)}
>
<View style={styles.list}>
<Text
style={styles.text}>{x.title}</Text>
</View>
</Touchable>
)
})
}
</View>
</View>
)
})
}
<View style={{height: 100}}></View>
</ScrollView>
<View style={styles.sideMenu}
{...this._panResponder.panHandlers}
>
{
info.map((item, i)=> {
return (
<TouchableOpacity key={i} onPress={this._scrollTo.bind(this, i)}>
<View>
<Text
style={styles.sideText}>{item.title.charAt(3) || item.title.charAt(0)}</Text>
</View>
</TouchableOpacity>
)
})
}
</View>
</View>
)
}
}
const styles = StyleSheet.create({
wrapper: {
paddingLeft: 6,
paddingRight: 6,
paddingTop: 10,
paddingBottom: 10,
flex: 1,
backgroundColor: '#fff'
},
sideText: {
width: 25,
height: 20,
lineHeight: 20,
textAlign: 'center'
},
sideMenu: {
position: 'absolute',
right: 0,
top: 100,
zIndex: 100,
backgroundColor: '#eee'
},
lists: {},
listBox: {
flexDirection: 'row',
flexWrap: 'wrap'
},
list: {
width: (WinWidth - 20) / 5,
},
title: {
fontSize: 18,
color: DEFAULT_COLOR,
paddingTop: 14,
paddingBottom: 10
},
text: {
flex: 1,
textAlign: 'center',
paddingTop: 10,
paddingBottom: 10,
}
})
you can try this
componentWillMount() {
this.getInfo()
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onStartShouldSetPanResponderCapture: this._handleStartShouldSetPanResponderCapture.bind(this),
onMoveShouldSetPanResponder: this._handlerMoveShouldSetPanResponder.bind(this),
onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture.bind(this),
onPanResponderTerminationRequest: this._handleMoveShouldSetPanResponderCapture,
onPanResponderMove: this._handlePanResponderMove.bind(this),
});
}