2017-06-07 18 views
6

私はReact-Nativeでスワイプしてタブナビゲータを独自に実装しています。それはうまく動作しますが、私は自分のタブの中にScrollViewを持っていると、それは壊れそうです。左右にスワイプしてタブを変更すると、うまく機能し、スクロールビューで上下にスクロールできます。スクロールビューをドラッグしてクリックしてスワイプすることなく横に移動すると、ブレークします。その後、タブシステムは最初のタブにリセットされます。PanResponder内のScrollView

スクロール表示がスクロールされたときに、タブの内側からスワイプを無効にするハックを作成しました。これはうまくいきますが、タブの内容がタブ内にあることを認識しなければならないため、悪い解決策のように感じます。あなたのコードブロック(scrollView、パンジェスチャー)を使用しているあなたのビューコントローラで

import React, { Component, PropTypes } from 'react'; 
import { connect } from 'react-redux'; 
import { View, Animated, Dimensions, PanResponder } from 'react-native'; 
import Immutable from 'immutable'; 
import Tab1 from './Tab1'; 
import Tab2 from './Tab2'; 
import ScrollViewTab from './ScrollViewTab'; 


@connect(
    state => ({ 
     tabs: state.tabs 
    }) 
) 

export default class Tabs extends Component { 
    static propTypes = { 
     tabs: PropTypes.instanceOf(Immutable.Map).isRequired, 
     dispatch: PropTypes.func.isRequired 
    }; 
    constructor(props) { 
     super(props); 
     this.justLoaded = true; 
     this.state = { 
      left: new Animated.Value(0), 
      tabs: [{ // Tabs must be in order, despite index. 
       name: 'tab1', 
       component: <Tab1 setTab={this.setTab} />, 
       index: 0 
      }, { 
       name: 'tab2', 
       component: <Tab2 setTab={this.setTab} />, 
       index: 1 
      }, { 
       name: 'scrollViewTab', 
       component: <ScrollViewTab setTab={this.setTab} />, 
       index: 2 
      }] 
     }; 
     this.getIndex = this.getIndex.bind(this); 
    } 
    componentWillMount() { 
     this.panResponder = PanResponder.create({ 
      onMoveShouldSetResponderCapture:() => true, 
      onMoveShouldSetPanResponderCapture: (evt, gestureState) => { 
       if (Math.abs(gestureState.dx) > 10) { 
        return true; 
       } 
       return false; 
      }, 
      onPanResponderGrant:() => { 
       this.state.left.setOffset(this.state.left._value); 
       this.state.left.setValue(0); 
      }, 
      onPanResponderMove: (e, gestureState) => { 
       if (this.isSwipingOverLeftBorder(gestureState) || 
        this.isSwipingOverRightBorder(gestureState)) return; 
       Animated.event([null, { 
        dx: this.state.left 
       }])(e, gestureState); 
      }, 
      onPanResponderRelease: (e, gestureState) => { 
       this.state.left.flattenOffset(); 

       if (this.isSwipingOverLeftBorder(gestureState) || 
        this.isSwipingOverRightBorder(gestureState)) { 
        return; 
       } 

       Animated.timing(
        this.state.left, 
        { toValue: this.calcX(gestureState) } 
       ).start(); 
      } 
     }); 
    } 
    componentDidMount() { 
     this.justLoaded = false; 
    } 
    getStyle() { 
     const oldLeft = this.state.left; 
     let left = 0; 
     const screenWidth = Dimensions.get('window').width; 
     // Set tab carouselle coordinate to match the selected tab. 
     this.state.tabs.forEach((tab) => { 
      if (tab.name === this.props.tabs.get('tab')) { 
       left = -tab.index * screenWidth; 
      } 
     }); 


     if (this.justLoaded) { 
      Animated.timing(
       this.state.left, 
       { toValue: left, 
        duration: 0 
       } 
      ).start(); 

      return { transform: [{ translateX: oldLeft }], flexDirection: 'row', height: '100%' }; 
     } 
     Animated.timing(
       this.state.left, 
      { toValue: left 
      } 
      ).start(); 
     return { transform: [{ translateX: oldLeft }], flexDirection: 'row', height: '100%' }; 
    } 
    getIndex(tabN) { 
     let index = 0; 

     this.state.tabs.forEach((tab) => { 
      if (tab.name === tabN) { 
       index = tab.index; 
      } 
      return tab; 
     }); 

     return index; 
    } 
    setTab(tab, props) { 
     this.navProps = props; 
     this.props.dispatch({ type: 'SET_TAB', tab }); 
    } 
    isSwipingOverLeftBorder(gestureState) { 
     return (this.props.tabs.get('tab') === this.state.tabs[0].name && 
       gestureState.dx > 0); 
    } 
    isSwipingOverRightBorder(gestureState) { 
     return (this.props.tabs.get('tab') === this.state.tabs[this.state.tabs.length - 1].name && 
       gestureState.dx < 0); 
    } 
    calcX(gestureState) { 
     const screenWidth = Dimensions.get('window').width; 
     const activeTab = this.getIndex(this.props.tabs.get('tab')); 
     let coord = 0; 

     if (gestureState.dx > screenWidth * 0.2) { 
      coord = (activeTab * screenWidth) - screenWidth; 
     } else if (gestureState.dx < -(screenWidth * 0.2)) { 
      coord = (activeTab * screenWidth) + screenWidth; 
     } else { 
      coord = activeTab * screenWidth; 
     } 

     this.updateTab(-coord, screenWidth); 

     return -coord; 
    } 
    updateTab(coord, screenWidth) { 
     // Update current tab according to location and screenwidth 
     this.state.tabs.forEach((tab) => { 
      if (coord === -tab.index * screenWidth) { 
       this.props.dispatch({ type: 'SET_TAB', tab: tab.name }); 
      } 
     }); 
    } 
    render() { 
     return (
      <View 
      style={{ flex: 1 }} 
      > 
      <Animated.View 
       style={this.getStyle()} 
       {...this.panResponder.panHandlers} 
      > 
       {this.state.tabs.map(tab => tab.component)} 
      </Animated.View> 
      </View> 
     ); 
    } 
} 
+0

