iOS集成Flutter-手势冲突问题解决

Question:

对于这样一个segementScrollView(Android的称法),图片/视频是作为一个flutterView被集成到原生中,当用户在屏幕进行拖拽或轻扫动作时(pan or swipe gesture),flutterView本身listView的上下滑动属性完全失效,此时操作的就像是在操作一个segementScrollView。可以左右滑动,但图片/视频listView本身不滑动,这明显不符合我们的期望。


segementView.png

Solution:

步骤1. 禁止segementScrollView上的滑动属性

当我禁止segementScrollView.scrollEnabbled = No后,左右不可滑动,上下滑动正常。因为上下滑动其实用的是flutterView的listView的滑动属性。看上去当禁止segementScrollView的滑动属性后,flutterView就可以接受到用户的触摸事件了。

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
    NSInteger index = scrollView.contentOffset.x/ScreenWidth;
    if (self.segmentControl.selectedSegmentIndex != index) {
        self.segmentControl.selectedSegmentIndex = index;
    }
    
    //户型信息和图片/视频都是flutterView,因此我们需要将其scrollEnabled置为NO
    if(self.segmentControl.selectedSegmentIndex == 1 || self.segmentControl.selectedSegmentIndex == 2){
        self.segmentScrollView.scrollEnabled = NO;
    }else{
        self.segmentScrollView.scrollEnabled = YES;
    }
}
drawingBoard.gif

步骤2:利用flutterView的pan手势得到左右偏移量

这时候我们发现虽然上下滑动flutterView的listView就正常了,但现在左右不可以滑动了。在网上找了很多资料,自己也做了很多尝试,发现我们可以这样做:
既然现在flutterView的手势是可以工作的,那我们可以利用flutterview的pan手势进行左右偏移的时候,实时记录其偏移位置,并将这偏移位置告诉iOS原生,这里就涉及到了flutterView与原生的交互部分了。

//flutter部分代码
Widget buildBuildingInfoContainer() {
      return GestureDetector(
      //手势滚动过程中
      onPanUpdate: (d) {
        print(
            "onPanUpdate----direction=${d.delta.direction},dx=${d.delta.dx},dy=${d.delta.dy},distance=${d.localPosition.distance}");
        movedX += d.delta.dx;
        movedY += d.delta.dy;
        //调用原生的方法,传递offsetX值以及状态ID
        MethodChannelUtil.flutterInvokeNative(
            MethodName.houseTypeCellScrolled, {
          "status": 6,
          "statusDesc": "移动",
          "offsetX": movedX - originalX,
          "offsetY": movedY - originalY
        });
      },

      //手势开始拖拽
      onPanStart: (d) {
        print(
            "onPanStart----dx=${d.localPosition.dx},dy=${d.localPosition.dy},distance=${d.localPosition.distance}");
         //调用原生的方法,传递offsetX值以及状态ID
        MethodChannelUtil.flutterInvokeNative(
            MethodName.houseTypeCellScrolled, {
          "status": 5,
          "statusDesc": "开始",
          "startX": originalX,
          "startY": originalY
        });
      },

      //手势拖动结束
      onPanEnd: (DragEndDetails details) {
        print("onPanEnd");
        double offsetX = movedX - originalX;
        double offsetY = movedY - originalY;
        print("offsetX=$offsetX offsetY=$offsetY");
         //调用原生的方法,传递offsetX值以及状态ID
        MethodChannelUtil.flutterInvokeNative(
            MethodName.houseTypeCellScrolled, {
          "status": 7,
          "statusDesc": "结束",
          "offsetX": offsetX,
          "offsetY": offsetY
        });
      },
      child: Container(

      ),
}

步骤3:原生接受到flutter模块的callBack,作出相应处理

原生模块可以接受到flutter模块的callBack,并得到偏移量的值,以及相应的status值。得到偏移量就很简单了,跟我们在iOS模块里设置scrollView偏移量没有两样。

    self.videoPhotoFlutterViewController.callBack = ^(NSString * _Nonnull jsonStr) {
        NSLog(@"OC----%@---",jsonStr);
        NSDictionary *dict =  jsonStr.mj_JSONObject;
        NSInteger value = [[dict objectForKey:@"status"] integerValue];
        CGFloat offsetX = [[dict objectForKey:@"offsetX"] doubleValue];
        CGFloat offsetY = [[dict objectForKey:@"offsetY"] doubleValue];

        //移动中
        if(value == 6){
            startX = weakself.segmentScrollView.contentOffset.x;
            CGFloat startY = weakself.segmentScrollView.contentOffset.y;
            startX += -offsetX;
            [weakself.segmentScrollView setContentOffset:CGPointMake(startX, 0)];
            NSLog(@"startX=%f",startX);
        }

        //停止移动
        if(value == 7){
            if(startX>ScreenWidth*1.5 && startX< ScreenWidth*2 + ScreenWidth*0.5){
                [weakself.segmentScrollView setContentOffset:CGPointMake(ScreenWidth*2,0) animated:true];
            }
            if (startX >= ScreenWidth*2 + ScreenWidth*0.5) {
                [weakself.segmentScrollView setContentOffset:CGPointMake(UIScreen.mainScreen.bounds.size.width*3,0) animated:true];
            }
            if (startX <= ScreenWidth*1.5){
                [weakself.segmentScrollView setContentOffset:CGPointMake(ScreenWidth,0) animated:true];
            }
        }
    };

最终效果与总结

drawingBoard2.gif

现在从效果上来,可以总结以下几点:

  1. 在iOS系统的segmentViewController中,上方是一个自定义的segmentTitleView,下方是一个左右滑动的scrollView。这个segmentItem每一个item都是独立的ViewController,其中当然也包括我们的flutterViewController。
  2. 一开始的时候,左右滑动是没有问题的,但就是flutterView里的listView不能正常滑动,因为我们现在所有的手势都在iOS层处理了,并不会传递给flutterView,flutterView的手势不起作用。因此我们需要设置segmentScrollView.scrollEnabled = NO去disable掉原生层面的手势,这样flutterView层面上的手势自动生效。
  3. segmentScrollView的滑动属性被disable掉之后,左右滑动不能work了。现在我们需要在flutterView的pan手势去计算得出其手势偏移量,将此偏移量传递给原生,然后由原生去设置segmentScrollView的contentOffset做偏移。
    这就是设计的基本思路。有更好的方案,欢迎提出讨论👏👏
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容