当前位置: 首页 > news >正文

「OC」剪不断,理还乱——UIResponder、UIGestureRecognizer、UIControl的响应优先级探究

「OC」剪不断,理还乱——UIResponder、UIGestureRecognizer、UIControl的响应优先级探究

文章目录

  • 「OC」剪不断,理还乱——UIResponder、UIGestureRecognizer、UIControl的响应优先级探究
    • 前言
    • 介绍
      • UIResponder
      • UIGestureRecognizer
      • UIControl
    • 正文
      • UIGestureRecognizer和UIResponder
        • 短按
        • 快速按下
        • 长按
        • 补充
    • 乱上加乱
      • 在view上添加点击手势识别器
      • 在button上添加手势识别器
    • 总结
    • 参考资料

前言

上一篇我们记录了编译器是如何找到最佳响应者以及响应者链的构成「OC」初识iOS事件处理流程,那么今天我们就来探究,UIResponder、UIGestureRecognizer、UIControl在处理事件时的优先级,以及相关的细节。让我们开始UIResponder、UIGestureRecognizer、UIControl的爱恨情仇吧

介绍

先简单介绍一下本文的三个主角

UIResponder

UIResponder是iOS中处理事件的核心类,它是许多重要UI类的父类,包括UIApplication、UIView和UIViewController。

主要特点:

  • 定义了响应和处理事件的接口
  • 是构建响应者链的基础
  • 可以处理触摸事件、运动事件、远程控制事件等

UIGestureRecognizer

UIGestureRecognizer是一个抽象基类,用于检测特定的触摸手势。

主要特点:

  • 简化了复杂手势的识别过程
  • 可以添加到任何UIView上
  • 有多个具体子类,如UIPanGestureRecognizer, UITapGestureRecognizer等

补充:其实手势接受事件也是通过与其他两者类似的方式,即触摸开始——触摸移动——触摸取消——触摸结束,他们被写在了UIGestureRecognizerSubclass.h之中,我们下文探究触摸优先级时,打印日志需要在UITapGestureRecognizer的子类之中引入 import <UIKit/UIGestureRecognizerSubclass.h>

手势分为离散型手势(discrete gestures)和持续型手势(continuous gesture)。系统提供的离散型手势包括点按手势(UITapGestureRecognizer)和轻扫手势(UISwipeGestureRecognizer),其余均为持续型手势。

在手势之中,有三个属性比较重要

@property(nonatomic) BOOL cancelsTouchesInView;
@property(nonatomic) BOOL delaysTouchesBegan;
@property(nonatomic) BOOL delaysTouchesEnded;

cancelsTouchesInView:默认值为YES,表示当手势识别器成功识别了手势之后,会通知Application取消响应链对事件的响应,并不再传递事件给hit-test view。若设置成NO,表示手势识别成功后不取消响应链对事件的响应,事件依旧会传递给hit-test view。

delaysTouchesBegan:默认为NO。默认情况下手势识别器在识别手势期间,当触摸状态发生改变时,Application都会将事件传递给手势识别器和hit-tested view;若设置成YES,则表示手势识别器在识别手势期间,截断事件,即不会将事件发送给hit-tested view。

delaysTouchesEnded:默认为YES。当手势识别失败时,若此时触摸已经结束,会延迟一小段时间(0.15s)再调用响应者的 touchesEnded:withEvent:;若设置成NO,则在手势识别失败时会立即通知Application发送状态为end的touch事件给hit-tested view以调用 touchesEnded:withEvent: 结束事件响应。

UIControl

UIControl是一个抽象基类,为特定的用户界面元素(如按钮、开关、滑块等)提供了基础结构。它的继承了UIView,而UIView又继承于UIResponder,所以在某些时候我们可以将两者等同看待,在细节方面有相似之处

主要特点:

  • 继承自UIView
  • 定义了一组标准的控制事件(如触摸按下、抬起、拖动等)
  • 允许为不同事件添加多个目标动作

