satya164 / react-native-tab-view

A cross-platform Tab View component for React Native
MIT License
5.13k stars 1.07k forks source link

TabViewAnimated disable swipe not working #272

Closed jesouhaite08 closed 7 years ago

jesouhaite08 commented 7 years ago

I followed the directions in the example to disable swipe, but it is not working. Is this example still valid? Below is my code:

<TabViewAnimated
  animationEnabled={false}
  swipeEnabled={false}
  style={[ styles.container, this.props.style ]}
  navigationState={this.state}
  renderScene={this._renderScene.bind(this)}
  renderFooter={this._renderFooter.bind(this)}\
  onRequestChangeTab={this._handleChangeTab.bind(this)}  />

_renderFooter(props) {
  if (!this.state.keyboardOpen) {
    return (
      <TabBar 
        {...props} 
        scrollEnabled={false}
        renderIcon={this._renderIcon.bind(this)}
        renderIndicator={this._renderIndicator.bind(this)}
        style={styles.tabbar}
        tabStyle={styles.tab}
        labelStyle={styles.tabBarLabelStyle}
        renderLabel={this._renderLabel(props).bind(this)}
      />
    )
  } else {
    return
 }
}
satya164 commented 7 years ago

It certainly does in the example. Do you have the latest version? Can you post the full component?

Also you shouldn't bind functions in render. It creates new functions every render and will negate the pure render optimization. You can bind in the constructor or use class properties like in the example.

jesouhaite08 commented 7 years ago

@satya164 Interesting, good to know. I'm on version 0.0.56. Below is my full component:

import React, { Component } from 'react';
import {
  Animated,
  Keyboard,
  KeyboardAvoidingView,
  StyleSheet,
  Text,
  TextInput,
  View,
} from 'react-native';
import { 
  Button,
  Header,
  Title,   
} from 'native-base';
import { connect } from 'react-redux';
import Icon from 'react-native-vector-icons/Ionicons';
import theme from '../../themes/base-theme';
import globalStyles from '../styles/global';
import Details from './Details';
import Inspection from './Inspection';
import Insects from './Insects';
import Radon from './Radon';
import { TabViewAnimated, TabBar, TabViewPagerAndroid } from 'react-native-tab-view';
import { Ionicons } from '@expo/vector-icons';
import Drawer from 'react-native-drawer'
import SideBar from '../drawer/SideBar'

class Index extends Component {
  constructor(props) {
    super(props)
    this.state = {
      index: this._getInitialIndex(),
      routes: [
        { key: '1', title: 'Details', icon: 'ios-list-box-outline'},
        { key: '2', title: 'Inspection', icon: 'ios-home-outline' },
        { key: '3', title: 'Insects', icon: 'ios-bug' },
        { key: '4', title: 'Radon', icon: 'ios-nuclear-outline' },
      ],    
      drawerIsOpen: false,
    }
    this._keyboardDidShow = this._keyboardDidShow.bind(this)
    this._keyboardDidHide = this._keyboardDidHide.bind(this)
    this._getIndexName = this._getIndexName.bind(this)
    this.send = this.send.bind(this)
  }

  componentWillUnmount() {
    this.keyboardDidShowListener.remove()
    this.keyboardDidHideListener.remove()
  }

  _keyboardDidShow() {
    this.setState({ keyboardOpen: true })
  }

  _keyboardDidHide() {
    this.setState({ keyboardOpen: false })
  }

