Skip to content

Instantly share code, notes, and snippets.

@cedricbahirwe
Created August 15, 2021 21:49
Show Gist options
  • Save cedricbahirwe/96c1e8b3f928e7fad5b63b96552455a5 to your computer and use it in GitHub Desktop.
Save cedricbahirwe/96c1e8b3f928e7fad5b63b96552455a5 to your computer and use it in GitHub Desktop.
OneTime Code SwiftUI Wrapper Customizable View
struct PassCodeCodeView: UIViewRepresentable {
init(_ count: Int = 5,
width: CGFloat,
height: CGFloat,
spacing: CGFloat = 10,
action: @escaping (String) -> ()) {
self.width = width
self.height = height
self.spacing = spacing
self.action = action
for i in 1...count {
let field = UITextField()
field.tag = i
fields.append(field)
}
}
let width: CGFloat
let height: CGFloat
let spacing: CGFloat
var action: (String) -> ()
private var fields: [UITextField] = []// [UITextField]()
private var fieldsCount: Int { fields.isEmpty ? 1 : fields.count }
private var estimatedFieldWidth: CGFloat {
let estimation = width - spacing*CGFloat(fieldsCount-1)
return estimation / CGFloat(fieldsCount)
}
private var estimatedFieldHeight: CGFloat {
height // - 5
}
func beautifyFields(_ fields: [UITextField]) {
for field in fields {
field.placeholder = ""
field.clearButtonMode = .whileEditing
field.textColor = .label
field.font = .systemFont(ofSize: 20, weight: .bold)
field.textAlignment = .center
field.autocorrectionType = .no
field.clearButtonMode = .never
field.autocapitalizationType = .none
field.textContentType = .oneTimeCode
field.keyboardType = .numberPad
field.heightAnchor.constraint(equalToConstant: estimatedFieldHeight).isActive = true
field.widthAnchor.constraint(equalToConstant: estimatedFieldWidth).isActive = true
field.layer.borderColor = UIColor.label.cgColor
field.layer.borderWidth = 1
field.layer.cornerRadius = 5
}
}
func updateTextFields(_ fields: [UITextField]) {
for field in fields {
field.layer.borderColor = UIColor.label.cgColor
field.textColor = .label
}
}
func makeUIView(context: Context) -> UIStackView {
fields.forEach { $0.delegate = context.coordinator }
beautifyFields(fields)
//Stack View
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.alignment = .center
stackView.spacing = spacing
fields.forEach({ stackView.addArrangedSubview($0) })
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}
func updateUIView(_ uiViewController: UIStackView, context: Context) {
updateTextFields(fields)
}
func makeCoordinator() -> PassCodeCodeView.Coordinator {
Coordinator(parent: self)
}
class Coordinator: NSObject, UITextFieldDelegate {
var parent: PassCodeCodeView
init(parent: PassCodeCodeView) {
self.parent = parent
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if !(string == "") {
textField.text = string
for i in (0..<parent.fields.count) {
if textField == parent.fields.last {
textField.resignFirstResponder()
let inputDigits = parent.fields.compactMap(\.text)
let passCode = inputDigits.joined()
if passCode.count == parent.fieldsCount {
parent.action(passCode)
}
break
} else if textField == parent.fields.fiedAt(i) {
parent.fields.fiedAt(i+1)?.text = ""
parent.fields.fiedAt(i+1)?.becomeFirstResponder()
break
}
}
return false
}
else {
if string.count == 0 && range.length > 0 {
textField.text = ""
for i in (0..<parent.fields.count).reversed() {
if textField == parent.fields.fiedAt(i) {
parent.fields.fiedAt(i-1)?.becomeFirstResponder()
break
}
}
return false
}
}
return true
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if (textField.text?.count ?? 0) > 0 {
}
return true
}
}
}
/// Use these extensions with precaution, (e.g: when you're sure that the array is not empty.)
private extension Array where Element == UITextField {
func fiedAt(_ index: Index) -> UITextField? {
if indices.contains(index) {
return self[index]
} else {
return nil
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment