Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 33 additions & 21 deletions Example/MHVerifyCodeView/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}()
}

183 changes: 105 additions & 78 deletions MHVerifyCodeView/Classes/MHVerifyCodeView.swift
Original file line number Diff line number Diff line change
@@ -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<UITouch>, 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..<count).compactMap({ builder?($0) })
arranges.forEach { item in
stacks.addArrangedSubview(item)
[item].forEach({
$0.translatesAutoresizingMaskIntoConstraints = false
})
NSLayoutConstraint.activate([
item.widthAnchor.constraint(equalTo: stacks.heightAnchor, constant: 0.0),
item.heightAnchor.constraint(equalTo: stacks.heightAnchor, constant: 0.0)
])
}
}

/**验证码输入完成后的回调闭包,返回参数为验证码*/
var completeHandler: ((_ verifyCode: String) -> 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<UITouch>, with event: UIEvent?) {
hideTextField.becomeFirstResponder()
@objc private func showFieldEvent(gesture: UITapGestureRecognizer) {
field.becomeFirstResponder()
}

}
83 changes: 63 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down