Skip to content

Commit

Permalink
Fixed delete backward issue
Browse files Browse the repository at this point in the history
  • Loading branch information
arturdev committed Oct 10, 2019
1 parent de826bf commit 19ec851
Show file tree
Hide file tree
Showing 9 changed files with 311 additions and 253 deletions.
2 changes: 1 addition & 1 deletion SwiftyCodeView.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
Pod::Spec.new do |s|
s.swift_version = '4.1'
s.name = 'SwiftyCodeView'
s.version = '0.3.2'
s.version = '0.3.3'
s.summary = 'An UI Component for verification codes written in swift'

s.description = <<-DESC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ open class SwiftyCodeTextField: UITextField {
weak open var deleteDelegate: SwiftyCodeTextFieldDelegate?

override open func deleteBackward() {
super.deleteBackward()
deleteDelegate?.deleteBackward(sender: self)
super.deleteBackward()
}
}
264 changes: 142 additions & 122 deletions SwiftyCodeView/Classes/SwiftyCodeView/SwiftyCodeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,135 +6,155 @@

import UIKit

#if canImport(RxCocoa)
import RxSwift
import RxCocoa
extension Reactive where Base: SwiftyCodeView {

/// Reactive wrapper for `code` property.
internal var code: ControlProperty<String> {
return controlProperty(editingEvents: [.allEditingEvents, .valueChanged],
getter: { codeView in
codeView.code
}, setter: { codeView, value in
codeView.code = value
})
}
}
#endif


@objc
public protocol SwiftyCodeViewDelegate: class {
func codeView(sender: SwiftyCodeView, didFinishInput code: String)
func codeView(sender: SwiftyCodeView, didFinishInput code: String)
}

@IBDesignable
open class SwiftyCodeView: UIControl {
@IBInspectable open var length: Int = 4 {
didSet {
setupUI()
}
}
@IBOutlet open weak var delegate: SwiftyCodeViewDelegate?
var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
return stackView
}()
fileprivate var items: [SwiftyCodeItemView] = []
open var code: String {
get {
let items = stackView.arrangedSubviews.map({$0 as! SwiftyCodeItemView})
let values = items.map({$0.textField.text ?? ""})
return values.joined()
}
set {
let array = newValue.map(String.init)
for i in 0..<array.count {
let item = stackView.arrangedSubviews[i] as! SwiftyCodeItemView
item.textField.text = array[i]
}
}
}
override open func awakeFromNib() {
super.awakeFromNib()
setupUI()
let tap = UITapGestureRecognizer(target: self, action: #selector(becomeFirstResponder))
addGestureRecognizer(tap)
}
fileprivate func setupUI() {
stackView.frame = self.bounds
if stackView.superview == nil {
addSubview(stackView)
}
stackView.arrangedSubviews.forEach{($0.removeFromSuperview())}
for i in 0..<length {
let itemView = generateItem()
itemView.textField.deleteDelegate = self
itemView.textField.delegate = self
itemView.tag = i
itemView.textField.tag = i
stackView.addArrangedSubview(itemView)
}
}
open func generateItem() -> SwiftyCodeItemView {
let type = SwiftyCodeItemView.self
let typeStr = type.description().components(separatedBy: ".").last ?? ""
let bundle = Bundle(for: type)
return bundle
.loadNibNamed(typeStr,
owner: nil,
options: nil)?
.last as! SwiftyCodeItemView
}
override open func becomeFirstResponder() -> Bool {
let items = stackView.arrangedSubviews
.map({$0 as! SwiftyCodeItemView})
return (items.filter({($0.textField.text ?? "").isEmpty}).first ?? items.last)!.becomeFirstResponder()
}
override open func resignFirstResponder() -> Bool {
stackView.arrangedSubviews.forEach({$0.resignFirstResponder()})
return true
}
override open func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupUI()
}
@IBInspectable open var length: Int = 4 {
didSet {
setupUI()
}
}

@IBOutlet open weak var delegate: SwiftyCodeViewDelegate?