  async componentWillMount() {
    this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow)
    this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide)
  }

  _getInitialIndex() {
    switch(this.props.tab) {
    case 'details':
      return 0
    case 'inspection':
      return 1
    case 'insects':
      return 2
    case 'radon':
      return 3
    default:
      return null;
    }
  }

  _getIndexName() {
    switch(this.state.index) {    
    case 0:
      return 'details'
    case 1:
      return 'inspection'
    case 2:
      return 'insects'
    case 3:
      return 'radon'
    default:
      return null;
    }
  }

  _handleChangeTab(index) {
    this.setState({ index })
  }

  _renderIcon({ route }: any) {
    return (
      <Ionicons
        name={route.icon}
        size={22}
        style={styles.icon}
      />
    );
  }

  _renderLabel = (props: any) => ({ route, index }) => {
    return (
      <Animated.Text style={styles.label}>
        {route.title}
      </Animated.Text>
    )
  }

  _renderFooter(props) {
    if (!this.state.keyboardOpen) {
      return (
        <TabBar 
          {...props} 
          scrollEnabled={false}
          renderIcon={this._renderIcon.bind(this)}
          renderIndicator={this._renderIndicator.bind(this)}
          style={styles.tabbar}
          tabStyle={styles.tab}
          labelStyle={styles.tabBarLabelStyle}
          renderLabel={this._renderLabel(props).bind(this)}
        />
      )
    } else {
      return
    }
  }

  _renderScene ({ route }) {
    switch (route.key) {
    case '1':
      return <Details details={this.props.report.report.details} report={this.props.report} tab={'details'} />
    case '2':
      return <Inspection navigator={this.props.navigator} report={this.props.report} tab={'inspection'} />
    case '3':
      return <Insects insects={this.props.report.report.insects} report={this.props.report} tab={'insects'} />
    case '4':
      return <Radon radon={this.props.report.report.radon} report={this.props.report} tab={'radon'} />
    default:
      return null
    }
  }

  _renderIndicator(props) {
    const { width, position } = props
    const translateX = Animated.multiply(position, width)

    return (
      <Animated.View
        style={[ styles.container, { width, transform: [ { translateX } ] } ]}
      >
        <View style={styles.indicator} />
      </Animated.View>
    );
  }

  _configureTransition() {
    null
  }

  getReportId() {
    switch(this.state.index) { 
    case 0:
      return this.props.report.report.details.id
    case 1:
      return this.props.report.report.details.id
    case 2:
      return this.props.report.report.insects.id
    case 3:
      return this.props.report.report.radon.id
    default:
      return null;
    }
  }

  getIncompleteTextList(incompleteItems, isInspection = false) {
    let incompleteText = !isInspection ? '\n \n You did not enter the: \n ' : '\n \n'

    if (incompleteItems.length > 0) {
      incompleteText += incompleteItems.join('\n ')
    }

    return incompleteText
  }

  getIncompleteInsectsText() {
    let incompleteItems = [],
      incompleteText = ''

    if (this.props.report.report.insects.structure.length === 0) {
      incompleteItems.push('Type of house')
    }

    if (this.props.report.report.insects.structure.length === 0) {
      incompleteItems.push('Inspection findings')
    }

    if (incompleteItems.length > 0) {
      incompleteText = this.getIncompleteTextList(incompleteItems)
    }

    return incompleteText
  }

  getIncompleteRadonText() {
    let incompleteItems = []
    let incompleteText = ''

    if (this.props.report.report.radon.location.length === 0) {
      incompleteItems.push('Location')
    }

    if (this.props.report.report.radon.start_date.length === 0) {
      incompleteItems.push('Start date')
    }

    if (this.props.report.report.radon.start_time.length === 0) {
      incompleteItems.push('Start time')
    }

    if (this.props.report.report.radon.end_date.length === 0) {
      incompleteItems.push('End date')
    }

    if (this.props.report.report.radon.end_time.length === 0) {
      incompleteItems.push('End time')
    }

    if (this.props.report.report.radon.level.length === 0) {
      incompleteItems.push('Results')
    }

    if (incompleteItems.length > 0) {
      incompleteText = this.getIncompleteTextList(incompleteItems)
    }

    return incompleteText
  }

  getIncompleteCounts(incompleteItems) {
    let counts = {}

    for (let item of incompleteItems) {
      counts[item] = (counts[item] || 0) + 1 
    }

    return counts
  }

  getIncompleteItemsList(incompleteItems) {
    let counts = this.getIncompleteCounts(incompleteItems)
    let items = []

    for (let key in counts) {
      if (counts.hasOwnProperty(key)) {
        items.push(key + ' has ' + counts[key] + ' still required')
      }
    }

    return items
  }

  getIncompleteInspectionText() {
    let incompleteItems = [],
      incompleteText = '',
      incompleteItemList = '',
      areItemsAdded = false

    this.props.report.report.inspection.filter(function(area) {
      area.items.filter(function(item) {
        areItemsAdded = false

        if (item.savedDescriptions !== undefined) {
          for (let desc of item.savedDescriptions) {
            if (desc.active) {
              areItemsAdded = true
              break
            }
          }
        }

        if (item.description.length > 0) {
          areItemsAdded = true
        }

        if (item.title === 'Exterior Photo of House') {
          if ('images' in item && item.images.length > 0) {
            areItemsAdded = true
          }
        }

        if (!item.defect && !areItemsAdded) {
          incompleteItems.push(area.area)
        }
      })
    })

    incompleteItemList = this.getIncompleteItemsList(incompleteItems)

    if (incompleteItemList.length > 0) {
      incompleteText = this.getIncompleteTextList(incompleteItemList, true)
    }

    return incompleteText
  }

  getIncompleteText() {
    switch(this.state.index) { 
    case 0:
      return ''
    case 1:
      return this.getIncompleteInspectionText()
    case 2:
      return this.getIncompleteInsectsText()
    case 3:
      return this.getIncompleteRadonText()
    default:
      return null
    }
  }

  send() {
    this.props.navigator.push({
      id: 'confirm',
      navigator: this.props.navigator,
      incompleteText: this.getIncompleteText(),
      reportId: this.getReportId(),
      report: this.props.report,
      tab: this._getIndexName(),
    })
  }

  showSendIcon() {
    let completedDate = '',
      isAdded = false

    if (this._getIndexName() === 'insects') {
      completedDate = this.props.report.report.insects.completed_date
      isAdded = this.props.report.report.details.insects
    } else if (this._getIndexName() === 'radon') {
      completedDate = this.props.report.report.radon.completed_date
      isAdded = this.props.report.report.details.radon
    } if (this._getIndexName() === 'inspection') {
      completedDate = this.props.report.report.details.inspection_completed_date
      isAdded = this.props.report.report.details.inspection
    }

    return this.state.index != 0 && this.props.report.id != undefined && isAdded && !completedDate 
  }

  _toggleDrawer() {
    let currentState = this.state.drawerIsOpen

    if (currentState) {
      this._drawer.close()
    } else {
      this._drawer.open()
    }

    this.setState({drawerIsOpen: !currentState})
  }

  closeDrawer() {
    this._drawer.close()
    this.setState({drawerIsOpen: false})
  }

  render() {
    return (
      <View style={styles.container}> 
        <Header style={globalStyles.header}>
          <Button transparent onPress={() => this._toggleDrawer()}>
            {this.state.drawerIsOpen ? (
              <Icon name='md-close' style={globalStyles.headerToolbarIcon} />
            ) : (
              <Icon name='md-menu' style={globalStyles.headerToolbarIcon} />
            )}
          </Button>
          <Title></Title>
          {this.showSendIcon() &&
          <Button transparent onPress={() => this.send()}>
            <Icon name='ios-paper-plane' style={globalStyles.headerToolbarIcon} />
          </Button>
          }
        </Header>
        <Drawer
          ref={(ref) => this._drawer = ref}     
          panOpenMask={0.80}
          openDrawerOffset={180}
          tapToClose={true}
          onClose={() => this.setState({drawerIsOpen: false})}
          captureGestures='open'
          content={<SideBar navigator={this.props.navigator} report={this.props.report} closeDrawer={() => this.closeDrawer()} />}
          >
            <KeyboardAvoidingView behavior='padding' style={styles.keyboardAvoidingView}>
              <TabViewAnimated
                animationEnabled={false}
                swipeEnabled={false}
                style={[ styles.container, this.props.style ]}
                navigationState={this.state}
                renderScene={this._renderScene.bind(this)}
                renderFooter={this._renderFooter.bind(this)}
                onRequestChangeTab={this._handleChangeTab.bind(this)}
              />
            </KeyboardAvoidingView>
          </Drawer>
      </View>
    )
  }
}

const mapStateToProps = (state, ownProps) => {
  for (let report of state.reports) {
    if (ownProps.report.report.id === report.report.id) {
      return { report }
    }
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  page: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  tabbar: {
    backgroundColor: '#455399',
  },
  tab: {
    padding: 0,
    backgroundColor: '#455399',
  },
  indicator: {
    flex: 1,
    margin: 0,
    borderRadius: 0,
  },
  tabBarLabelStyle : {
    color: '#fff',
  },
  label: {
    paddingTop: 3,
    paddingBottom: 8,
    fontFamily: 'Avenir-Roman',
    fontSize: 12,
    color: '#fff',
  },
  icon: {
    color: '#fff',
  },
  keyboardAvoidingView: {
    flex: 1,
    justifyContent: 'center',
  },
})

export default Index = connect(
  mapStateToProps,
)(Index)
satya164 commented 7 years ago

@jesouhaite08 you can disable swiping that way only in 0.60 and later. In 0.56, you needed this https://github.com/react-native-community/react-native-tab-view/blob/v0.0.56/example/src/NoAnimationExample.js#L157

(you don't need all three there, just pan is fine if you have both swipe and animations disabled)

jesouhaite08 commented 7 years ago

You're awesome, thank you @satya164 !