【iOS】事件传递与响应机制

文章目录

  • 前言
  • 事件UIEvent
  • 一、事件传递
    • 遍历顺序
  • 二、手势识别
  • 三、响应机制
      • UIResponder(响应者)
      • 响应者链
  • 四、相关应用
    • 扩大button点击范围
    • 穿透事件
  • 总结


前言

提到响应者链与事件传递,如果看过其他人的博客,经常能看到这经典的三张图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文会对事件的传递与响应机制进行详细的讲解

事件UIEvent

在开讲之前,我们先来理解一下UIEvent
事件指的是 UIEvent : NSObject,它的API文档很简单:

typedef NS_ENUM(NSInteger, UIEventType) {
    UIEventTypeTouches,
    UIEventTypeMotion,
    UIEventTypeRemoteControl,
    UIEventTypePresses API_AVAILABLE(ios(9.0)),
    UIEventTypeScroll      API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) = 10,
    UIEventTypeHover       API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) = 11,
    UIEventTypeTransform   API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) = 14,
};

typedef NS_ENUM(NSInteger, UIEventSubtype) {
    // available in iPhone OS 3.0
    UIEventSubtypeNone                              = 0,

    // for UIEventTypeMotion, available in iPhone OS 3.0
    UIEventSubtypeMotionShake                       = 1,

    // for UIEventTypeRemoteControl, available in iOS 4.0
    UIEventSubtypeRemoteControlPlay                 = 100,
    UIEventSubtypeRemoteControlPause                = 101,
    UIEventSubtypeRemoteControlStop                 = 102,
    UIEventSubtypeRemoteControlTogglePlayPause      = 103,
    UIEventSubtypeRemoteControlNextTrack            = 104,
    UIEventSubtypeRemoteControlPreviousTrack        = 105,
    UIEventSubtypeRemoteControlBeginSeekingBackward = 106,
    UIEventSubtypeRemoteControlEndSeekingBackward   = 107,
    UIEventSubtypeRemoteControlBeginSeekingForward  = 108,
    UIEventSubtypeRemoteControlEndSeekingForward    = 109,
};

/// Set of buttons pressed for the current event
/// Raw format of: 1 << (buttonNumber - 1)
/// UIEventButtonMaskPrimary = 1 << 0
typedef NS_OPTIONS(NSInteger, UIEventButtonMask) {
    UIEventButtonMaskPrimary    = 1 << 0,
    UIEventButtonMaskSecondary  = 1 << 1
} NS_SWIFT_NAME(UIEvent.ButtonMask) API_AVAILABLE(ios(13.4)) API_UNAVAILABLE(tvos, watchos);

/// Convenience initializer for a button mask where `buttonNumber` is a one-based index of the button on the input device
/// .button(1) == .primary
/// .button(2) == .secondary
UIKIT_EXTERN UIEventButtonMask UIEventButtonMaskForButtonNumber(NSInteger buttonNumber) NS_SWIFT_NAME(UIEventButtonMask.button(_:)) API_AVAILABLE(ios(13.4)) API_UNAVAILABLE(tvos, watchos);

UIKIT_EXTERN API_AVAILABLE(ios(2.0)) @interface UIEvent : NSObject

@property(nonatomic,readonly) UIEventType     type API_AVAILABLE(ios(3.0));
@property(nonatomic,readonly) UIEventSubtype  subtype API_AVAILABLE(ios(3.0));

@property(nonatomic,readonly) NSTimeInterval  timestamp;

@property (nonatomic, readonly) UIKeyModifierFlags modifierFlags API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos);
@property (nonatomic, readonly) UIEventButtonMask buttonMask API_AVAILABLE(ios(13.4)) API_UNAVAILABLE(tvos, watchos);

@property(nonatomic, readonly, nullable) NSSet <UITouch *> *allTouches;
- (nullable NSSet <UITouch *> *)touchesForWindow:(UIWindow *)window;
- (nullable NSSet <UITouch *> *)touchesForView:(UIView *)view;
- (nullable NSSet <UITouch *> *)touchesForGestureRecognizer:(UIGestureRecognizer *)gesture API_AVAILABLE(ios(3.2));

