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

iOS如何自定义一个类似UITextView的本文编辑View

对于IOS涉及文本输入常用的两个View是UITextView和UITextField,一个用于复杂文本输入,一个用于简单文本输入,在大多数开发中涉及文本输入的场景使用这两个View能够满足需求。但是对于富文本编辑相关的开发,这两个View就无法满足自己业务的需要了。

在多数情况下,设计富文本开发的业务,大多需要根据键盘输入的内容根据业务场景对文本进行布局排版,这方面有个开源的富文本编辑View那就是YYText,网上也有很多对其源码分析的文章,这里就不过多介绍了,这个自定义的YYTextView堪称大神级,性能和效果都很不错,代码质量相当高,可以细细研读。但是这个代码相对比较复杂,对于研究如何自定义文本编辑View不太友好。然而苹果官方提供了一个简单版自定义文本编辑View的开源项目SimpleTextInput,这个简单易理解,下面就基于此说下如何自定义一个文本编辑的view

对于文本编辑而言,设计两个关键点:1、和键盘交互,当点击键盘上的按键时屏幕上如何显示;2、内容排版,屏幕上的内容如何布局显示。

对于第一点,iOS提供了两个相应协议:1、UIKeyInput简单的键盘交互

2、UITextInput复杂的键盘交互,可以看出UITextInput继承UIKeyInput,因此包含了UIKeyInput所有接口

UIKeyInput只能输入字符,因此提供的接口也简单,只有是否有文本内容,插入、删除三个接口

UITextInput用于输入点击键盘时屏幕不是显示最终内容时,比如输入汉字时,在输入拼音时,屏幕显示的是拼音字符有个蓝色底色,只有最终选中汉字时会将蓝色底色包围的拼音替换为汉字

对比只继承UIKeyInput的view继承UITextInput的view键盘多了红框选字的部分。

那么如何使用这两个协议呢,很简单,基于UIView创建一个自定义View子类,继承UIKeyInput或者UITextInput

@interface APLEditableCoreTextView : UIView <UITextInput> @end

为了使自定义的view能够接受输入需要重载方法canBecomeFirstResponder返回YES这样自定义view才能相应输入

如果是继承UIKeyInput就只需实现

@implementation APLEditableCoreTextView- (BOOL)canBecomeFirstResponder
{return YES;
}- (BOOL)hasText
{return (self.text.length != 0);
}- (void)insertText:(NSString *)text
{}- (void)deleteBackward
{}@end

这三个接口就可以了,当点自定义的view处于第一响应时,系统会触发hasText先判断是否有内容,当点击键盘字母时会调insertText:(NSString*)text方法,当点击键盘delete键会触发deleteBackward方法

如果是继承UITextInput需要实现的就比较多了,这个协议的@required下所有方法都必须实现再加上UIKeyInput的三个方法

