方格验证码输入框实现方式

引言

在实际开发过程中验证码输入框是一个很常见UI界面。通常来讲有简单的输入框,也有方格的输入框,其中相对较为棘手就是这种方格输入框里面还需要显示光标的情况。本篇博客我们就来主要讨论一下方格带光标的验证码输入框样式。

实现方案

在着手实现这个功能之前,首先我们需要明确需要做的事情:

  1. 创建出方格,并且每个方格都需要响应用户输入。
  2. 在方格输入一个数字后,需要自动切换到下一个方格响应输入。
  3. 在方格删除一个数字后,需要自动切换到上一个方格响应。
  4. 所有方格都输入完成之后,需要通知页面控制器。

1.创建方格

既然每个方格都需要响应用户的输入(且显示光标),那我们很容易就会想到方格需要有UITextFiled用来响应用户输入和光标。当切换到下一个方格时,上一个方格还需要显示已经输入的内容,这里我们采用一个UILabel来实现。

这样我们自定义一个单元输入框大概结构就有了,代码如下:

class PHInputItemView: UIView, UITextFieldDelegate {
    
    /// 显示label
    private let label = UILabel()
    /// 实际输入框
    private let codeField = PHCodeField()
   
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
        setLayout()
        setEvent()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setupView() {
        self.layer.masksToBounds = true
        self.layer.cornerRadius = 8.0
        self.layer.borderWidth = 1.0
        self.layer.borderColor = UIColor.clear.cgColor
        
        self.addSubview(codeField)
        codeField.font = UIFont.systemFont(ofSize: 30, weight: .medium)
        codeField.textAlignment = .center
        codeField.textColor = .black
        codeField.tintColor = .black
        codeField.backgroundColor = .clear
        
        self.addSubview(label)
        label.font = UIFont.systemFont(ofSize: 30, weight: .medium)
        label.textAlignment = .center
        label.textColor = .black
        label.backgroundColor = .black.withAlphaComponent(0.4)
        
    }
    
    func setLayout() {
        label.frame = self.bounds
        codeField.frame = self.bounds
    }
    
    func setEvent() {
       
    }

    
    override func layoutSubviews() {
        super.layoutSubviews()
        setLayout()
    }
    
}

由于我们需要在用户输入或者删除时进行一些切换的操作,所以这个自定义的单元输入需要将事件回调出去,这里我们采用了闭包的形式进行回调,操作如下:

用户输入

增加输入回调闭包:

    /// 输入回调
    var inputAction: ((String) -> Void)?

设置代理:

        codeField.delegate = self

实现代理方法:

    //MARK: UITextFieldDelegate
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if string.count == 1 {
            self.label.text = string
            self.inputAction?(string)
            return false
        }
        return true
    }

用户删除

用户删除,我们使用代理方法或者是监听通知时,如果输入框内本来就没有内容是没有获取到回调的,因此我们采用了重写UITextFiled的方式来获取删除按钮的点击事件。

代码如下:

import UIKit

class PHCodeField: UITextField {
    
    /// 键盘删除按钮点击回调
    var deleteAction: (() -> Void)?
    
    override func deleteBackward() {
        super.deleteBackward()
        deleteAction?()
    }
}

增加删除回调:

    /// 删除回调
    var deleteAction: (() -> Void)?

调用删除回调:

    func setEvent() {
        codeField.deleteAction = { [weak self] in
            guard let self = self else { return }
            self.deleteAction?()
        }
    }

自定义单元输入框状态

另外为了区分输入框是否处于响应状态我们采用不同的UI样式来进行区分一下,代码如下:

    /// 选中属性
    var isSelected:Bool = false {
        didSet {
            if isSelected {
                self.layer.borderColor = UIColor.orange.cgColor
                codeField.becomeFirstResponder()
                codeField.isHidden = false
                label.isHidden = true
            } else {
                self.layer.borderColor = UIColor.clear.cgColor
                codeField.resignFirstResponder()
                codeField.isHidden = true
                label.isHidden = false
            }
        }
    }

获取验证码

添加一个只读属性,读取验证码的值。

/// 验证码
    var code:String? {
        get {
            return label.text
        }
    }

2.创建验证码输入视图

这个相对简单一些,我们只需要根据验证码的个数将刚刚创建的最小单元输入口整齐的排在上面就可以了,案例中我们设置输入框的个数为5,间距为8,size为{40,54}。

class PHVerifyCodeView: UIView {
    
    /// 验证码数字个数
    private var count = 5
    /// 默认间距
    private var space:CGFloat = 8.0
    /// stackView
    private let contentView = UIView()
    /// 默认size
    private var itemSize:CGSize = CGSize(width: 40.0, height: 54.0)

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
        setLayout()
        addInputItemViews()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setupView() {
        self.addSubview(contentView)
        
    }
    
    func setLayout() {
        contentView.frame = self.bounds
    }
    