// An array of auxiliary UITouch’s for the touch events that did not get delivered for a given main touch. This also includes an auxiliary version of the main touch itself.
- (nullable NSArray <UITouch *> *)coalescedTouchesForTouch:(UITouch *)touch API_AVAILABLE(ios(9.0));

// An array of auxiliary UITouch’s for touch events that are predicted to occur for a given main touch. These predictions may not exactly match the real behavior of the touch as it moves, so they should be interpreted as an estimate.
- (nullable NSArray <UITouch *> *)predictedTouchesForTouch:(UITouch *)touch API_AVAILABLE(ios(9.0));

@end

NS_ASSUME_NONNULL_END

#else
#import <UIKitCore/UIEvent.h>
#endif

我们以 UIEventType 作为突破口

typedef NS_ENUM(NSInteger, UIEventType) {
    UIEventTypeTouches,
    UIEventTypeMotion,
    UIEventTypeRemoteControl,
    UIEventTypePresses API_AVAILABLE(ios(9.0)),
    UIEventTypeScroll      API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) = 10,
    UIEventTypeHover       API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) = 11,
    UIEventTypeTransform   API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) = 14,
};

目前iOS主流使用的事件有三种

  • touch events(触摸事件)
  • motion events(运动事件)
  • remote-control events(远程控制事件)
    在这里插入图片描述

我们这里着重讲一下触摸事件

触摸事件就是我们的手指或者苹果的
Pencil(触笔)在屏幕中所引发的互动,比如轻点、长按、滑动等操作,是我们最常接触到的事件类型。触摸事件对象可以包含一个或多个触摸,并且每个触摸由
UITouch 对象表示。当触摸事件发生时,系统会将其沿着线路传递,找到适当的响应者并调用适当的方法,例如
touchedBegan:withEvent:。响应者对象会根据触摸来确定适当的方法。 触摸事件分为以下几类:

手势事件

  • 长按手势(UILongPressGestureRecognizer)
  • 拖动手势(UIPanGestureRecognizer)
  • 捏合手势(UIPinchGestureRecognizer)
  • 响应屏幕边缘手势(UIScreenEdgePanGestureRecognizer)
  • 轻扫手势(UISwipeGestureRecognizer)
  • 旋转手势(UIRotationGestureRecognizer)
  • 点击手势(UITapGestureRecognizer)
  • 自定义手势
  • 点击 button 相关

触摸事件对应的对象为 UITouch,UITouch实际上就对应着我们的手指,有几根手指就有几个UITouch对象

一、事件传递

事件传递机制(Event Handling)
iOS的事件传递系统将触摸和其他事件(如动作、手势)发送到视图层次结构中的适当对象。在事件传递过程中,系统通常从根视图开始查找,并递归向下查找以找到最适合处理该事件的视图。

传递流程

  • 事件的产生
    用户通过手势或是触摸等其他操作与设备交互,生成事件,系统将事件传递给应用的UIApplication实例,以开始事件分发
  • UIApplication事件分发
    UIApplication负责顶层管理所有用户输入事件。
    它将事件传递给当前活动的UIWindow对象,以进一步查找适合的响应者。
  • UIWindow事件分发
    当前活动的UIWindow对象接收事件并通过hitTest:withEvent:方法开始寻找适当的视图
    UIWindow遍历整个视图层次结构,以找到最合适的视图来响应事件。
  • 命中测试(Hit-Testing)
    hitTest:withEvent:寻找第一响应者的核心方法。它通过以下步骤工作:
  1. 检查当前视图的userInteractionEnabled、hiddenalpha属性以确保视图可交互。当视图隐藏属性hidden=NO、交互userInteractionEnabled=YES、透明度alpha>0.01三者同时满足才拥有响应能力。
  2. 调用pointInside:withEvent:,确定触摸点是否在当前视图的边界范围内。
  3. 从后往前遍历子视图,递归调用子视图的hitTest:withEvent:方法。
  4. 如果找到合适的子视图,它将返回该子视图作为第一响应者;否则返回当前视图自身。

