自适应键盘,自带隐藏键盘的输入框(UITextField)

引言

在iOS开发中,输入框占据着举足轻重的地位。与安卓不同,iOS输入框经常面临键盘遮挡的问题,或者无法方便地取消键盘。为了解决这些问题,有许多针对iOS键盘管理的库,如IQKeyboardManager、TPKeyboardAvoiding和KeyboardManager等等。

然而,一些库可能对整个项目的侵入性较大,可能会影响到其他功能。有时,我们可能不希望某些输入框被这些库管理,虽然它们通常也提供了相应的解决方案,但有时会显得有些繁琐。

因此,我们可以考虑自己实现一个输入框,根据项目需求定制输入框的功能。这样做不仅轻量级,而且更加灵活。

实现

本篇博客将通过继承的方式,分别介绍如何自定义实现UITextFiled和UITextView。即使你的项目已经存在一段时间,也可以采用"黑魔法"的方式来实现这些功能。

我们首先明确两个要解决的问题:第一个是解决键盘遮挡输入框的问题,第二个是管理键盘的显示和隐藏。

UITextField

首先继承UITextField创建一个名为LATextField的类,然后通过重写它的init方法来处理上面要解决的两个问题。

解决键盘遮挡

为它添加一个属性,该属性是指当键盘出现时,需要跟随键盘上移的视图,我们可以通过遍历父图层的方式自动获取,也可以使用主动赋值的方式。

但属性一定要使用weak来修饰(子视图不能持有它的父视图)。

代码如下:

class LATextField: UITextField {/// 滑动的视图weak var sliderView:UIView?override init(frame: CGRect) {super.init(frame: frame)}required init?(coder: NSCoder) {fatalError("init(coder:) has not been implemented")}
}