    func addInputItemViews()  {
        var offsetX = (contentView.bounds.size.width - CGFloat(count) * itemSize.width - CGFloat(count - 1) * space) / 2.0
        for i in 0 ..< count {
            let itemView = PHInputItemView(frame: CGRect(x: offsetX, y: 0.0, width: itemSize.width, height: itemSize.height))
            contentView.addSubview(itemView)
            if i == 0 {
                itemView.isSelected = true
            }
            offsetX += itemSize.width + space
        }
    }
 
    
    override func layoutSubviews() {
        super.layoutSubviews()
        setLayout()
    }
}

3.响应用户输入,自动切换到下一个单元输入框

响应用户的输入回调,代码如下:

            itemView.inputAction = { [weak self] text in
                guard let self = self else { return }
                self.input(text: text, index: i, itemView: itemView)
            }

实现自动切换到下一个输入框,这里面我们需要注意到临界值:

    private func input(text:String,index:Int,itemView:PHInputItemView) {
        if index == count - 1 {
            itemView.isSelected = false
        } else {
            let nextItemView = contentView.subviews[index + 1] as! PHInputItemView
            nextItemView.isSelected = true
            itemView.isSelected = false
        }
    }

4.相应用户删除,自动切换到上一个输入框

响应用户删除,代码如下:

            itemView.deleteAction = { [weak self] in
                guard let self = self else { return }
                self.delete(index: i, itemView: itemView)
            }

实现自动切换到上一个输入框,代码如下:

    private func delete(index:Int,itemView:PHInputItemView) {
        if index == 0 {
            return
        }
        let preItemView = contentView.subviews[index - 1] as! PHInputItemView
        preItemView.isSelected = true
        itemView.isSelected = false
    }

5.输入完成回调

添加输入完成回调:

/// 输入完成回调
    var inputFinish: ((String) -> Void)?
    

在用户输入完成之后,调用回调函数传入验证码:

    private func input(text:String,index:Int,itemView:PHInputItemView) {
        if index == count - 1 {
            itemView.isSelected = false
            inputFinish?(getVerifyCode())
        } else {
            let nextItemView = contentView.subviews[index + 1] as! PHInputItemView
            nextItemView.isSelected = true
            itemView.isSelected = false
        }
    }
    
    /// 获取验证码
    func getVerifyCode() -> String {
        var code = ""
        for view in contentView.subviews {
            let itemView = view as! PHInputItemView
            code += itemView.code ?? ""
        }
        return code
    }

6.使用验证码输入框

在视图控制器中直接使用,代码如下:

        let verifyCodeView = PHVerifyCodeView(frame: CGRect(x: 0.0, y: 100.0, width: self.view.bounds.size.width, height: 54.0))
        verifyCodeView.backgroundColor = .cyan
        self.view.addSubview(verifyCodeView)

实现输入完成的回调:

  verifyCodeView.inputFinish = { code in
            print("输入完成:\(code)")
        }

结语

实现一个带有光标的方块输入框就完成了,如果不需要显示光标,那么实现起来会非常简单。我们只需创建几个 UILabel和一个隐藏的 UITextField,然后监听用户输入,将所有内容分割并按顺序显示到 UILabel 中即可。

每个需求和设计的实现方案都不是唯一的,如果大家有更好的方案,欢迎分享出来。

  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!如果你想在Vue中创建一个单个验证码输入框,你可以按照以下步骤进行操作: 1. 在Vue项目中创建一个新的组件,可以将其命名为"VerificationCodeInput"。 2. 在该组件的模板中,添加一个文本输入框和一个按钮。你可以使用`<input type="text">`来创建文本输入框,`<button>`来创建按钮。 3. 为文本输入框添加一个v-model指令,将其值绑定到Vue实例的数据属性上。这样,当用户输入验证码时,该数据属性会自动更新。 4. 为按钮添加一个点击事件处理程序,当用户点击按钮时,可以执行一些操作,比如验证验证码是否正确。 5. 在Vue实例中使用该组件,在父组件中引入并在模板中放置`<verification-code-input></verification-code-input>`标签。 下面是一个简单的示例代码,供你参考: ```vue <template> <div> <verification-code-input></verification-code-input> </div> </template> <script> // 导入VerificationCodeInput组件 import VerificationCodeInput from './components/VerificationCodeInput.vue'; export default { components: { VerificationCodeInput } } </script> <!-- VerificationCodeInput.vue --> <template> <div> <input type="text" v-model="verificationCode"> <button @click="validateCode">验证</button> </div> </template> <script> export default { data() { return { verificationCode: '' } }, methods: { validateCode() { // 在这里执行验证验证码的操作 console.log(this.verificationCode); } } } </script> ``` 这只是一个简单的示例,你可以根据实际需求来自定义组件的样式和验证逻辑。希望能对你有所帮助!如果有任何疑问,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值