root-two / react-native-drawer

React Native Drawer
MIT License
2.54k stars 390 forks source link

Possible to terminate pans on main view when panning drawer? #17

Closed lukekarrys closed 9 years ago

lukekarrys commented 9 years ago

First off, thanks for this component, it has worked great and the API has been very easy to use. :smile:

I have an app that is a full screen MapView with a Settings view as the drawer. What I'm seeing is that (with what I've tried so far) I can't start panning the drawer without the map also panning for the duration of the drawer panning.

I'm not sure if this should (or can) be solved within react-native-drawer, but if you have any ideas about what could work, I would greatly appreciate it.

Here's the code I'm using currently for my registered component:

const styles = StyleSheet.create({
  shadow: {
    shadowColor: '#000000',
    shadowOpacity: 0.6,
    shadowRadius: 15,
    flex: 1
  },
  map: {
    flex: 1
  }
});

export default React.createClass({
  render () {
    return (
      <Drawer type='static' content={<Settings {...this.state} />}>
        <View style={styles.shadow}>
          <MapView style={style.map} {...this.state} />
        </View>
      </Drawer>
    );
  }
});

Things I've tried:

rt2zz commented 9 years ago

I believe, but would need to verify, what is happening is the map view is a child of the drawer, and hence has first dibs at responding to the pan. The event propogates up to the drawer, which then also pans. I am not sure how to solve this elegantly.

I suspect you could put an invisible View as a child as a sibling after the MapView, and set up a pan responder. Since this view will have a "higher" z index, it will get first dibs on the pan responder, and furthermore I suspect it will propogate up to the Drawer without ever triggering the pan on the MapView.

I have not played with the way pan and event propogation works much, so this would definitely take some tinkering to get right. If the above method does work, we could potentially add a new prop preventPanPropogation

Not sure when I will have time to look into this, but please let me know if you get it working!

lukekarrys commented 9 years ago

Thanks for the tips @rt2zz. I'll keep investigating, and if I find an elegant solution that would work within react-native-drawer I'll submit a PR :smile:

rt2zz commented 9 years ago

Code for what I had in mind (again not sure if this will work, but I think the approach would look something like this):

const styles = StyleSheet.create({
  shadow: {
    shadowColor: '#000000',
    shadowOpacity: 0.6,
    shadowRadius: 15,
    flex: 1
  },
  map: {
    flex: 1
  },
  panCapture: {
    width: 20,
    height: fullHeight,
    position: 'absolute',
    left:0,
    top:0,
  }
});

export default React.createClass({

  componentWillMount: function() {
    this._panGesture = PanResponder.create({
      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
      onMoveShouldSetPanResponder: (evt, gestureState) => true,
      onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
      onPanResponderGrant: (evt, gestureState) => {},
      onPanResponderMove: (evt, gestureState) => {},
      onPanResponderTerminationRequest: (evt, gestureState) => true,
      onPanResponderRelease: (evt, gestureState) => {},
      onPanResponderTerminate: (evt, gestureState) => {},
      onShouldBlockNativeResponder: (evt, gestureState) => {},
    });
  },

  render () {
    return (
      <Drawer type='static' content={<Settings {...this.state} />}>
        <View style={styles.shadow}>
          <MapView style={style.map} {...this.state} />
          <View style={style.panCapture} {...this._panResponder.panHandlers} />
        </View>
      </Drawer>
    );
  }
});
rt2zz commented 9 years ago

@lukekarrys I just learned about onStartShouldSetPanResponderCapture which may be the solution here. I just pushed 1.2.6 with a new captureGestures prop. Set that to true and you may be able to get something workable for your use case.

lukekarrys commented 9 years ago

Combining the invisible panCapture view and captureGestures={true} did the trick! Thanks @rt2zz!

lukekarrys commented 9 years ago

In case anyone else wants to see my full implementation:

import React, {StyleSheet, View, Component, PropTypes, NativeModules} from 'react-native';
import Drawer from 'react-native-drawer';

const screen = NativeModules.UIManager.Dimensions.window;
const styles = StyleSheet.create({
  drawer: {
    shadowColor: '#000000',
    shadowOpacity: 0.6,
    shadowRadius: 15,
    flex: 1
  },
  panCapture: {
    height: screen.height,
    backgroundColor: 'transparent',
    position: 'absolute',
    left: 0,
    top: 0
  }
});

export default class CaptureDrawer extends Component {
  static propTypes = {
    children: PropTypes.node.isRequired,
    panOpenMask: PropTypes.number.isRequired
  }

  render () {
    const {children, ...drawerProps} = this.props;
    const {panOpenMask} = drawerProps;
    const panOpenWidth = screen.width * panOpenMask;
    return (
      <Drawer {...drawerProps} captureGestures={true}>
        <View style={styles.drawer}>
          {children}
          <View style={[styles.panCapture, {width: panOpenWidth}]} />
        </View>
      </Drawer>
    );
  }
}

then from my main view I can render like this:

<CaptureDrawer type='static' panOpenMask={0.1} content={<Settings />}>
  <ContentView />
</CaptureDrawer>
rt2zz commented 9 years ago

nice! I was thinking about making some implementation specific modules like material-drawer or what have you. This is awesome!

BTW are you sure you still need the panCapture view? I think captureGestures might be sufficient.

lukekarrys commented 9 years ago

@rt2zz You're right! And if you see in the example, the capturePanHandlers are never even being passed to the View. They were left over from my previous implementation and I didn't realize I wasn't even using them anymore. :grinning: I'l gonna edit the comment and remove the componentWillMount method.

rt2zz commented 9 years ago

good stuff, if only we had known how onStartShouldSetPanResponderCapture worked from the get go! :)