/* Methods for manipulating text. */
// 根据给定的范围返回一个字符串,一般是屏幕上已有的内容取range范围内的子字符串
- (nullable NSString *)textInRange:(UITextRange *)range;
// 用给定的字符串替换给定范围部分,一般是屏幕上已有内容range范围内替换为text
- (void)replaceRange:(UITextRange *)range withText:(NSString *)text;/* Text may have a selection, either zero-length (a caret) or ranged.  Editing operations are* always performed on the text from this selection.  nil corresponds to no selection. */// 已选的范围
@property (nullable, readwrite, copy) UITextRange *selectedTextRange;/* If text can be selected, it can be marked. Marked text represents provisionally* inserted text that has yet to be confirmed by the user.  It requires unique visual* treatment in its display.  If there is any marked text, the selection, whether a* caret or an extended range, always resides within.** Setting marked text either replaces the existing marked text or, if none is present,* inserts it from the current selection. */ // 标记的文本范围,如果没有标记文本返回nil,标记文本可以理解为输入内容时,屏幕上蓝色包裹的字符串
@property (nullable, nonatomic, readonly) UITextRange *markedTextRange; // Nil if no marked text.
// 标记文本如何显示
@property (nullable, nonatomic, copy) NSDictionary<NSAttributedStringKey, id> *markedTextStyle; // Describes how the marked text should be drawn.
// 用给定范围设置标记文本内容
- (void)setMarkedText:(nullable NSString *)markedText selectedRange:(NSRange)selectedRange; // selectedRange is a range within the markedText
// 解除标记文本,可以理解为用键盘内容替换蓝色包裹部分,比如选中键盘上汉字替换为屏幕上拼音时,拼音就是要解除的标记文本
- (void)unmarkText;/* The end and beginning of the the text document. */
// 文本开始位置,一般是0
@property (nonatomic, readonly) UITextPosition *beginningOfDocument;
// 文本结束位置,一般是屏幕文本长度
@property (nonatomic, readonly) UITextPosition *endOfDocument;/* Methods for creating ranges and positions. */
// 用给定的开始和结束位置,返回一个range
- (nullable UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition;
// 用给定位置和偏移量返回一个新的位置
- (nullable UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset;
// 用给定的位置、方向和偏移量返回一个新的位置
- (nullable UITextPosition *)positionFromPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction offset:(NSInteger)offset;/* Simple evaluation of positions */
// 比较两个位置关系
- (NSComparisonResult)comparePosition:(UITextPosition *)position toPosition:(UITextPosition *)other;
// 返回给定起始位置和结束位置的偏移量
- (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition;/* A system-provided input delegate is assigned when the system is interested in input changes. */
// 输入代理,这个代理不需要自己继承UITextInputDelegate协议定义一个类,系统会给其分配一个实例的
@property (nullable, nonatomic, weak) id <UITextInputDelegate> inputDelegate;/* A tokenizer must be provided to inform the text input system about text units of varying granularity. */
// 分词器,需要给其创建,如果没有特殊需求直接使用UITextInputStringTokenizer就可以了
@property (nonatomic, readonly) id <UITextInputTokenizer> tokenizer;/* Layout questions. */
// 根据给定的布局范围和方向返回一个最远的新位置
- (nullable UITextPosition *)positionWithinRange:(UITextRange *)range farthestInDirection:(UITextLayoutDirection)direction;
// 根据给定的位置和方向返回一个字符的范围
- (nullable UITextRange *)characterRangeByExtendingPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction;/* Writing direction */
// 根据给的位置和记录方向返回书写方向
- (NSWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction;
// 设置指定范围内的指定的书写方向
- (void)setBaseWritingDirection:(NSWritingDirection)writingDirection forRange:(UITextRange *)range;/* Geometry used to provide, for example, a correction rect. */
// 返回给定范围内的rect
- (CGRect)firstRectForRange:(UITextRange *)range;
// 返回给定位置的光标rect,一般输入框都会有个闪烁的竖线,这个竖线其实是个矩形,为了能让这个竖线在文本间显示且不与文字重叠,需要给字与字之间留够空隙,这个空隙是文字布局时做到,但是空隙大小是通过这个方法得到的
- (CGRect)caretRectForPosition:(UITextPosition *)position;
// 返回给定范围内的选区,这是个数组,因为选区内可能有个多个rect
- (NSArray<UITextSelectionRect *> *)selectionRectsForRange:(UITextRange *)range API_AVAILABLE(ios(6.0));       // Returns an array of UITextSelectionRects/* Hit testing. */
// 返回给定点的最近位置
- (nullable UITextPosition *)closestPositionToPoint:(CGPoint)point;
// 返回给定范围内指定点的最近位置
- (nullable UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange *)range;
// 返回给定点出的文本范围,一般是用于分词,比如在“今天天气不错”这个内容的“气”字位置处可能想要的的是“天气”这个词的范围
- (nullable UITextRange *)characterRangeAtPoint:(CGPoint)point;

这些方法的触发都是在与键盘交互时相应的。其中UITextInput有个属性inputDelegate,这个不需要设置,由系统设置分配的UIKeyboardImpl实例

通过调用堆栈可以看出,当设置view为第一响应者时,会给inputDelegate设置一UIKeyboardImpl的实例

其中tokenizer属性需要给其分配实例,self.tokenizer = [[UITextInputStringTokenizer alloc] initWithTextInput:self];用户给键盘使用,实际view中并不怎么用这个属性

以上就是关于和键盘交互的所有说明,记住UIKeyInput/UITextInput的接口都是有键盘触发的,可以在这些接口实现断点调试看下堆栈,比如hasText,当点击键盘时会立即触发

再比如markedTextRange

等等,通过堆栈发现,执行UITextInput的接口时,都是通过UIKeyboardImpl这个实例最终执行到UITextInput的接口实现,这个UIKeyboardImpl就是inputDelegate的实例,这个UITextInputDelegate协议有四个接口

我们在selectedTextRange方法处断点调试,通过堆栈可以看出,这个方法就是inputDelegate执行selectionDidChange最终执行了selectedTextRange

最后我们看下UITextInput中涉及到的两个类UITextPosition和UITextRange

@interface UITextPosition : NSObject@end@interface UITextRange : NSObject@property (nonatomic, readonly, getter=isEmpty) BOOL empty;     //  Whether the range is zero-length.
@property (nonatomic, readonly) UITextPosition *start;
@property (nonatomic, readonly) UITextPosition *end;@end

通过定义可以看出,UITextPosition是个空类,没有任何变量和属性,通过类名可以知道这是个定义位置的类,但是并没有变量和属性如何知道位置的值呢。同时UITextRange是一个选区类,有个属性empty显然是为了说明选区是否为空,但是并没有实现,这两个类都只给了定义并没有实现。因此需要开发者继承这两个类自定义UITextPosition和UITextRange。

@interface APLIndexedPosition : UITextPosition@property (nonatomic) NSUInteger index;
@property (nonatomic) id <UITextInputDelegate> inputDelegate;+ (instancetype)positionWithIndex:(NSUInteger)index;@end@implementation APLIndexedPosition#pragma mark IndexedPosition implementation// Class method to create an instance with a given integer index.
+ (instancetype)positionWithIndex:(NSUInteger)index
{APLIndexedPosition *indexedPosition = [[self alloc] init];indexedPosition.index = index;return indexedPosition;
}@end
@interface APLIndexedRange : UITextRange@property (nonatomic) NSRange range;
+ (instancetype)indexedRangeWithRange:(NSRange)range;@end@implementation APLIndexedRange// Class method to create an instance with a given range
+ (instancetype)indexedRangeWithRange:(NSRange)range
{if (range.location == NSNotFound) {return nil;}APLIndexedRange *indexedRange = [[self alloc] init];indexedRange.range = range;return indexedRange;
}// UITextRange read-only property - returns start index of range.
- (UITextPosition *)start
{return [APLIndexedPosition positionWithIndex:self.range.location];
}// UITextRange read-only property - returns end index of range.
- (UITextPosition *)end
{return [APLIndexedPosition positionWithIndex:(self.range.location + self.range.length)];
}// UITextRange read-only property - returns YES if range is zero length.
-(BOOL)isEmpty
{return (self.range.length == 0);
}

这就是在selectedTextRange方法中返回的是APLIndexedRange的原因

以上是如何键盘交互,对于已经在屏幕上的内容,如何布局排版显示,涉及文字绘制,可以参考官方文档Core Text这是一个纯C语言的较为底层的文本排版渲染的框架,其中UITextInput中- (CGRect)caretRectForPosition:(UITextPosition *)position接口就涉及core text相关方法来计算字间距,这里就不对此进行详细阐述了,网上对core text详解有很多,而对UITextInput的使用较少,这里对此做详细说明

相关文章:

iOS如何自定义一个类似UITextView的本文编辑View

对于IOS涉及文本输入常用的两个View是UITextView和UITextField&#xff0c;一个用于复杂文本输入&#xff0c;一个用于简单文本输入&#xff0c;在大多数开发中涉及文本输入的场景使用这两个View能够满足需求。但是对于富文本编辑相关的开发&#xff0c;这两个View就无法满足自…...

JavaWeb文件上传

文件上传总览 文件上传主要是指将本地文件&#xff08;包括但不限于图片、视频、音频等&#xff09;上传到服务器&#xff0c;提供其他用户浏览或下载的过程。在日常生活中&#xff0c;我们在很多情况下都需要使用文件上传功能&#xff0c;比如&#xff1a;发微博、发朋友圈等…...

C#实现1ms定时器不精准?如何实现一个高性能高精度的1ms定时器?(附完整示例Demo)

在C#日常开发中&#xff0c;我们经常需要使用定时器&#xff08;Timer&#xff09;进行周期性任务的执行。 例如&#xff0c;每隔1秒打印一条日志&#xff0c;或每隔100毫秒执行某个数据刷新逻辑。 但是&#xff0c;当我们尝试在C#中实现一个1毫秒&#xff08;1ms&#xff09;…...

LeetCode 3. 无重复字符的最长子串

题目链接&#xff1a;3. 无重复字符的最长子串 首先想到的就是暴力破解&#xff0c;直接两层循环遍历&#xff0c;因为它说求无重复&#xff0c;那就可以用 set 来存储遍历到的字符&#xff0c;如果遍历到了同样的字符(在 set 中存在)&#xff0c;就直接跳出第二层循环&#x…...

深度解析 Ansible:核心组件、配置、Playbook 全流程与 YAML 奥秘(上)

文章目录 一、ansible的主要组成部分二、安装三、相关文件四、ansible配置文件五、ansible 系列 一、ansible的主要组成部分 ansible playbook&#xff1a;任务剧本&#xff08;任务集&#xff09;&#xff0c;编排定义ansible任务集的配置文件&#xff0c;由ansible顺序依次执…...

记一次由docker容器使得服务器cpu占满密码和密钥无法访问bug

Bug场景&#xff1a; 前几天在服务器上部署了一个免费影视网站&#xff0c;这个应用需要四个容器&#xff0c;同时之前的建站软件workpress也是使用docker部署的&#xff0c;也使用了三个容器。在使用workpress之前&#xff0c;我将影视软件的容器全部停止。 再使用workpress…...

功能篇:JAVA实现记住我功能

在Java Web应用程序中实现“记住我”功能&#xff0c;通常涉及以下几个步骤&#xff1a; 1. 创建一个持久化的标识符&#xff08;如一个令牌或哈希值&#xff09;&#xff0c;并将其与用户账户关联。 2. 将这个标识符保存到客户端的cookie中。 3. 在服务器端&#xff0c;当用户…...

实现 DataGridView 下拉列表功能(C# WinForms)

本文介绍如何在 WinForms 中使用 DataGridViewComboBoxColumn 实现下拉列表功能&#xff0c;并通过事件响应来处理用户的选择。以下是实现步骤和示例代码。 1. 效果展示 该程序的主要功能是展示如何在 DataGridView 中插入下拉列表&#xff0c;并在选择某一项时触发事件。 2.…...

2024年上半年网络工程师综合知识真题及答案解析

2024年上半年网络工程师综合知识真题及答案解析 以下不属于5G网络优点的是()A.传输过程中消耗的资源少,对设备的电池更友好B.支持大规模物联网,能够连接大量低功耗设备,提供更高效的管理C.引入了网络切片技术,允许将物理网络划分为多个虚拟网络D.更好的安全性,采用更…...

数合平台功能-管理角色

前一阵&#xff0c;有朋友问到&#xff0c;看咱们产品的功能描述很强大&#xff0c;但很多功能看不到。为此&#xff0c;基于数据建模产品最新版本&#xff0c;和大家一起串一下产品的功能和使用路径。本节重点说一下管理角色有哪些功能 一、功能清单 从上图中可以看到&#x…...

LVGL9 开关控件 (lv_switch) 使用指南

文章目录 前言主体1. **控件概述**2. **控件的样式和组成部分**3. **使用控件**改变开关状态 4. **事件处理**5. **按键支持**6. **示例代码** 总结 前言 lv_switch 是 LittlevGL 提供的一个开关控件&#xff0c;外观类似一个小型滑块&#xff0c;常用于实现开关功能&#xff…...

麒麟 V10 系统(arm64/aarch64)离线安装 docker 和 docker-compose

前期准备 查看操作系统版本&#xff0c;跟本文标题核对一下 uname -a查看操作系统架构 uname -m下载离线包 下载 docker 离线包 地址&#xff1a;https://download.docker.com/linux/static/stable/ 选择系统架构对应的文件目录&#xff1a;aarch64&#xff0c;我目前使用…...

独立ip服务器有什么优点?

网站的性能和安全性直接影响到用户体验和业务发&#xff0c;独立IP服务器作为一种主流的托管方式&#xff0c;因其独特的优势而受到许多企业和个人站长的青睐。与共享IP相比&#xff0c;独立IP服务器到底有哪些优点呢&#xff1f; 使用独立IP的用户不必担心与其他网站共享同一…...

oracle之用户的相关操作

&#xff08;1&#xff09;创建用户(sys用户下操作) 简单创建用户如下&#xff1a; CREATE USER username IDENTIFIED BY password; 如果需要自定义更多的信息&#xff0c;如用户使用的表空间等&#xff0c;可以使用如下&#xff1a; CREATE USER mall IDENTIFIED BY 12345…...

深入浅出:PHP中的数组操作全解析

文章目录 引言理解数组创建数组使用方括号使用array()函数 访问数组元素数值索引数组关联数组 遍历数组使用for循环使用foreach循环 添加和修改数组元素添加元素修改元素 删除数组元素删除单个元素删除整个数组 多维数组创建多维数组访问多维数组元素 常用数组函数获取数组长度…...

2024年12月7日历史上的今天大事件早读

1732年12月07日英国皇家大剧院在伦敦开幕 1798年12月07日清代诗人袁枚逝世 1889年12月07日第一个充气轮胎受专利保护 1916年12月07日劳合-乔治出任英国首相 1926年12月07日第一台电冰箱受美国专利保护 1937年12月07日南京保卫战正式打响 1941年12月07日日本偷袭珍珠港 1…...

pymysql模块详解

华子目录 简介安装pymysql连接对象常用方法游标对象常用方法数据库操作查改批量增加删 使用with语句总结 简介 pymysql是一个用于Python编程的第三方模块&#xff0c;用于连接和操作MySQL数据库。它提供了一个简单而强大的接口&#xff0c;使开发者能够轻松地在Python程序中执…...

计算机网络研究实训室建设方案

一、概述 本方案旨在规划并实施一个先进的计算机网络研究实训室&#xff0c;旨在为学生提供一个深入学习、实践和研究网络技术的平台。实训室将集教学、实验、研究于一体&#xff0c;覆盖网络基础、网络架构、网络安全、网络管理等多个领域&#xff0c;以培养具备扎实理论基础…...

WEB安全 PHP学习

PHP基础 PHP编码显示问题 header ("Content-type: text/html; charsetgb2312"); header("Content-Type: text/html;charsetutf-8"); windows需要使用gbk编码显示 源码是 <?php header ("Content-type: text/html; charsetgb2312"); sys…...

Redis高阶之容错切换

当一台主机master宕掉之后&#xff0c;他的从机会取代主机么&#xff1f; 查看集群状态 127.0.0.1:6385> cluster nodes c8ff33e8da5fd8ef821c65974dda304d2e3327f9 192.168.58.129:638216382 slave f6b1fd5e58df90782f602b484c2011d52fc3482d 0 1733220836918 1 connecte…...

构建高效OTA旅游平台的技术指南

1. 引言 在信息技术高速发展的今天&#xff0c;互联网深刻地改变了人们的旅行方式。传统的旅行社模式逐渐被在线旅游平台所取代&#xff0c;OTA&#xff08;Online Travel Agency&#xff0c;在线旅行社&#xff09;旅游平台应运而生&#xff0c;成为人们获取旅游信息、预订旅…...

数据结构 (25)图的存储结构

前言 数据结构中的图是一种用于表示多对多关系的结构&#xff0c;其存储结构主要有两种&#xff1a;邻接矩阵和邻接表。 一、邻接矩阵 定义&#xff1a;邻接矩阵是一个二维数组&#xff0c;用于存储图中各个顶点之间的关系。数组的行和列分别代表图中的顶点&#xff0c;元素的值…...

【C语言】fscanf 和 fprintf函数

【C语言】fscanf 和 fprintf函数 文章目录 [TOC](文章目录) 前言一、定义二、代码例程三、实验结果四、参考文献总结 前言 使用工具&#xff1a; 1.编译器&#xff1a;DEVC 2.C Primer Plus 第六版-1 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一…...

C#调用c++创建的动态链接库dll文件

在C#中调用外部DLL文件是一种常见的编程实践&#xff0c;它具有以下几个重要意义&#xff1a;1.代码重用&#xff1b;2.模块化&#xff1b;3.性能优化&#xff1b;4.安全性&#xff1b;5.跨平台兼容性&#xff1b;6.方便更新和维护&#xff1b;7.利用特定技术或框架&#xff1b…...

【数字电路与逻辑设计】实验一 序列检测器

文章总览&#xff1a;YuanDaiMa2048博客文章总览 【数字电路与逻辑设计】实验一 序列检测器 一、实验内容二、设计过程&#xff08;一&#xff09;作出状态图或状态表&#xff08;二&#xff09;状态化简&#xff08;三&#xff09;状态编码 三、源代码&#xff08;一&#xff…...

沈阳工业大学《2024年827自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《沈阳工业大学827自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2024年真题 Part1&#xff1a;2024年完整版真题 2024年真题...

Javascript Clipper library, v6(介绍目录)

1.老祖宗C#版的Clipper2 Clipper2库可以对简单和复杂的多边形执行交集、并并、差分和异或布尔运算。它还执行多边形偏移 github地址&#xff1a;GitHub - AngusJohnson/Clipper2: Polygon Clipping and Offsetting - C, C# and Delphi 2.目前的移植版本 基于C#版的移植版本…...

uniapp+vue3+ts请求接口封装

1.安装luch-request yarn add luch-requestnpm install luch-request2.新建文件src/utils/request.ts 需要自己修改config.baseURL和token&#xff08;获取存储的token&#xff09; // import HttpRequest from luch-request; import type { HttpRequestConfig, HttpRespons…...

​‌Spring Boot中的@GetMapping注解可以用于处理HTTP GET请求,并且可以接收对象参数​,详细示例

下面内容来自Ai回答&#xff0c;经过亲自验证&#xff0c;正确 ‌ Spring Boot中的GetMapping注解可以用于处理HTTP GET请求&#xff0c;并且可以接收对象参数。‌ 接收对象参数的基本方式 在Spring Boot中&#xff0c;可以通过GetMapping注解接收对象参数&#xff0c;这通…...

详解Vue设计模式

详解 vue 设计模式 ​ Vue.js 作为一个流行的前端框架&#xff0c;拥有许多设计模式&#xff0c;这些设计模式帮助开发者更好地组织和管理代码&#xff0c;提升代码的可维护性、可扩展性和可读性。Vue 设计模式主要体现在以下几个方面&#xff1a; 1. 组件化设计模式 (Compon…...

webpack 题目

文章目录 webpack 中 chunkHash 和 contentHash 的区别loader和plugin的区别&#xff1f;webpack 处理 image 是用哪个 loader&#xff0c;限制 image 大小的是...&#xff1b;webpack 如何优化打包速度 webpack 中 chunkHash 和 contentHash 的区别 主要从四方面来讲一下区别&…...

Mysql - 存储引擎

一 MYSQL体系结构简介 MYSQL的体系结构可以分为四个层级&#xff0c;从上往下依次为&#xff1a; 1. 连接层: 最上层为客户端以及一些连接服务&#xff0c;包含连接操作&#xff0c;例如JAVA想要与MYSQL建立连接就需要用到JDBC&#xff0c;PHP语言与Python也可以连接到MYSQL&am…...

【实战教程】使用YOLOv8 OBB进行旋转框目标检测的数据集定义与训练【附源码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...

怎么实现邮件营销自动化?

邮件营销能够出色地帮助我们与客户建立良好关系。无论是新客户还是老客户&#xff0c;都可以通过邮件来达成较为良好的客户关系。然而&#xff0c;从消费者的角度来看&#xff0c;每个人都有自己独特的习惯和特点&#xff0c;没有人希望收到千篇一律、营销意味过重的邮件。因此…...

华为服务器使用U盘重装系统

一、准备工作 下载官方系统&#xff08;注意服务器CPU的架构是x86-64还是aarch64&#xff0c;不然可能报意想不到的错&#xff09;制作启动U盘&#xff08;下载rufus制作工具&#xff0c;注意文件系统选FAT32还是NTFS&#xff09; 二、安装步骤 将U盘插入USB接口重启服务器…...

空安全编程的典范:Java 8中的安全应用指南

文章目录 一、Base64 编码解码1.1 基本的编码和解码1.2 URL 和文件名安全的编码解码器1.3 MIME Base64编码和解码 二、Optional类三、Nashorn JavaScript 一、Base64 编码解码 1.1 基本的编码和解码 Base64 编码&#xff1a; 使用 Base64.getEncoder().encodeToString(origin…...

深入解析 Loss 减少方式:mean和sum的区别及其在大语言模型中的应用 (中英双语)

深入解析 Loss 减少方式&#xff1a;mean 和 sum 的区别及其在大语言模型中的应用 在训练大语言模型&#xff08;Large Language Models, LLM&#xff09;时&#xff0c;损失函数&#xff08;Loss Function&#xff09;的处理方式对模型的性能和优化过程有显著影响。本文以 re…...

opencv4.8 ubuntu20.04源码编译 安装报错记录

-- IPPICV: Downloading ippicv_2021.8_lnx_intel64_20230330_general.tgz from https://raw.githubusercontent.com/opencv/opencv_3rdparty/1224f78da6684df04397ac0f40c961ed37f79ccb/ippicv/ippicv_2021.8_lnx_intel64_20230330_general.tgz make -j8 到这咋不动了 代理配…...

16-03、JVM系列之:内存与垃圾回收篇(三)

JVM系列之&#xff1a;内存与垃圾回收篇(三) ##本篇内容概述&#xff1a; 1、执行引擎 2、StringTable 3、垃圾回收一、执行引擎 ##一、执行引擎概述 如果想让一个java程序运行起来&#xff0c;执行引擎的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。 简…...

在 Spring Boot 中使用 JPA(Java Persistence API)进行数据库操作

步骤 1: 添加依赖 在 pom.xml 文件中添加相关依赖&#xff1a; <dependencies><!-- Spring Boot Starter Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><…...

【sqlserver】mssql 批量加载数据文件 bulk copy使用

参考文章&#xff1a; Using bulk copy with the JDBC driver SqlServer数据批量写入 SqlServer批量插入数据方法–SqlBulkCopy sqlserver buld copy需要提供&#xff0c;数据文件的对应表的元数据信息主要的字段的位置、字段的名称、字段的数据类型。 执行bulk load时候不一…...

卷积神经网络(CNN)的层次结构

卷积神经网络&#xff08;CNN&#xff09;是一种以其处理图像和视频数据的能力而闻名的深度学习模型&#xff0c;其基本结构通常包括以下几个层次&#xff0c;每个层次都有其特定的功能和作用&#xff1a; 1. 输入层&#xff08;Input Layer&#xff09;&#xff1a; 卷积神经网…...

使用Excel的COUNTIFS和SUMIFS函数进行高级数据分析

使用Excel的COUNTIFS和SUMIFS函数进行高级数据分析 引言 在处理数据时&#xff0c;Excel 提供了多种内置函数来帮助用户快速获取所需信息。其中&#xff0c;COUNTIFS 和 SUMIFS 是两个非常强大的多条件聚合函数&#xff0c;它们允许你根据一个或多个标准来统计或汇总数据。本…...

上传ssh公钥到目标服务器

创建密钥 ssh-keygen -t rsa -b 4096 -C "xxxx.xx"上传 sudo ssh-copy-id -i /Users/xx/.ssh/id_rsa.pub root127.0.0.1...

在visio2021 中插入MathType公式

首先要确保有着两个软件&#xff0c;且能用。 1、打开visio2021&#xff0c;之后点击“插入”-“对象” 2、打开后&#xff0c;选择MathType&#xff0c;确定 3、确定后就会弹出MathType编辑器...

【计算机视觉】图像的几何变换

最常见的几何变换有仿射变换和单应性变换两种&#xff0c;最常用的仿射变换有缩放、翻转、旋转、平移。 1. 缩放 将图像放大或缩小会得到新的图像&#xff0c;但是多出的像素点如何实现----插值 1.1 插值方法 最近邻插值 双线性插值 cv2.resize() 是 OpenCV 中用于调整图像…...

IS-IS四

目录 点到点中LSP(类似LSA&#xff09;的同步过程 注意LSP只有&#xff08;1类LSA和2类LSA) 查看详细信息&#xff1a;display isis lsdb 0000.0000.0001.00-00 verbose 开摸&#xff1a; ISIS的伪节点LSP&#xff08;类似LSA&#xff09;没有路由信息 L1路由器的路由计算…...

CODA 离线安装及虚幻镜迁移

1、离线安装 1.1 下载Miniconda安装脚本 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh1.2 添加权限 chmod x Miniconda3-latest-Linux-x86_64.sh1.3 执行安装 ./Miniconda3-latest-Linux-x86_64.sh遇到问题&#xff0c;一路回车即可 1.4 …...

【Rive】混合动画

1 混合动画简介 【Rive】动画 中介绍了 Rive 中动画的基础概念和一般动画的制作流程&#xff0c;本文将介绍混合动画的基础概念和一般制作流程。Unity 中混合动画介绍详见→ 【Unity3D】动画混合。 混合动画是指同一时刻多个动画按照一定比例同时执行&#xff0c;这些动画控制的…...

软件体系结构复习-02 软件体系结构定位及构建

软件体系结构复习-02 软件体系结构定位及构建 原文链接&#xff1a;《软件体系结构复习-02 软件体系结构定位及构建》 目录 软件体系结构复习-02 软件体系结构定位及构建 1 什么是软件体系结构 2 软件生命周期中的软件体系结构 2.1 生命周期 2.2 定位与作用 1 规划和需求…...