大前端代码重构之事件拦截iOS Flutter Vue示例分析

作者:SoaringHeart 时间:2021-12-11 20:16:53 

一、需求来源

app需要支持实现游客模式,启动后直接进入首页菜单,但是进入二级页则自动调用登录页面。总结需求就是父视图拦截子视图的响应事件,思考之后发现在事件响应链上做拦截是最优方法。

大前端代码重构之事件拦截iOS Flutter Vue示例分析

二、iOS 事件拦截

1、使用示例

absorbing 属性为 true 时,会拦截子视图的事件。点击 button 时只会调用 absorbPointerView(绿色) 的响应方法。

absorbing 属性为 false 时,不会拦截子视图的事件。点击 button 时只会调用 button(蓝色)的响应方法。

大前端代码重构之事件拦截iOS Flutter Vue示例分析

import UIKit
import SnapKit
import SwiftExpand
/**
通过递归遍历将所有子视图设置 isUserInteractionEnabled = false,则该视图可以响应事件;
*/
class NNAbsorbPointerViewController: UIViewController {
   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
       edgesForExtendedLayout = []
       view.backgroundColor = .white
       title = "NNAbsorbPointerView"
       absorbPointerView.addSubview(button)
       view.addSubview(absorbPointerView)
//        view.recursion{ e in
//            e.isUserInteractionEnabled = false;
//        }
       view.addGestureTap { reco in
           debugPrint("\(Date()):reco.view")
       }
   }
   override func viewDidLayoutSubviews() {
       super.viewDidLayoutSubviews()
       let edge = UIEdgeInsets(all: 50)
       button.snp.makeConstraints { make in
           make.edges.equalToSuperview().inset(edge)
       }
       absorbPointerView.snp.makeConstraints { make in
           make.edges.equalToSuperview().inset(edge)
       }
   }
   lazy var absorbPointerView: NNAbsorbPointerView = {
       let view = NNAbsorbPointerView(frame: .zero);
       view.absorbing = true;
       view.backgroundColor = .green;
       view.addGestureTap { reco in
           debugPrint("\(Date()):NNAbsorbPointerView")
       }
       return view
   }()
   lazy var button: UIButton = {
       let view = UIButton(type: .custom);
       view.setTitle("UIButton", for: .normal)
       view.setTitleColor(.white, for: .normal)
       view.backgroundColor = .blue;
       view.addGestureTap { reco in
           debugPrint("\(Date()):button")
       }
       return view
   }()
}

2、自定义视图 NNAbsorbPointerView,用来拦截它子视图事件。

import UIKit
class NNAbsorbPointerView: UIView {
   /// 是否拦截响应
   var absorbing = false;
   // **MARK: - 重写加载方法**
   override init(frame: CGRect) {
       super.init(frame: frame);
   }
   required init?(coder: NSCoder) {
       fatalError("init(coder:) has not been implemented")
   }
   override func layoutSubviews() {
       super.layoutSubviews()
   }
   override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
       if absorbing {
           return self
       }
       // 1.判断能不能处理事件
       if isUserInteractionEnabled == false, isHidden, alpha <= 0.01 {
           return nil
       }
       // 2.判断点在不在当前控件上
       if self.point(inside: point, with: event) == false {
           return nil;
       }
       for subView in subviews.reversed() {
           let subPoint = self.convert(point, to: subView);
           if let targetView = subView.hitTest(subPoint, with: event) {
               return targetView;
           }
       }
       return self
   }
   // **MARK: - 私有方法**
}

三、Flutter 事件拦截

1、使用示例

absorbing 属性为 true 时,会拦截子视图的事件。点击蓝色 Container 时只会调用绿色 Container 的响应方法。

absorbing 属性为 false 时,不会拦截子视图的事件。点击蓝色 Container 时只会调用蓝色 Container 的响应方法。

大前端代码重构之事件拦截iOS Flutter Vue示例分析

