From 925f377037c5fa2cd3db6fa7f4bec716b43d0ee2 Mon Sep 17 00:00:00 2001 From: moonShadow <331614794@qq.com> Date: Fri, 29 Mar 2024 16:58:08 +0800 Subject: [PATCH 1/2] [feat] - Completely refactored to resolve event conflicts and out-of-bounds protection; - Fully customizable item; - iOS 11.x features needed; - You can update the pod repository yourself, although I don't think it's necessary. --- Example/MHVerifyCodeView/ViewController.swift | 54 ++++-- .../Classes/MHVerifyCodeView.swift | 183 ++++++++++-------- 2 files changed, 138 insertions(+), 99 deletions(-) diff --git a/Example/MHVerifyCodeView/ViewController.swift b/Example/MHVerifyCodeView/ViewController.swift index a26f338..92aad0b 100644 --- a/Example/MHVerifyCodeView/ViewController.swift +++ b/Example/MHVerifyCodeView/ViewController.swift @@ -13,30 +13,42 @@ class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view, typically from a nib. - let count = 4 - let spacing: CGFloat = 10 - let height: CGFloat = 50 - let width: CGFloat = height * CGFloat(count) + spacing * CGFloat(count - 1) - let verifyCodeView = MHVerifyCodeView.init { (vc) in - let alert = UIAlertController.init(title: "验证码", message: vc, preferredStyle: .alert) - alert.addAction(UIAlertAction.init(title: "确定", style: .cancel, handler: nil)) - self.show(alert, sender: nil) - } - verifyCodeView.verifyCount = count - - verifyCodeView.spacing = spacing - - verifyCodeView.frame = CGRect(x: (UIScreen.main.bounds.width - width) / 2, y: (UIScreen.main.bounds.height - height) / 2, width: width, height: height) + loadViews(in: view) + } + + // MARK: View + + private func loadViews(in box: UIView) { + box.backgroundColor = .lightGray - self.view.addSubview(verifyCodeView) + box.addSubview(mhField) + [mhField].forEach({ + $0.translatesAutoresizingMaskIntoConstraints = false + }) + NSLayoutConstraint.activate([ + mhField.centerYAnchor.constraint(equalTo: box.centerYAnchor, constant: 0.0), + + mhField.leftAnchor.constraint(equalTo: box.leftAnchor, constant: 0.0), + mhField.rightAnchor.constraint(equalTo: box.rightAnchor, constant: 0.0), + + mhField.heightAnchor.constraint(equalToConstant: 64.0), + ]) } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. + + private func shouldSendCode(_ code: String) { + let message = "shouldSendCode, code=\(code)" + print("MOON__Log" + message) + let alert = UIAlertController(title: "Hint", message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "ok", style: .default)) + present(alert, animated: true) } - + + private lazy var mhField: MHVerifyCodeView = { + let field = MHVerifyCodeView { [weak self] code in + self?.shouldSendCode(code) + } + return field + }() } diff --git a/MHVerifyCodeView/Classes/MHVerifyCodeView.swift b/MHVerifyCodeView/Classes/MHVerifyCodeView.swift index 45bb7c3..0d8659c 100644 --- a/MHVerifyCodeView/Classes/MHVerifyCodeView.swift +++ b/MHVerifyCodeView/Classes/MHVerifyCodeView.swift @@ -1,116 +1,143 @@ +// +// VerifyCodeView.swift +// +// Created by moonShadow on 2024/03/22 +// Copyright © 2024 jiejing. All rights reserved. +// +// LICENSE: SAME AS REPOSITORY +// Contact me: [GitHub](https://github.com/darkThanBlack) +// import UIKit -class VerifyCodeSingleView: UILabel{ +/// Simple verify code field. +/// TODO: iOS 11.x auto input support needed... +/// +/// refer: https://github.com/feaskters/MHVerifyCodeView/blob/master/MHVerifyCodeView/ +public class MHVerifyCodeView: UIView, UITextFieldDelegate { - override init(frame: CGRect) { - super.init(frame: frame) - self.layer.borderColor = UIColor.lightGray.cgColor - self.layer.borderWidth = 0.5 - self.textAlignment = .center - self.font = UIFont.systemFont(ofSize: 16) - self.textColor = .black - } + // MARK: Interface - required init?(coder: NSCoder) { - super.init(coder: coder) - } + /// + public var completedHandler: ((_ verifyCode: String) -> Void)? - override func touchesBegan(_ touches: Set, with event: UIEvent?) { - super.touchesBegan(touches, with: event) + /// + public func setupDefItem(count: Int = 4, configer: ((_ label: UILabel) -> Void)? = nil) { + setupItem(count: count, builder: { _ in + let label = UILabel() + label.backgroundColor = .white + label.layer.masksToBounds = true + label.layer.cornerRadius = 8.0 + label.textAlignment = .center + label.font = UIFont.systemFont(ofSize: 23, weight: .medium) + label.textColor = UIColor(red: 61 / 255.0, green: 61 / 255.0, blue: 61 / 255.0, alpha: 1.0) + configer?(label) + return label + }) } -} - -public class MHVerifyCodeView: UIStackView, UITextFieldDelegate { - - var verifyCodes: [VerifyCodeSingleView]! - /**验证码字体*/ - public var font: UIFont = UIFont.systemFont(ofSize: 16) { - didSet{ - for verifyCode in self.verifyCodes{ - verifyCode.font = font - } - } - } + // MARK: Customable - /**验证码数量*/ - public var verifyCount: Int? { - didSet{ - for _ in Range(0...(verifyCount ?? 4) - 1) { - let singleView = VerifyCodeSingleView(frame: CGRect(x: 0, y: 0, width: 0, height: 0)) - verifyCodes.append(singleView) - self.addArrangedSubview(singleView) - } + /// + public func setupItem(count: Int, builder: ((_ index: Int) -> UIView)?) { + stacks.arrangedSubviews.forEach({ $0.removeFromSuperview() }) + + let arranges = (0.. Void)! - - //隐藏的输入框 - lazy var hideTextField: UITextField = { + /// + public lazy var field: UITextField = { let textfield = UITextField() - self.addSubview(textfield) textfield.isHidden = true textfield.keyboardType = .numberPad textfield.delegate = self return textfield }() - override init(frame: CGRect) { - super.init(frame: frame) - self.axis = .horizontal - self.distribution = .fillEqually - verifyCodes = [] - verifyCount = 4 + /// + public lazy var stacks: UIStackView = { + let stacks = UIStackView() + stacks.axis = .horizontal + stacks.alignment = .center + stacks.distribution = .equalSpacing + return stacks + }() + + /// + private var items: [UILabel] { + return stacks.arrangedSubviews.compactMap({ $0 as? UILabel }) } - /** - - parameter complete: 验证完成回调闭包,返回参数为验证码 - */ - public convenience init(complete: @escaping (_ verifyCode: String) -> Void) { - self.init() - setCompleteHandler(complete: complete) + //MARK: Life Cycle + + /// + public convenience init(count: Int = 4, completed: ((_ verifyCode: String) -> Void)?) { + self.init(frame: .zero) + + setupDefItem(count: count) + completedHandler = completed + } + + override init(frame: CGRect) { + super.init(frame: frame) + + loadViews(in: self) + + isUserInteractionEnabled = true + let singleTap = UITapGestureRecognizer(target: self, action: #selector(showFieldEvent(gesture:))) + singleTap.numberOfTapsRequired = 1 + singleTap.numberOfTouchesRequired = 1 + addGestureRecognizer(singleTap) } - required init(coder: NSCoder) { + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - /**设置验证码输入完成后的回调闭包*/ - public func setCompleteHandler(complete: @escaping (_ verifyCode: String) -> Void) { - self.completeHandler = complete + //MARK: View + + private func loadViews(in box: UIView) { + [field, stacks].forEach({ + box.addSubview($0) + $0.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + $0.topAnchor.constraint(equalTo: box.topAnchor, constant: 0.0), + $0.leftAnchor.constraint(equalTo: box.leftAnchor, constant: 0.0), + $0.rightAnchor.constraint(equalTo: box.rightAnchor, constant: 0.0), + $0.bottomAnchor.constraint(equalTo: box.bottomAnchor, constant: 0.0), + ]) + }) } public func textFieldDidChangeSelection(_ textField: UITextField) { - guard textField.text!.count <= (verifyCount ?? 4) else { - textField.text = String(textField.text!.prefix(4)) + let text = textField.text ?? "" + guard text.count <= items.count else { + textField.text = String(text.prefix(items.count)) return } - var index = 0 - for char in textField.text! { - verifyCodes[index].text = String(char) - index += 1 - } - guard index < (verifyCount ?? 4) else { - - self.endEditing(true) - - if let complete = self.completeHandler { - complete(textField.text!) - } - return - } - for i in Range(index...(verifyCount ?? 4) - 1) { - verifyCodes[i].text = "" + items.forEach({ $0.text = "" }) + for (index, char) in text.enumerated() { + items[index].text = String(char) } + if (text.count == items.count) { + endEditing(true) + completedHandler?(text) + } } - public override func touchesBegan(_ touches: Set, with event: UIEvent?) { - hideTextField.becomeFirstResponder() + @objc private func showFieldEvent(gesture: UITapGestureRecognizer) { + field.becomeFirstResponder() } - } From 495cbeef1a4e5b3c58102c8357bffe2d62fb6676 Mon Sep 17 00:00:00 2001 From: moonShadow <331614794@qq.com> Date: Fri, 29 Mar 2024 17:06:50 +0800 Subject: [PATCH 2/2] [feat] - Update README.md text; - Gif update needed. --- README.md | 83 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index b44d7ac..2cb53e3 100644 --- a/README.md +++ b/README.md @@ -5,40 +5,83 @@ [![License](https://img.shields.io/cocoapods/l/MHVerifyCodeView.svg?style=flat)](https://cocoapods.org/pods/MHVerifyCodeView) [![Platform](https://img.shields.io/cocoapods/p/MHVerifyCodeView.svg?style=flat)](https://cocoapods.org/pods/MHVerifyCodeView) -## Example -To run the example project, clone the repo, and run `pod install` from the Example directory first. + +## Example ![VerifyCodeView](https://s1.ax1x.com/2020/04/30/JqcKXj.gif) -## Requirements - - iOS >= 9.0 - - swift5 -## Installation +## Sample + +```swift +class ViewController: UIViewController { -MHVerifyCodeView is available through [CocoaPods](https://cocoapods.org). To install -it, simply add the following line to your Podfile: + override func viewDidLoad() { + super.viewDidLoad() + + loadViews(in: view) + } + + // MARK: View + + private func loadViews(in box: UIView) { + box.backgroundColor = .lightGray + + box.addSubview(mhField) + [mhField].forEach({ + $0.translatesAutoresizingMaskIntoConstraints = false + }) + NSLayoutConstraint.activate([ + mhField.centerYAnchor.constraint(equalTo: box.centerYAnchor, constant: 0.0), + + mhField.leftAnchor.constraint(equalTo: box.leftAnchor, constant: 0.0), + mhField.rightAnchor.constraint(equalTo: box.rightAnchor, constant: 0.0), + // Static constraint needed. + mhField.heightAnchor.constraint(equalToConstant: 64.0), + ]) + } + + private func shouldSendCode(_ code: String) { + let message = "shouldSendCode, code=\(code)" + print("MOON__Log" + message) + let alert = UIAlertController(title: "Hint", message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "ok", style: .default)) + present(alert, animated: true) + } + + private lazy var mhField: MHVerifyCodeView = { + let field = MHVerifyCodeView { [weak self] code in + self?.shouldSendCode(code) + } + return field + }() +} -```ruby -pod 'MHVerifyCodeView' ``` -## Usage -```swift -let verifyCodeView = MHVerifyCodeView.init() -verifyCodeView.spacing = 10 -verifyCodeView.verifyCount = 4 -verifyCodeView.setCompleteHandler { (result) in - print(result) -} -``` ## Author -feaskters, 739296759@qq.com +[feaskters](739296759@qq.com) + + + +## Contributor + +[darkThanBlack](https://github.com/darkThanBlack) + + + +## Update + +2024/03/29. Completely refactored by [darkThanBlack](https://github.com/darkThanBlack). + +2020/04/30. Created by [feaskters](739296759@qq.com). + + ## License