一、需求来源
app需要支持实现游客模式,启动后直接进入首页菜单,但是进入二级页则自动调用登录页面。总结需求就是父视图拦截子视图的响应事件,思考之后发现在事件响应链上做拦截是最优方法。
二、iOS 事件拦截
1、使用示例
absorbing 属性为 true 时,会拦截子视图的事件。点击 button 时只会调用 absorbPointerView(绿色) 的响应方法。
absorbing 属性为 false 时,不会拦截子视图的事件。点击 button 时只会调用 button(蓝色)的响应方法。
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 的响应方法。
// // 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 时,父视图(绿色)会拦截响应事件。
<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!!!
NNAbsorbPointerViewController.swift
以上就是大前端代码重构之事件拦截iOS Flutter Vue示例分析的详细内容,更多关于前端重构事件拦截iOS Flutter Vue的资料请关注好代码网其它相关文章!