我们可以写一个其简单实现实例

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    // 视图不能交互、隐藏或不可见时,直接返回nil
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha < 0.01) {
        return nil;
    }

    // 判断触摸点是否在当前视图范围内
    if (![self pointInside:point withEvent:event]) {
        return nil;
    }

    // 从后往前遍历子视图(子视图叠加次序),递归调用
    for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
        // 转换坐标到子视图的坐标系
        CGPoint convertedPoint = [subview convertPoint:point fromView:self];
        // 递归查找子视图
        UIView *hitView = [subview hitTest:convertedPoint withEvent:event];
        if (hitView != nil) {
            return hitView;
        }
    }

    // 没有合适的子视图时,当前视图自己成为第一响应者
    return self;
}
  • 第一响应者确定
    如果确定了当前触摸点在当前视图上,同时当前视图没有任何子视图,那么当前视图就成为第一响应者并开始处理触摸事件
    touchesBegan:withEvent:、touchesMoved:withEvent:、touchesEnded:withEvent:、touchesCancelled:withEvent:方法由第一响应者接收并处理,这些都是触摸事件

讲到这里,其实我们的事件传递就已经结束了,事件传递的目的就是为了让我们找到第一响应者

总结一下第一响应者

  1. 能够响应触摸事件
  2. 触摸点在自己身上
  3. 没有任何子视图,或是所有子视图都不在触摸点上

在这里插入图片描述

遍历顺序

我们在上图中看到了在subViews中查找响应者的过程是倒序遍历,这是什么意思呢

也就是我们遍历当前视图的子视图时,首先hitTest:withEvent:方法会被子视图数组中的最后一个元素调用

如何理解这句话呢?简单理解就是会从最后一个添加到当前视图的子视图进行遍历,也就是视图上最上层的子视图是第一个被遍历的,然后再继续去遍历其他子视图,我们来看一个demo:

在这里插入图片描述
可以看到touchView1先被添加到view中,随后再添加touchView2,我们来看一下subViews数组

在这里插入图片描述
可以看到touchView2在数组的尾部,这也就说明了倒序遍历就是从后面添加的视图向前遍历

二、手势识别

找到了合适的View,也就是第一响应者,如果是触摸事件,我们就要去识别是何种手势

使用不同的手势会调用不同次数的事件,这里我们不细讲,只要知道有这么一个过程即可

三、响应机制

在了解响应者链前,我们需要知道什么是响应者

UIResponder(响应者)

在 iOS 中,只有继承于 UIResponder 的对象、或者它本身才能成为响应者。很多常见的对象都可以相应事件,比如 UIApplication 、UIViewController、所有的 UIView(包括 UIWindow)
我们来看一张继承图
请添加图片描述
可以看到UIResponder提供了我们平时最常用的touchesBegan/touchesMoved/touchesEnded方法。此外还有如下几个属性比较重要:

  • isFirstResponder:判断该View是否为第一响应者。
  • canBecomeFirstResponder:判断该View是否可以成为第一响应者。
  • becomeFirstResponder:使该View成为第一响应者。
  • resignFirstResponder:取消View的第一响应者。

如果我们将一个view_A先加在view_B上,然后又加到view_C上,那么view_A.nextResponder指的是view_B

响应者链

找到第一响应者之后并且识别出手势后,我们就要确定由谁来响应这个事件了,如何理解这句话呢?
第一响应者不一定能响应事件,因为他可能并没有实现触摸事件

我们来以一个Demo来理解
在这里插入图片描述
在这里插入图片描述
红色的是V1,蓝色的是V2,V2是第一响应者

我们为我们的V1添加点击事件
在这里插入图片描述

我们点击蓝色区域
在这里插入图片描述
可以看到响应触摸事件的我们的V1,也就是红色区域,这也说明了第一响应者不一定能响应事件

这里需要注意的一点是如果我们要给视图添加触摸事件,一定要新建一个子类View,不能再UIViewC中重写touches实例方法,因为这样事件的响应者就是UIViewC而非你期望中的View,我们也以一个Demo来示范

