完美解决iOS上与flutter页面手势冲突问题

场景

横滑tab上嵌套flutter页面,iOS 的UIScrollview内放Flutter页面,scrollview可左右滑动,scrollview的手势优先级最高,滑动flutter页面的时候会触发iOS原生手势,flutter页面很难滑动

手势原理

iOS的所有手势优先级高于touch事件,滑动手势的优先级高于touch事件

flutter的滑动手势是由 iOS的touch事件传递到flutterview上,flutter对touch事件进行处理得到的flutter的手势动作

滑动冲突原因

UIScrollview 的滑动手势在此场景下优先级最高,这个场景的scrollview的滑动手势是在左右滑动下触发,正常上下滑动不会触发UIScrollview的手势,这个时候flutterview是可以滑动的,但是手指稍微有一点左右偏移,就会触发原生手势,触发原生手势之后,touch事件就不会触发,flutterview获得不了touch事件就无法识别手势,就无法滑动了

解决

iOS原生手势的优先级最高,flutter的手势是touch事件,所以原生手势比flutter天生高一个级别,要解决这个问题必须把flutter和原生提到一个级别,进行手势冲突竞争处理,我们在原生中在flutterview上加一个原生手势,这个手势是flutter在原生中竞争的代理手势,用这个代理手势和原生手势竞争,这个代理手势的优先级最高,用户触摸屏幕时先让这个手势响应,把touch事件传递到flutter响应手势,如果flutter页面可以响应手势,那这个原生的代理手势就竞争成功了,如果flutter不能响应手势就告诉原生代理手势响应失败了,让iOS的Scrollview的手势响应,

此方案的flutter端代码用在安卓上相同的原理也可以解决冲突问题,安卓上的不需要代理手势直接修改滑动手势是否响应就可以,

判断flutter是否有手势可以响应,可以利用flutter的手势竞技场,在flutter的widget最上层加一个PanGestureRecognizer手势优先级是最低的,如果有其他手势响应会触发rejectGesture,如果没有其他手势触发会响应这个手势触发acceptGesture,

实现

设置代理手势的优先级高于UIScrollview高于的滑动手势

[superScrollView.panGestureRecognizer requireGestureRecognizerToFail:self.flutterGesture];

将flutter代理手势的touch事件传递到flutter

iOS的手势有个属性cancelsTouchesInView 默认是YES,响应手势的时候不会触发touch事件,给flutter的代理手势设置为YES就可以在代理手势响应的时候把touch事件传递到flutterView

flutterGesture.cancelsTouchesInView = NO;

判断flutter是否可以响应手势

class _PointerTracker extends PanGestureRecognizer {
  MethodChannel channel = const MethodChannel('com.qqreader.flutter/scroll_channel');
  bool _flutterGestureIsWorking = false;
  @override
  void rejectGesture(int pointer) {
    super.rejectGesture(pointer);
    _flutterGestureIsWorking = true;
    _notify();
  }

  @override
  void acceptGesture(int pointer) {
    super.acceptGesture(pointer);
    _flutterGestureIsWorking = false;
    _notify();
  }

  void _notify() {
    try{
      if (_flutterGestureIsWorking) {
        channel.invokeMethod("isWork", "");
      } else {
        channel.invokeMethod("noWork", "");
      }
    } catch(e){
      // ignored, really.
    }
  }
}

以上代码基本可以解决滑动冲突的问题了,但是会发现flutter页面滑动的时候反应会很慢,那是因为UIScrollview的delaysContentTouches 属性导致的,这个属性默认是YES,touch事件会延迟传递给内部的view flutter收到手势的时机会被延迟,会出现滑得很快但是页面走的很慢,delaysContentTouches设置为NO就可以解决了

superScrollView.delaysContentTouches = NO;

源码
iOS端 
@interface QRNativeScrollFixViewController ()

@property (nonatomic, strong) UIPanGestureRecognizer *flutterGesture;
@property (nonatomic, strong) FlutterMethodChannel *scrollChannel;
@end

@implementation QRNativeScrollFixViewController

- (void)viewDidLoad{
    [super viewDidLoad];
   
    UIPanGestureRecognizer *flutterGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(flutterGestureAction)];
    flutterGesture.delegate = self;
    
    flutterGesture.cancelsTouchesInView = NO;
    [self.view addGestureRecognizer:flutterGesture];
    self.flutterGesture = flutterGesture;
    
    self.scrollChannel = [FlutterMethodChannel
                          methodChannelWithName:@"com.qqreader.flutter/scroll_channel"
                          binaryMessenger:self.engine.binaryMessenger];
    [self handleMethod];
}

