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

引言

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

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

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

实现

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

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

UITextField

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

解决键盘遮挡

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

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

代码如下:

Swift 复制代码
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方法中添加关于键盘出现和消失的监听,代码如下:

Swift 复制代码
    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

代码如下:

Swift 复制代码
    @objc fileprivate func keyboardWillShow(notification: Notification) {
        let userInfo = notification.userInfo
        let duration = userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as! TimeInterval
        let keyboardFrame = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
        let keyboardHeight = keyboardFrame.size.height

    }

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

Swift 复制代码
        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.height
        if textfiledBottom > keyboardFrame.origin.y {
            let offsetY = textfiledBottom - keyboardFrame.origin.y
            UIView.animate(withDuration: duration) {
                sliderView.transform = CGAffineTransform(translationX: 0, y: -offsetY)
            }
        }

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

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

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

Swift 复制代码
    @objc fileprivate func keyboardWillHide(notification: Notification) {
        let userInfo = notification.userInfo
        let 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方法取消输入框的第一响应者身份。

代码如下:

Swift 复制代码
    // 添加默认的输入框附加视图
    fileprivate func addInputAccessoryView() {
        let inputAccessortyView = UIView(frame: CGRect(x: 0, y: 0, width: SCREENWIDTH, height: 40))
        inputAccessortyView.backgroundColor = .clear
        let arrowButton = LADownButton()
        arrowButton.frame = CGRect(x: SCREENWIDTH - 40, y: 8, width: 30, height: 30)
        arrowButton.layer.cornerRadius = 15
        arrowButton.layer.masksToBounds = true
        arrowButton.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来进行绘制,它的代码如下:

Swift 复制代码
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.0
        path.lineJoinStyle = .round
        path.stroke()
    }

}

整个输入框效果如下:

结语

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

相关推荐
我不爱编程1 个月前
Mac 在vscode 中的常用快捷键
macos·mac·键盘
方永锐1 个月前
切换笔记本键盘的启用与禁用状态
windows·笔记本电脑·键盘·批处理
风翼靓崽1 个月前
记一次键盘f2和f5键被自动触发情况
计算机外设·键盘
前端开发菜鸟的自我修养2 个月前
【java入门】关键字、标识符与变量初识
java·开发语言·后端·键盘·合成复用原则
文件销毁硬盘销毁海龙2 个月前
《安全回收电脑,从销毁硬盘数据开始》文件硬盘数据销毁
运维·安全·新媒体运营·旅游·键盘
软件架构师-叶秋2 个月前
qmk开源键盘 rgb_matrix_indicators_advanced_user函数
github·键盘·开源项目·qmk键盘
Jurio.3 个月前
Mac 自定义键盘快捷键打开系统应用
macos·计算机外设·键盘
码界领航4 个月前
罗技K380无线键盘及鼠标:智慧互联,一触即通
计算机外设·键盘·罗技·k380
Casia_Dominic4 个月前
【机械键盘调整灯光】腹灵MK870说明书(个人备用)
linux·运维·ubuntu·计算机外设·github·键盘·机械键盘