在VC中重写方法
在这里插入图片描述

在VC中重写方法
在这里插入图片描述
可以看到当我点击空白区域时候响应的是VC,点击蓝色或是红色区域时响应的是V1,这是因为事件的传递是沿响应者链传递的,由此引出我们对响应者链的讨论

响应者链示意图:
在这里插入图片描述
在这里插入图片描述

  • Response Chain,响应链,一般我们称之为响应者链。
  • 在我们的 app 中,所有的视图都是按照一定的结构组织起来的,即树状层次结构,每个 view 都有自己的 superView,包括 controller 的 topmost view(即 controller 的 self.view)。
  • 当一个 view 被 add 到 superView 上的时候,它的 nextResponder 属性就会被指向它的 superView。
  • 当 controller 被初始化的时候,self.view(topmost view) 的 nextResponder 会被指向所在的 controller,而 controller 的 nextResponder 会被指向 self.view的superView。
  • 这样,整个 app 就通过 nextResponder 串成了一条链,这就是我们所说的响应者链。
  • 所以响应者链是一条虚拟的链,并没有一个对象来专门存储这样的一条链,而是通过 UIResponder 的属性串联起来的。
    @property(nonatomic, readonly, nullable) UIResponder *nextResponder;

总结一下响应者链的响应流程
判断当前视图能否响应,再去判断当前视图的nextResponder,如果是VCView,那么nextResponder就是VC
如果不是控制器的 View,上一个响应者就是SuperView

响应的大致的过程 第一响应者 –> super view –> ……–> view controller –> window –>Application

四、相关应用

扩大button点击范围

解决:给button加分类然后重写pointInside
实现步骤:

  • 自定义按钮:创建一个自定义按钮子类,继承自 UIButton。
  • 重写 point(inside:with:):在自定义按钮类中重写此方法。该方法接受一个点,并判断该点是否在视图的范围内。你可以扩展点击区域,以便更宽泛的区域内点击时视图仍然会接收点击事件。
  • 设定点击区域扩展的大小:可以设定需要扩大点击范围的值,在四个方向上(上、下、左、右)同时增大或仅针对特定方向。
// CustomButton.h
#import <UIKit/UIKit.h>

@interface CustomButton : UIButton

@property (nonatomic) UIEdgeInsets hitTestEdgeInsets;

@end

// CustomButton.m
#import "CustomButton.h"

@implementation CustomButton

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    // 计算新的扩大后的点击区域
    CGRect largerFrame = CGRectMake(self.bounds.origin.x - self.hitTestEdgeInsets.left,
                                    self.bounds.origin.y - self.hitTestEdgeInsets.top,
                                    self.bounds.size.width + self.hitTestEdgeInsets.left + self.hitTestEdgeInsets.right,
                                    self.bounds.size.height + self.hitTestEdgeInsets.top + self.hitTestEdgeInsets.bottom);

    // 判断点是否在新的点击区域内
    return CGRectContainsPoint(largerFrame, point);
}

@end

在你的视图控制器中,将自定义按钮的 hitTestEdgeInsets 属性设置为所需的值,以扩大点击区域:

// Example usage in a view controller
CustomButton *button = [[CustomButton alloc] initWithFrame:CGRectMake(100, 100, 100, 40)];
button.backgroundColor = [UIColor systemBlueColor];
[button setTitle:@"Click Me" forState:UIControlStateNormal];

// 将点击区域向四个方向各扩展10个点
button.hitTestEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10);

[self.view addSubview:button];

穿透事件

在这里插入图片描述
例如我们想点击蓝色区域时响应事件的是红色区域,但是第一响应者是蓝色区域,那么就需要我们重写(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event方法,让其无法成为响应者,这样就会让红色成为响应者

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    self.userInteractionEnabled = NO;
    return [super hitTest:point withEvent:event];
   }