- (void)flutterGestureAction{
    //do nothing
}

- (void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];
    UIScrollView *superScrollView = [self getScrollView];
    superScrollView.delaysContentTouches = NO;
    if(superScrollView){
        [superScrollView.panGestureRecognizer requireGestureRecognizerToFail:self.flutterGesture];
    }
}

- (UIScrollView *)getScrollView{
    UIView *superView = self.view.superview;
    while (superView) {
        if([superView isKindOfClass:UIScrollView.class]){
            return (UIScrollView *)superView;
        } else {
            superView = superView.superview;
        }
    }
    return nil;
}

- (void)flutterHandleTouch:(BOOL)isWorking{
    if(isWorking){
        self.flutterGesture.state = UIGestureRecognizerStateBegan;
    } else {
        self.flutterGesture.state = UIGestureRecognizerStateFailed;
    }
}

- (void)handleMethod {
    @weakify(self);
    [self.scrollChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
        @strongify(self);

        if ([@"isWork" isEqualToString:call.method]) {
            [self flutterHandleTouch:YES];
        }
        if ([@"noWork" isEqualToString:call.method]) {
            [self flutterHandleTouch:NO];
        }
        result(nil);
    }];
}
@end
 
   
flutter源码

使用时直接用QRNativeScrollFixWidget 嵌套在最外层就可以

class QRNativeScrollFixWidget extends StatelessWidget {
  const QRNativeScrollFixWidget({super.key, required this.child});
  final Widget child;
  @override
  Widget build(BuildContext context) {
    return RawGestureDetector(
      behavior: HitTestBehavior.translucent,
      gestures: {
        _PointerTracker: GestureRecognizerFactoryWithHandlers<_PointerTracker>(
          () => _PointerTracker(),
          (_PointerTracker instance) {
            instance
              ..onStart = (DragStartDetails details) {
                print('开始');
              }
              ..onEnd = (detail) {
                print('结束');
              };
          },
        )
      },
      child: child,
    );
  }
}

class _PointerTracker extends PanGestureRecognizer {
  MethodChannel channel = const MethodChannel('com.qqreader.flutter/scroll_channel');
  bool _flutterGestureIsWorking = false;
  @override
  void rejectGesture(int pointer) {
    super.rejectGesture(pointer);
    _flutterGestureIsWorking = true;
    _notify();
  }

  @override
  void acceptGesture(int pointer) {
    super.acceptGesture(pointer);
    _flutterGestureIsWorking = false;
    _notify();
  }

  void _notify() {
    try{
      if (_flutterGestureIsWorking) {
        channel.invokeMethod("isWork", "");
      } else {
        channel.invokeMethod("noWork", "");
      }
    } catch(e){
      // ignored, really.
    }
  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
iOS上调试Flutter页面崩溃的主要目标是定位并解决潜在的错误或异常,以下是一些常见的调试技巧和方法: 1. Flutter DevTools:Flutter DevTools是一个用于调试和分析Flutter应用程序的强大工具。它提供了一系列的性能分析、错误日志和UI层级检查等功能,帮助我们更轻松地定位和解决问题。 2. 日志输出:通过在代码中添加日志输出,可以追踪应用程序的执行过程。可以使用print语句或调试工具中的日志输出功能来记录Flutter页面运行时的关键信息。这样可以帮助我们找到崩溃的位置,并确定可能的原因。 3. 断点调试:在Xcode中使用断点调试功能可以帮助我们逐步执行代码,查看每一步的变化情况。通过在可能引发崩溃的位置设置断点,可以在崩溃发生时捕获并定位问题。 4. 异常捕获:在适当的地方使用try-catch块来捕获Flutter页面中可能引发的异常。通过捕获异常可以避免整个页面崩溃,而是执行后续的异常处理逻辑。 5. 错误处理:在代码中实现恰当的错误处理机制,例如校验输入、空值检查等,可以减少应用程序崩溃的可能性。 6. 内存管理:管理好Flutter页面的内存使用是避免崩溃的关键。对于资源密集的页面,可以优化内存管理策略,及时释放不再需要的对象,避免内存溢出导致的崩溃。 总之,调试Flutter页面崩溃的关键在于全面了解Flutter的调试工具,通过日志输出、断点调试、异常捕获等多种手段来定位和解决问题。同时,良好的错误处理和内存管理也能有效地减少页面崩溃的可能性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值