var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
return stackView
}()

fileprivate var items: [SwiftyCodeItemView] = []
open var code: String {
get {
let items = stackView.arrangedSubviews.map({$0 as! SwiftyCodeItemView})
let values = items.map({$0.textField.text ?? ""})
return values.joined()
}
set {
let array = newValue.map(String.init)
for i in 0..<array.count {
let item = stackView.arrangedSubviews[i] as! SwiftyCodeItemView
item.textField.text = array[i]
}
}
}

override open func awakeFromNib() {
super.awakeFromNib()
setupUI()

let tap = UITapGestureRecognizer(target: self, action: #selector(becomeFirstResponder))
addGestureRecognizer(tap)
}

fileprivate func setupUI() {
stackView.frame = self.bounds
if stackView.superview == nil {
addSubview(stackView)
}
stackView.arrangedSubviews.forEach{($0.removeFromSuperview())}

for i in 0..<length {
let itemView = generateItem()
itemView.textField.deleteDelegate = self
itemView.textField.delegate = self
itemView.tag = i
itemView.textField.tag = i
stackView.addArrangedSubview(itemView)
}
}

open func generateItem() -> SwiftyCodeItemView {
let type = SwiftyCodeItemView.self
let typeStr = type.description().components(separatedBy: ".").last ?? ""
let bundle = Bundle(for: type)
return bundle
.loadNibNamed(typeStr,
owner: nil,
options: nil)?
.last as! SwiftyCodeItemView
}

override open func becomeFirstResponder() -> Bool {
let items = stackView.arrangedSubviews
.map({$0 as! SwiftyCodeItemView})
return (items.filter({($0.textField.text ?? "").isEmpty}).first ?? items.last)!.becomeFirstResponder()
}

override open func resignFirstResponder() -> Bool {
stackView.arrangedSubviews.forEach({$0.resignFirstResponder()})
return true
}

override open func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupUI()
}
}

extension SwiftyCodeView: UITextFieldDelegate, SwiftyCodeTextFieldDelegate {

public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

if string == "" { //is backspace
return true
}

if !textField.hasText {
let index = textField.tag
let item = stackView.arrangedSubviews[index] as! SwiftyCodeItemView
item.textField.text = string
sendActions(for: .valueChanged)
if index == length - 1 { //is last textfield
delegate?.codeView(sender: self, didFinishInput: self.code)
textField.resignFirstResponder()
return false
}

_ = stackView.arrangedSubviews[index + 1].becomeFirstResponder()
}

return false
}

public func deleteBackward(sender: SwiftyCodeTextField) {
for i in 1..<length{
let itemView = stackView.arrangedSubviews[i] as! SwiftyCodeItemView

if !itemView.textField.isFirstResponder {
continue
}

let prevItem = stackView.arrangedSubviews[i-1] as! SwiftyCodeItemView
_ = prevItem.becomeFirstResponder()
prevItem.textField.text = ""
}
sendActions(for: .valueChanged)
}

public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

if string == "" { //is backspace
return true
}

if !textField.hasText {
let index = textField.tag
let item = stackView.arrangedSubviews[index] as! SwiftyCodeItemView
item.textField.text = string
sendActions(for: .valueChanged)
if index == length - 1 { //is last textfield
delegate?.codeView(sender: self, didFinishInput: self.code)
textField.resignFirstResponder()
return false
}

_ = stackView.arrangedSubviews[index + 1].becomeFirstResponder()
}

return false
}

public func deleteBackward(sender: SwiftyCodeTextField) {
for i in 1..<length{
let itemView = stackView.arrangedSubviews[i] as! SwiftyCodeItemView

if !itemView.textField.isFirstResponder {
continue
}

let prevItem = stackView.arrangedSubviews[i-1] as! SwiftyCodeItemView
if itemView.textField.text?.isEmpty ?? true {
prevItem.textField.text = ""
_ = prevItem.becomeFirstResponder()
}
}
sendActions(for: .valueChanged)
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 19ec851

Please sign in to comment.