总结

  • 当触摸事件发生后,系统会自动生成一个UIEvent对象,记录事件产生的时间和类型
  • 然后系统会将UIEvent事件加入到一个由UIApplication管理的事件队列中
  • 然后UIApplication将事件分发给UIWindow,主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件
  • 不断递归调用hitTest方法来找到第一响应者
  • 如果第一响应者无法响应事件,那么按照响应者链往上传递,也就是传递给自己的父视图
  • 一直传递直到UIApplication,如果都无法响应则事件被丢弃
    参考博客事件传递与响应 详解(精通iOS系列)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/609171.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《探索信息技术中心一体化系统架构:提升企业数字化转型的效率与能力》

在当今数字化时代&#xff0c;信息技术中心&#xff08;IT Center&#xff09;扮演着至关重要的角色&#xff0c;它不仅是企业的技术支撑&#xff0c;更是推动数字化转型的引擎。为了更好地应对日益复杂的业务需求和技术挑战&#xff0c;许多企业开始探索一体化系统架构&#x…

引用数据类型 栈内存 堆内存

let m { a: 10, b: 20 }; let n m; n.a 15; console.log(m.a) // 15 原因&#xff1a;基本数据类型存储在栈内存中&#xff0c;引用数据类型存储在堆内存中 &#xff0c;引用数据类型存储在堆内存中会在栈内存中创建一个指针&#xff0c;栈内存中的这个指针指向堆内存中的地…

英语学习笔记9——How are you today?

How are you today? 你好吗&#xff1f; 词汇 Vocabulary well adj. 好的 n. 井 fine adj. 美好的 两个方面&#xff1a;天气、身体。 搭配&#xff1a;a fine day 晴朗的一天    It’s a fine day today. 今天很晴朗。 good adj. 好的 口语偏多 搭配&#xff1a;Good jo…

基于光纤技术的新能源汽车电池安全监测--FOM²系统

为什么要进行动力电池包的温度监测&#xff1f; 新能源电动汽车的动力电池包的工作温度&#xff0c;不仅会影响电池包性能&#xff0c;而且直接关系到车辆安全。时有发生的新能源汽车电池包起火事件&#xff0c;对电池包、冷却系统以及电池管理系统&#xff08;BMS&#xff09…

初阶C语言(8) - 实用的调试技巧

1. 什么是bug? bug 是计算机领域专业术语&#xff0c;是计算机在硬件、软件、协议和系统安全策略上存在的缺陷&#xff0c;攻击者能够在未授权情况下访问的危害&#xff0c;世界最早的一批程序设计师之一&#xff0c;美国的葛丽丝霍波在调试设备时出现故障&#xff0c;拆开继电…

Java基础教程 - 6 方法

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 6 方法 方法也可以称之为…

算法day03

第一题 179. 查找总价格为目标值的两个商品 本题采用前后指针和单调性规律可解&#xff1b; 解题思路如下&#xff1a; 1、设置前后指针 2、前后指针所指的两数之和大于目标数&#xff0c;右指针左移&#xff1b; 前后指针所指的两数之和小于目标数&#xff0c;左指针右移&…

软件工程复习之软件定义时期

1.什么是软件&#xff1f; 答&#xff1a;软件是程序&#xff0c;数据和文档的集合。 程序是完成指定功能的计算机可执行的指令序列。 数据是程序进行信息处理的数据结构。 文档是开发&#xff0c;使用&#xff0c;维护的图文资料。 2.软件有何特点&#xff1f; 答&#…

2024年小沙弥小视频,轻松吸引中老年观众,上手简单,轻松月入了万

利用人工智能工具制作小沙弥主题的抖音内容&#xff0c;已成为网络赚钱的新途径。这个项目主要吸引中老年人群体&#xff0c;尤其是对智慧和人生哲理感兴趣的观众。小沙弥以其温馨且启发性的内容&#xff0c;引起中老年用户的共鸣&#xff0c;为他们提供心灵慰藉。 项 目 地 …

上传自己的项目到PyPI

准备工作 已注册pypi账号pypi账号已经配置了双重验证pypi账号的token令牌&#xff08;最后上传到pypi需要这个&#xff09;pip install twine&#xff08;上传需要用到的工具&#xff09; 操作步骤 1、准备好工程2、编写setup.py3、开始上传 大功告成 在pypi查看自己包的…

Linux—— 任务规划、SELinux、ACL、磁盘介绍

任务规划&#xff1a; 未来任务的一次性调度 atatqatrm服务&#xff1a; atd周期性任务的调度 crontab 所有用户的任务列表都以文件的形式存在于系统 /var/spool/cron/用户名-l -e-u-r时间 需要执行的任务服务&#xff1a; crond系统维护任务&#xff1a; yum软件仓库缓存的…

MySQL数据库基础(数据库操作,常用数据类型,表的操作)

MySQL数据库基础&#xff08;数据库操作&#xff0c;常用数据类型&#xff0c;表的操作&#xff09; 前言 数据库的操作1.显示当前数据库2.创建数据库3.使用数据库4.删除数据库 常用数据类型1.数值类型2.字符串类型3.日期类型 表的操作1.查看表结构2.创建表3.删除表 总结 前言 …

5. 分布式链路追踪TracingFilter改造增强设计

前言 在4. 分布式链路追踪客户端工具包Starter设计一文中&#xff0c;我们实现了基础的Starter包&#xff0c;里面提供了我们自己定义的Servlet过滤器和RestTemplate拦截器&#xff0c;其中Servlet过滤器叫做HoneyTracingFilter&#xff0c;仅提供了提取SpanContext&#xff0…

等保测评技术方案

等保&#xff0c;即“网络安全等级保护”&#xff0c;是中国实施的一项信息安全保护制度&#xff0c;旨在对不同重要性的信息和信息系统实行分等级保护&#xff0c;保障国家安全、社会秩序以及公共利益。等保技术方案是指为了达到国家网络安全等级保护标准要求&#xff0c;针对…

Linux的并发与竞争

文章目录 一、并发二、竞争三、保护内容是什么四、解决并发与竞争的几种常用方法1.原子操作原子整型API函数原子位操作 API 函数 2.自旋锁自旋锁格式如下&#xff1a;自旋锁 API 函数自旋锁的使用注意事项 3.信号量信号量 API 函数信号量格式如下&#xff1a; 4.互斥体API函数如…

正交频分复用回顾(通俗易懂)

OFDM我们知道&#xff0c;叫做正交频分复用&#xff0c;它是4G的一个关键技术&#xff0c;4G的多址技术叫做OFDMA&#xff0c;也就是说4G是通过OFDM来作用户区分的&#xff0c;具体是什么意思呢&#xff1f;继续往下看。 图1 在2G和3G时代&#xff0c; 单用户都是用的一个载波…

进口原装二手 Keysight86142B 是德86142A 高性能光谱分析仪

进口原装二手 Keysight86142B 是德86142A 高性能光谱分析仪 内置测试应用程序 • 10 pm 波长精度 • 快速双扫法 • 覆盖 S、C 和 L 波段 Keysight 86142B是一款台式光谱分析仪&#xff08;OSA&#xff09;&#xff0c;最适于对功率和波长精度、动态范围和低偏振敏感性都要…

深入理解Linux中TCP/IP协议栈的实现原理与具体过程

一、Linux内核与网络体系结构 在我们了解整个linux系统的网络体系结构之前&#xff0c;我们需要对整个网络体系调用&#xff0c;初始化和交互的位置&#xff0c;同时也是Linux操作系统中最为关键的一部分代码-------内核&#xff0c;有一个初步的认知。 1、Linux内核的结构 …

ansible离线部署etcd二进制集群

目录 概述资源安装执行过程集群验证 概述 功能如下&#xff1a; ansible 2.9版本离线安装centos 7 内核离线升级cfssl 离线二进制安装etcd 3.5.13版本 二进制离线安装 资源 相关前置资源如下 资源地址Ansible离线安装地址ansible-playbook离线升级centos内核地址ansible离线…

基于springboot实现社区医院管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现社区医院管理系统演示 摘要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&…