//
//  AbsorbPointerDemo.dart
//  flutter_templet_project
//
//  Created by shang on 10/25/21 11:05 AM.
//  Copyright © 10/25/21 shang. All rights reserved.
//
// AbsorbPointer本身可以接收点击事件,消耗掉事件,而IgnorePointer无法接收点击事件,其下的控件可以接收到点击事件(不是子控件)。
import "package:flutter/material.dart";
import 'package:flutter_templet_project/extension/ddlog.dart';
class AbsorbPointerDemo extends StatefulWidget {
 const AbsorbPointerDemo({Key? key}) : super(key: key);
 @override
 _AbsorbPointerDemoState createState() => _AbsorbPointerDemoState();
}
class _AbsorbPointerDemoState extends State<AbsorbPointerDemo> {
 bool _disable = false;
 bool _switchValue = false;
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: Text('Absorbpointer'),
       centerTitle: true,
       elevation: 0,
     ),
     body: Column(
       mainAxisAlignment: MainAxisAlignment.start,
       children: <Widget>[
         Row(
           children: <Widget>[
             Text('不可点击:absorbing: ${_disable}'),
             Switch(
               value: _disable,
               onChanged: (bool val) {
                 _disable = val;
                 setState(() {});
               },
             )
           ],
         ),
         Divider(),
         _buildAbsorbPointerNew(absorbing: _disable),
         MaterialButton(
           color: Colors.lightBlue,
           onPressed: () => onClick('我是外面的按钮,不受影响'),
           child: Text('我是外面的按钮,不受影响'),
         ),
       ],
     ),
   );
 }
 /// 默认吸收事件,拦截事件
 _buildAbsorbPointerNew({bool absorbing = true}) {
   return InkWell(
     onTap: () => onClick("outside"),
     child: Container(
       color: Colors.green,
       padding: EdgeInsets.all(20),
       child: AbsorbPointer(
         absorbing: absorbing,
         child: InkWell(
           onTap: () => onClick("inside"),
           child: Container(
             color: Colors.blue,
             width: 200.0,
             height: 100.0,
             alignment: Alignment.center,
             child: Text("Container"),
           ),
         ),
       ),
     ),
   );
 }
 onClick(String msg) {
   debugPrint(msg);
 }
}

四、Web 事件拦截

1、Vue 事件拦截

实现很简单,@click 添加修饰符 capture.stop 即可拦截子标签事件。

点击 button 时,父视图(绿色)会拦截响应事件。

大前端代码重构之事件拦截iOS Flutter Vue示例分析

<template>
 <h2>{{ $route.meta.title }}</h2>
 <!-- <h2>{{ JSON.stringify(route) }}</h2> -->
 <div class="page" @click.capture.stop="doThis">
   <button @click="onClick">button</button>
 </div>
</template>
<script setup>
import { getCurrentInstance, ref, reactive, watch, onMounted, } from 'vue';
import { useRouter, useRoute } from 'vue-router';
const router = useRouter();
const route = useRoute();
const doThis = () => {
 console.log(`${new Date()}: doThis`);
};
const onClick = () => {
 console.log(`${new Date()}: onClick`);
};
</script>
<style scoped lang='scss'>
.page{
 background-color: green;
}
</style>

2、react 事件拦截

暂无

3、angular 事件拦截

暂无

最后、总结

1、iOS 还有一种办法,递归遍历所有子视图进行处理,让其不响应事件,事件自然会传递到 目标父视图,只是性能较差;

2、Flutter 中随组件类型不同有略微差距,使用时需要根据实际情况调试。

3、大前端思路都是通的,事件机制一端弄懂了就三端都差不多了,细微差距可以在实际开发中再思考总结。以后大前端加一门后端技能是时代趋势,Keep Learning!!!

AbsorbPointerDemo.dart

NNAbsorbPointerViewController.swift

EventIntercept.vue

来源:https://juejin.cn/post/7217755581846716471

标签:前端重构,事件拦截,iOS,Flutter,vue
0
投稿

猜你喜欢

  • 解决javac不是内部或外部命令,也不是可运行程序的报错问题

    2022-07-13 22:39:25
  • maven 配置多个仓库的方法

    2021-06-09 03:30:24
  • Android仿微信录制语音功能

    2022-10-18 15:54:18
  • SpringBoot如何整合redis实现过期key监听事件

    2023-08-04 18:51:19
  • C++类的返回值是*this的成员函数问题

    2023-02-01 19:52:50
  • SpringCloud Feign远程调用实现详解

    2021-09-28 11:35:15
  • C#使用DoddleReport快速生成报表

    2022-05-31 06:39:59
  • Java的Spring框架中AOP项目的一般配置和部署教程

    2021-07-26 16:49:25
  • SpringCloud实战之Zuul网关服务

    2023-01-30 11:33:53
  • java实现归并排序算法

    2023-02-09 07:34:01
  • Spring JPA 错题集解决案例

    2022-03-26 22:44:03
  • Java编程使用卡片布局管理器示例【基于swing组件】

    2022-03-03 10:46:33
  • Android桌面插件App Widget用法分析

    2022-02-05 02:26:39
  • Java线程中断interrupt的常用方法

    2021-08-08 18:31:07
  • Android控件CardView实现卡片效果

    2023-09-05 17:01:16
  • Java中Runnable与Callable接口的区别详解

    2021-12-22 11:33:28
  • Android实现MVVM架构数据刷新详解流程

    2023-07-05 13:33:41
  • java 获取日期的几天前,几个月前和几年前的实例

    2023-05-20 15:29:53
  • jmeter+ant+jenkins自动化测试环境配置搭建过程

    2023-11-17 23:38:06
  • WPF利用WindowChrome实现自定义窗口

    2021-06-08 07:33:55
  • asp之家 软件编程 m.aspxhome.com