target-action

  • target:处理交互事件的对象
  • action:处理交互事件的方式

UIControl作为能够响应事件的控件,必然也需要待事件交互符合条件时才去响应,因此也会跟踪事件发生的过程。不同于UIResponder以及UIGestureRecognizer通过 touches 系列方法跟踪,UIControl有其独特的跟踪方法:

- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event;
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event;
- (void)endTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)event;
- (void)cancelTrackingWithEvent:(nullable UIEvent *)event;

大致方法其实与我们先前接触的UIResponder的那4个方法相同,毕竟UIControl本身也是UIResponder的子类,但是这个UIControl之中的tracking系列的方法是在touch系列之中进行实现的。

具体的target-action具体流程,我按着网上给出的内容叙述一遍:

UIControl控件通过 addTarget:action:forControlEvents: 添加事件处理的target和action,当事件发生时,会调用 sendAction:to:forEvent: 将target、action以及event对象发送给全局应用,Application对象再通过 sendAction:to:from:forEvent: 向target发送action。

img

另外,若不指定target,即 addTarget:action:forControlEvents: 时target传空,那么当事件发生时,Application会在响应链上从上往下寻找能响应action的对象。

正文

接下来我们来探究一下,事件产生的先后优先顺序

我们先写一个简单的demo进行测试

#import "ViewController.h"@interface ViewController () <UITableViewDelegate, UITableViewDataSource>@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) NSArray *dataSource;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];self.title = @"手势冲突测试";self.view.backgroundColor = [UIColor whiteColor];// 初始化数据源self.dataSource = @[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8"];// 创建并设置UITableViewself.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];self.tableView.delegate = self;self.tableView.dataSource = self;self.tableView.backgroundColor = [UIColor orangeColor];self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;[self.view addSubview:self.tableView];UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(actionTapView)];[self.view addGestureRecognizer:tap];UIButton *bottomButton = [UIButton buttonWithType:UIButtonTypeCustom];[bottomButton setTitle:@"点我无压力" forState:UIControlStateNormal];[bottomButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];bottomButton.backgroundColor = [UIColor cyanColor];bottomButton.frame = CGRectMake(20, CGRectGetMaxY(self.view.bounds) - 60, CGRectGetWidth(self.view.bounds) - 40, 40);[bottomButton addTarget:self action:@selector(buttonTap) forControlEvents:UIControlEventTouchUpInside];[self.view addSubview:bottomButton];
}-(void)buttonTap {NSLog(@"button clicked!");
}#pragma mark - UITableViewDataSource- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {return self.dataSource.count;
}- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {return 55;
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {static NSString *cellIdentifier = @"Cell";UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];if (!cell) {cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];cell.backgroundColor = [UIColor orangeColor];cell.textLabel.textColor = [UIColor whiteColor];}cell.textLabel.text = self.dataSource[indexPath.row];return cell;
}#pragma mark - UITableViewDelegate- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {NSLog(@"cell selected!");
}- (void)actionTapView{NSLog(@"backview taped");
}@end

我们进行点击进行尝试

Sep-03-2024 21-21-52

可以发现当我短按cell的时候,触发的是手势事件,只有长按cell才能够触发点击cell的方法,这到底是为什么啊?

由于这三兄弟,都实现了类似的方法,所以我在他们的子类之中,增加的打印日志的行为,以探究这三者的运行关系

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;

具体打印日志的方式如下

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{NSLog(@"%s",__func__);[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{NSLog(@"%s",__func__);[super touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{NSLog(@"%s",__func__);[super touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{NSLog(@"%s",__func__);[super touchesCancelled:touches withEvent:event];
}

UIGestureRecognizer和UIResponder

关于UIGestureRecognizer和UIResponder的问题,其实上面UIGestureRecognizer的属性已经给我们透露一些相关的内容了,那我们就先从我们最有疑问的开始探究吧。

短按

image-20240904155530051

在短按的时候我们可以看到,其实在我们短按的时候,手势识别先开始,而tableView的触摸在随后也开始了,之所以我们的无法触发选择cell的方法是因为,cell的触摸在手势触发之后被Application给取消了。

引用官方文档的翻译:Window在将事件传递给hit-tested view之前,会先将事件传递给相关的手势识别器并由手势识别器优先识别。若手势识别器成功识别了事件,就会取消hit-tested view对事件的响应;若手势识别器没能识别事件,hit-tested view才完全接手事件的响应权。

因此通过这个点,我们可以总结出:手势识别器比UIResponder具有更高的事件响应优先级!!

快速按下

有的读者可能就有疑惑了,咋一听,如果短按的道理是上面解释的那样,那么按理来说快速按下的响应过程和短按应该是相同的才对。

我们知道UITableView是UIScrollView的子类,而UIScrollView因为要滚动,所以对事件做了特殊的处理: 当UIScrollView接收到事件之后,会暂时劫持当前的事件,会产生一个UIScrollViewDelayedTouchesBeganGestureRecognizer的手势识别器识别是否进行滚动操作,由于手势操作在应用之中可以进行同时识别,所以当UIScrollView这个机制劫持了触摸事件,和TableViewtouchesBegan: 的调用时间相差大概为0.15s,由于UIScrollViewDelayedTouchesBeganGestureRecognizer拦截了touchesBegan: 发送,当我们点击时间过短的话,没过延迟时间触摸就结束了,因此就会出现以下情况。

请添加图片描述

长按

由于我们手势劫持判断是有时效的,当时效结束之后,hit-tested view才完全接手事件,停止向手势识别器发送事件,仅向hit-test view发送事件。

image-20240904155620433

补充

刚刚我们讲的是离散型的手势,接下来我们补充一下持续性手势,仍是相同的内容但是把手势改为持续性的手势,

image-20240904200533662

我们可以看到即便滑动手势识别器识别了手势,Application也会依旧发送事件给tableView。

另外,在滑动的过程中,若手势识别器未能识别手势,则事件在触摸滑动过程中会一直传递给hit-tested view,直到触摸结束。

乱上加乱

如果让本就纠缠不清,复杂无比的UIGestureRecognizer和UIResponder,再有第三者UIControl插足呢?

真是头大,看的我都快看不懂字了。

根据官方文档,总结内容如下:UIControl会阻止父视图上的手势识别器行为,也就是UIControl处理事件的优先级比UIGestureRecognizer高,但前提是相比于父视图上的手势识别器。

我们设置一下情景

请添加图片描述

在view上添加点击手势识别器

image-20240904210011938

点击button后,事件先传递给手势识别器,再传递给作为hit-tested view存在的button(UIControl本身也是UIResponder),由于button阻止了父视图BlueView中的手势识别器的识别,导致手势识别器识别失败,button完全接手了事件的响应权,事件最终由button响应;

在button上添加手势识别器

请添加图片描述

点击button后,事件先传递给手势识别器,再传递给作为hit-tested view存在的button(UIControl本身也是UIResponder),button未阻止其本身绑定的手势识别器的识别,因此手势识别器先识别手势并识别成功,而后通知Application取消响应链对事件的响应,因为 touchesCancelled 被调用,同时 cancelTrackingWithEvent 跟着调用,因此button的target-action得不到执行。

所以我们可以得出结论:

UIControl比其父视图上的手势识别器具有更高的事件响应优先级。

总结

最后我们可以笼统的得出一个结论:UIRespnder、UIGestureRecognizer、UIControl,笼统地讲,事件响应优先级依次递增。

参考资料

01 触摸事件传递

iOS事件处理

iOS——事件、响应链和传递链

iOS触摸事件全家桶

UIGestureRecognizer

相关文章:

「OC」剪不断,理还乱——UIResponder、UIGestureRecognizer、UIControl的响应优先级探究

「OC」剪不断&#xff0c;理还乱——UIResponder、UIGestureRecognizer、UIControl的响应优先级探究 文章目录 「OC」剪不断&#xff0c;理还乱——UIResponder、UIGestureRecognizer、UIControl的响应优先级探究前言介绍UIResponderUIGestureRecognizerUIControl 正文UIGestur…...

GitHub Copilot的详细介绍

目录 主要功能&#xff1a; 示例用法&#xff1a; GitHub Copilot 的优缺点&#xff1a; 优点&#xff1a; 缺点&#xff1a; 如何使用 GitHub Copilot&#xff1f; 总结&#xff1a; GitHub Copilot 是一种基于人工智能的编程助手&#xff0c;由 GitHub 和 OpenAI 联合…...

opencv之阈值处理

文章目录 1. 阈值处理2. 阈值处理的基本原理3. 常见的阈值处理方法3.1 全局阈值&#xff08;Global Thresholding&#xff09;:3.2 自适应阈值&#xff08;Adaptive Thresholding&#xff09;:3.2.1 工作原理3.2.2 工作步骤3.2.3 适用场景3.2.4 优缺点自适应阈值的优点自适应阈…...

oracle startup失败,ORA-01078: failure in processing system parameters

SQL> startup ORA-01078: failure in processing system parameters LRM-00109: could not open parameter file /data/oracle/product/11.2.0/db_1/dbs/initorc1.ora 出错的原因可能是&#xff1a;文件名字不正确&#xff0c;文件权限不对&#xff0c;文件不存在&#x…...

【python因果推断库7】使用 pymc 模型的工具变量建模 (IV)2

目录 与普通最小二乘法 (OLS) 的比较 应用理论&#xff1a;政治制度与GDP 拟合模型&#xff1a;贝叶斯方法 多变量结果和相关性度量 结论 与普通最小二乘法 (OLS) 的比较 simple_ols_reg sk_lin_reg().fit(X.reshape(-1, 1), y)print("Intercept:", simple_ols_…...

【2024数模国赛赛题思路公开】国赛B题思路丨附可运行代码丨无偿自提

2024年国赛B题解题思路 问题 1: 抽样检测方案设计 【题目分析】 分析&#xff1a; 目标是设计一个高效的抽样检测方案&#xff0c;在尽量少的样本数量下&#xff0c;确保在高信度水平下做出正确的接受或拒收决策。需要处理两个不同的信度要求&#xff0c;这对样本量的计算提…...

智能优化特征选择|基于鲸鱼WOA优化算法实现的特征选择研究Matlab程序(KNN分类器)

智能优化特征选择|基于鲸鱼WOA优化算法实现的特征选择研究Matlab程序&#xff08;KNN分类器&#xff09; 文章目录 一、基本原理原理流程举个例子总结 二、实验结果三、核心代码四、代码获取五、总结 智能优化特征选择|基于鲸鱼WOA优化算法实现的特征选择研究Matlab程序&#x…...

使用udp进行通信

UDP chat 头文件 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <time…...

C#上位机使用Microsoft.Office.Interop.Excel和EPPlus库对Excel或WPS表格进行写操作

C#上位机使用Microsoft.Office.Interop.Excel和EPPlus库对Excel或WPS表格进行写操作 一、使用Microsoft.Office.Interop.Excel库 1、通过NuGet包管理器添加引用 按照下图中红框所示进行操作。 需要安装Microsoft.Office.Interop.Excel包 添加Microsoft Office 16.0 Object …...

java重点学习-redis

一.redis 穿透无中生有key&#xff0c;布隆过滤nul隔离 锁与非期解难题。缓存击穿过期key&#xff0c; 雪崩大量过期key&#xff0c;过期时间要随机。 面试必考三兄弟&#xff0c;可用限流来保底。 1.1 Redis的使用场景 根据自己简历上的业务进行回答 缓存穿透、击穿、雪崩、双…...

每日刷题(图论)

P1119 灾后重建 P1119 灾后重建 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路 看数据范围知道需要用到Floyd算法&#xff0c;但是道路是不能直接用的&#xff0c;需要等到连接道路的两个村庄重建好才可以使用&#xff0c;所以这需要按照时间依次加入中转点&#xff0c…...

Requestium - 将Requests和Selenium合并在一起的自动化测试工具

Requests 是 Python 的第三方库&#xff0c;主要用于发送 http 请求&#xff0c;常用于接口自动化测试等。 Selenium 是一个用于 Web 应用程序的自动化测试工具。Selenium 测试直接运行在浏览器中&#xff0c;就像真正的用户在操作一样。 本篇介绍一款将 Requests 和 Seleniu…...

mysql和pg等数据库之间的数据迁移实战分享

mysql和pg等数据库之间的数据迁移是常见的问题&#xff1a;比如一开始使用Oracle&#xff0c;后来想使用mysql&#xff0c;而且需要把Oracle数据库的数据迁移到mysql里面&#xff1b;后期有想使用pg数据库&#xff0c;同时需要把Mysql数据库的数据迁移到pgl里面&#xff0c;等等…...

消息中间件都有哪些

RabbitMQ&#xff1a;这可是一个开源的消息代理软件&#xff0c;也叫消息中间件。它支持多种消息传递协议&#xff0c;可以轻松地在分布式系统中进行可靠的消息传递。 Kafka&#xff1a;Apache Kafka是一个分布式流处理平台&#xff0c;它主要用于处理实时数据流。Kafka的设计初…...

数据结构(3)内核链表

一、内核链表 内核链表是一种在操作系统内核中使用的数据结构&#xff0c;主要用于管理和组织内核对象。它是有头双向链表的一种实现。 内核链表的特点 双向链表: 内核链表的每个节点都包含指向前一个节点和后一个节点的指针&#xff0c;这使得在链表中进行插入和删除操作时更…...

Linux 硬件学习 s3c2440 arm920t蜂鸣器

1.查找手册时钟图&#xff0c;输入12m想要通过pll得到400m的信号 2.对比pll值&#xff0c;找到最近的为405&#xff0c;得到pll中mdiv为127&#xff0c;pdiv为2&#xff0c;sdiv为1 3.想要得到fclk400&#xff0c;hclk100&#xff0c;pclk50&#xff0c;对比分频比例&#xff0…...

提交保存,要做重复请求拦截,避免出现重复保存的问题

**问题&#xff1a;**前端ajax提交数据的时候&#xff0c;当频繁点击的时候&#xff0c;或者两个账号以相同数据创建的时候&#xff0c;会出现问题。 **处理办法&#xff1a;**前端拦截&#xff0c;防止重复提交数据&#xff0c;在上一次请求返回结果之后才允许提交第二次&…...

华为 HCIP-Datacom H12-821 题库 (3)

有需要题库的可以看主页置顶​​​​​​​ 1.运行 OSPF 协议的路由器在交互 DD 报文时&#xff0c;会使用以下哪一个参数选举主从关系&#xff1f; A、接口的 IP 地址 B、接口的 DR 优先级 C、Area ID D、Router ID 答案&#xff1a;D 解析&#xff1a; Router-ID 大的为主&a…...

spring-boot 事件

事件触发时机常用监听器描述ApplicationStartingEvent应用启动时LoggingApplicationListener&#xff1a;决定加载哪个日志系统ApplicationEnvironmentPreparedEvent创建Environment之后BootstrapApplicationListener&#xff1a;加载spring-cloud bootstrap配置文件&#xff1…...

合碳智能 × Milvus:探索化学合成新境界——逆合成路线设计

合碳智能&#xff08;C12.ai&#xff09;成立于2022年&#xff0c;致力于运用AI和具身智能技术&#xff0c;为药物研发实验室提供新一代智能化解决方案&#xff0c;推动实验室从自动化迈向智能化&#xff0c;突破传统实验模式与人员的依赖&#xff0c;解决效率和成本的瓶颈&…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...