あなたはshouldRecognizeSimultaneouslyWith関数を使用しましたか? –

+0

ねえ、私はそれを知らなかった。確かめます。どのように私のソリューションでそれを使用する例ですか? – Waltari

答えて

4

してみてください。 gestureState.dxがgestureState.dyよりもはるかに大きい場合は、onMoveShouldSetResponderの関数を使用して水平スワイプします。

onMoveShouldSetResponder: (evt, gestureState) => { 
    return Math.abs(gestureState.dx) > Math.abs(gestureState.dy * 3); 
}, 

ます。またonPanResponderMoveで機能を持っている可能性がスワイプジェスチャーの方向を追跡し、あなたが問題を持っていないので、その後onPanResponderReleaseにリセットしたときにそのような水平方向のスワイプに垂直スワイプの変更:

checkSwipeDirection(gestureState) { 
    if( 
     (Math.abs(gestureState.dx) > Math.abs(gestureState.dy * 3)) && 
     (Math.abs(gestureState.vx) > Math.abs(gestureState.vy * 3)) 
    ) { 
     this._swipeDirection = "horizontal"; 
    } else { 
     this._swipeDirection = "vertical"; 
    } 
} 
canMove() { 
    if(this._swipeDirection === "horizontal") return true; 
    else return false; 
} 

はその後、そうのようにそれを使用します。

onMoveShouldSetPanResponder: this.canMove, 
onPanResponderMove: (evt, gestureState) => { 
    if(!this._swipeDirection) this.checkSwipeDirection(gestureState); 

// Your other code here 
}, 
onPanResponderRelease: (evt, gestureState) => { 
    this._swipeDirection = null; 
} 

私はミディアムHow I built React Native Tab View .ITのサタジットSahooによって、オンラインで素晴らしい記事を見つけた独自のタブ表示を実装する方法の深い多くを示しています。私は本当に有益なので、ブログ記事を見てみることをお勧めします。

更新:子コンポーネントがジェスチャレスポンダになることを親コンポーネントが防止するようにする場合はGesture Responder Lifecycleのドキュメントをチェックしてください。逆の場合も同様です。

0

書き込みこの機能:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { 
    return true 
} 

はそれが役に立てば幸い:)

+0

申し訳ありませんが、React-NativeでJavaScriptを使用しています。あなたはこのコード行をどこに置いていますか? – Waltari