在init方法中添加关于键盘出现和消失的监听,代码如下:

    override init(frame: CGRect) {super.init(frame: frame)addNotification()}fileprivate func addNotification() {// 监听键盘的弹出和收起NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)}

键盘弹出后,我们可以通过通知的userinfo来获取键盘出现的动画时长,以及键盘的frame。

动画时长对应的key为UIResponder.keyboardAnimationDurationUserInfoKey

键盘frame对一个的key为UIResponder.keyboardFrameEndUserInfoKey

代码如下:

    @objc fileprivate func keyboardWillShow(notification: Notification) {let userInfo = notification.userInfolet duration = userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as! TimeIntervallet keyboardFrame = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as! CGRectlet keyboardHeight = keyboardFrame.size.height}

当我们获取到了键盘的frame就可以判断输入框是否被键盘遮挡。而键盘出现的动画时长可以让我们的上移动画更平滑,像是跟随键盘进行上移和下移,具体代码如下:

        if !self.isFirstResponder {return}guard let sliderView = sliderView else { return }// 输入框相对于屏幕的位置let textfiledFrame = self.convert(self.bounds, to: UIApplication.shared.keyWindow)// 输入框的底部位置let textfiledBottom = textfiledFrame.origin.y + textfiledFrame.size.heightif textfiledBottom > keyboardFrame.origin.y {let offsetY = textfiledBottom - keyboardFrame.origin.yUIView.animate(withDuration: duration) {sliderView.transform = CGAffineTransform(translationX: 0, y: -offsetY)}}

这里面有两个需要注意的地方:

  1. 首先需要判断该输入框是否是第一响应者,如果不是第一响应者那么就不需要处理它。
  2. 第二我们采用了transform进行移动,而不是直接修改y值,防止移动被累加,也方便还原操作。

当键盘隐藏时,我们只需要设置sliderView的transform为.identity即可,代码如下:

    @objc fileprivate func keyboardWillHide(notification: Notification) {let userInfo = notification.userInfolet duration = userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as! TimeInterval
//        if !self.isFirstResponder {
//            return
//        }guard let sliderView = sliderView else { return }UIView.animate(withDuration: duration) {sliderView.transform = .identity}}

这里面就不需要进行判断该键盘是否是第一响应者了,因为键盘消失,那么该输入框明显已经不是第一响应者,如果此处添加了判断会影响页面还原。

解决键盘隐藏

这也是一个场景的问题,我们通常会每个输入框都单独处理,比如通过重写它的回车按钮,或者在页面添加手势点击后隐藏键盘。

在这里我们借助它的一个inputAccessoryView属性,在inputAccessoryView添加一个down按钮,点击后调用resignFirstResponder方法取消输入框的第一响应者身份。

代码如下:

    // 添加默认的输入框附加视图fileprivate func addInputAccessoryView() {let inputAccessortyView = UIView(frame: CGRect(x: 0, y: 0, width: SCREENWIDTH, height: 40))inputAccessortyView.backgroundColor = .clearlet arrowButton = LADownButton()arrowButton.frame = CGRect(x: SCREENWIDTH - 40, y: 8, width: 30, height: 30)arrowButton.layer.cornerRadius = 15arrowButton.layer.masksToBounds = truearrowButton.backgroundColor = .black.withAlphaComponent(0.1)inputAccessortyView.addSubview(arrowButton)arrowButton.addTarget(self, action: #selector(arrowButtonAction), for: .touchUpInside)self.inputAccessoryView = inputAccessortyView}@objc fileprivate func arrowButtonAction() {self.resignFirstResponder()}

其中LADownButton按钮是一个我们自定义的按钮,不过它里面并没有什么实际的东西只是绘制了一个向下的箭头,你可以使用文案代替,也可以使用图片代替。

借助了UIBezierPath来进行绘制,它的代码如下:

class LADownButton: UIButton {override func draw(_ rect: CGRect) {// 绘制向下的三角let path = UIBezierPath()path.move(to: CGPoint(x: rect.width * 0.2, y: rect.height * 0.4))path.addLine(to: CGPoint(x: rect.width / 2, y: rect.height * 0.7))path.addLine(to: CGPoint(x: rect.width * 0.8, y: rect.height * 0.4))UIColor.white.setStroke()path.lineWidth = 2.0path.lineJoinStyle = .roundpath.stroke()}}

整个输入框效果如下:

结语

在本篇博客中,我们演示了简单的实现方式,已经能够满足大部分需求。进一步优化的话,我们可以定制输入框与键盘之间的距离,还可以自动获取需要上移的视图等功能,这些都可以根据具体需求进行定制和扩展。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分库分表 [ 技术 ] 指南 ✨

目录 ✨探索 技术 分库分表✨ 数据库分库分表指南 一、什么是分库分表 二、分库分表策略 三、分库分表的实现 四、分库分表的常见问题及解决方案 五、总结 相关面试题目 1. 解释什么是数据库分库分表,以及它的主要目的是什么? 2. 描述垂直分库…

STM32学习(1)--STM32介绍

STM32介绍 1.STM32简介2.ARM3.STM32F103C8T64.外设5.命名规则6.系统结构7.引脚定义8.启动配置9.最小系统电路10.新建工程步骤11.工程架构 课程知识及代码来源均来自b站 江协科技,下学期即将做毕设,需要用到STM32单片机,在这个假期我将持续学…

摸鱼大数据——Spark Structured Steaming——新零售数据分析案例

1、数据源介绍 数据内容 字段说明 2、分析需求 数据清洗需求 清洗需求:1) 将客户id(CustomerID)不为0的数据保留下来: CustomerID ! 02) 将商品描述(Description)不为空的数据保留下来: Description !3) 将日期&#…

gds-linkqueue:泛型链式队列

类似于C的queue的泛型容器,初始化、销毁、清空、入队、出队、取队首/尾、队空。 ​​​​​​​ ​​​​​​​

对某次应急响应中webshell的分析

文章前言 在之前处理一起应急事件时发现攻击者在WEB应用目录下上传了webshell,但是webshell似乎使用了某种加密混淆手法,无法直观的看到其中的木马连接密码,而客户非要让我们连接webshell来证实此文件为后门文件且可执行和利用(也是很恼火&a…

fMATLAB中fill函数填充不同区域

只需获取填充区域的边缘信息,函数边缘越详细越好,然后调用fill函数。 fill函数能够根据指定的顶点坐标和填充颜色来绘制多边形或曲线形状,并在其内部填充指定的颜色。这使得在MATLAB中创建具有视觉吸引力的图形变得简单而高效。 fill函数的…

《0基础》学习Python——第二十讲__网路爬虫/<3>

一、用post请求爬取网页 同样与上一节课的get强求的内容差不多,即将requests.get(url,headershead)代码更换成requests.post(url,headershead),其余的即打印获取的内容,如果content-typejson类型的,打印上述代码的请求,则用一个命…

笔记:现代卷积神经网络之VGG

本文为李沐老师《动手学深度学习》笔记小结,用于个人复习并记录学习历程,适用于初学者 神经网络架构设计的模块化 然AlexNet证明深层神经网络卓有成效,但它没有提供一个通用的模板来指导后续的研究人员设计新的网络。 在下面的几个章节中&a…

【Vue】`v-if` 指令详解:条件渲染的高效实现

文章目录 一、v-if 指令概述二、v-if 的基本用法1. 基本用法2. 使用 v-else3. 使用 v-else-if 三、v-if 指令的高级用法1. 与 v-for 一起使用2. v-if 的性能优化 四、v-if 的常见应用场景1. 表单验证2. 弹窗控制 五、v-if 指令的注意事项 Vue.js 是一个用于构建用户界面的渐进式…

Flink调优详解:案例解析(第42天)

系列文章目录 一、Flink-任务参数配置 二、Flink-SQL调优 三、阿里云Flink调优 文章目录 系列文章目录前言一、Flink-任务参数配置1.1 运行时参数1.2 优化器参数1.3 表参数 二、Flink-SQL调优2.1 mini-batch聚合2.2 两阶段聚合2.3 分桶2.4 filter去重(了解&#xf…

【中项】系统集成项目管理工程师-第3章 信息技术服务-3.4服务标准化

前言:系统集成项目管理工程师专业,现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试,全称为“全国计算机与软件专业技术资格(水平)考试”&…

持续集成02--Linux环境更新/安装Java新版本

前言 在持续集成/持续部署(CI/CD)的旅程中,确保开发环境的一致性至关重要。本篇“持续集成02--Linux环境更新/安装Java新版本”将聚焦于如何在Linux环境下高效地更新或安装Java新版本。Java作为广泛应用的编程语言,其版本的更新对…

XLua原理(一)

项目中活动都是用xlua开发的,项目周更热修也是用xlua的hotfix特性来做的。现研究底层原理,对于项目性能有个更好的把控。 本文认为看到该文章的人已具备使用xlua开发的能力,只研究介绍下xlua的底层实现原理。 一.lua和c#交互原理 概括&…

用程序画出三角形图案

创建各类三角形图案 直角三角形&#xff08;左下角&#xff09; #include <iostream> using namespace std;int main() {int rows;cout << "输入行数: ";cin >> rows;for(int i 1; i < rows; i){for(int j 1; j < i; j){cout << &…

003uboot目录分析和两个阶段

我们都知道s3c2440是一个soc&#xff0c;内含cpu和各种控制器、片内的RAM&#xff0c;他的CPU是arm920t。 我们先来分析一下uboot原码的各个目录 1.uboot目录分析 board&#xff1a;board里存放的是支持各个开发板的文件&#xff0c;包括链接脚本 common: common目录中存放的…

graham 算法计算平面投影点集的凸包

文章目录 向量的内积&#xff08;点乘&#xff09;、外积&#xff08;叉乘&#xff09;确定旋转方向numpy 的 cross 和 outernp.inner 向量与矩阵计算示例np.outer 向量与矩阵计算示例 python 示例生成样例散点数据图显示按极角排序的结果根据排序点计算向量转向并连成凸包 基本…

set、map、multiset、multimap容器介绍和常用接口使用

文章目录 前言一、set容器二、multiset三、map四、multimap 前言 1、set、map、 multiset、 multimap都是基于红黑树实现的容器。 2、set、multiset都使用头文件#include<set>,map、multimap都是使用头文件#include<map> 一、set容器 1、set容器的介绍 C标准库中的…

pytest常用命令行参数解析

简介&#xff1a;pytest作为一个成熟的测试框架&#xff0c;它提供了许多命令行参数来控制测试的运行方式&#xff0c;以配合适用于不同的测试场景。例如 -x 可以用于希望出现错误就停止&#xff0c;以便定位和分析问题。–rerunsnum适用于希望进行失败重跑等个性化测试策略。 …

【BUG】已解决:AttributeError: ‘str‘ object has no attribute ‘get‘

已解决&#xff1a;AttributeError: ‘str‘ object has no attribute ‘get‘ 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c…

C++初学者指南-5.标准库(第一部分)--标准库查找算法

C初学者指南-5.标准库(第一部分)–标准库查找算法 文章目录 C初学者指南-5.标准库(第一部分)--标准库查找算法查找/定位一个元素findfind_iffind_if_notfind_last / find_last_if / find_last_if_notfind_first_of 查找范围内的子范围 search find_endstarts_withends_with 找到…