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
12 changes: 12 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// swift-tools-version:5.1
import PackageDescription

let package = Package(
name: "PinCodeTextField",
products: [
.library(name: "PinCodeTextField", targets: ["PinCodeTextField"])
],
targets: [
.target(name: "PinCodeTextField", dependencies: [], path: "Pod")
]
)
97 changes: 23 additions & 74 deletions Pod/PinCodeTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,14 @@
import Foundation
import UIKit

@IBDesignable open class PinCodeTextField: UIView {
@IBDesignable public class PinCodeTextField: UIView {
public weak var delegate: PinCodeTextFieldDelegate?

//MARK: Customizable from Interface Builder
@IBInspectable public var underlineWidth: CGFloat = 40
@IBInspectable public var underlineHSpacing: CGFloat = 10
@IBInspectable public var underlineVMargin: CGFloat = 0
@IBInspectable public var characterLimit: Int = 4 {
willSet {
if characterLimit != newValue {
updateView()
}
}
}
@IBInspectable public var characterLimit: Int = 5
@IBInspectable public var underlineHeight: CGFloat = 3
@IBInspectable public var placeholderText: String?
@IBInspectable public var text: String? {
Expand All @@ -42,20 +36,17 @@ import UIKit
@IBInspectable public var updatedUnderlineColor: UIColor = UIColor.clear
@IBInspectable public var secureText: Bool = false
@IBInspectable public var needToUpdateUnderlines: Bool = true
@IBInspectable public var characterBackgroundColor: UIColor = UIColor.clear
@IBInspectable public var characterBackgroundCornerRadius: CGFloat = 0
@IBInspectable public var highlightInputUnderline: Bool = false

//MARK: Customizable from code
public var keyboardType: UIKeyboardType = UIKeyboardType.alphabet
public var textContentType: UITextContentType = UITextContentType.oneTimeCode
public var keyboardAppearance: UIKeyboardAppearance = UIKeyboardAppearance.default
public var autocorrectionType: UITextAutocorrectionType = UITextAutocorrectionType.no
public var font: UIFont = UIFont.systemFont(ofSize: 14)
public var allowedCharacterSet: CharacterSet = CharacterSet.alphanumerics
public var textContentType: UITextContentType! = nil

private var _inputView: UIView?
open override var inputView: UIView? {
public override var inputView: UIView? {
get {
return _inputView
}
Expand All @@ -66,7 +57,7 @@ import UIKit

// UIResponder
private var _inputAccessoryView: UIView?
@IBOutlet open override var inputAccessoryView: UIView? {
@IBOutlet public override var inputAccessoryView: UIView? {
get {
return _inputAccessoryView
}
Expand All @@ -85,13 +76,12 @@ import UIKit
}

//MARK: Private
private var labels: [UILabel] = []
private var underlines: [UIView] = []
private var backgrounds: [UIView] = []
fileprivate var labels: [UILabel] = []
fileprivate var underlines: [UIView] = []


//MARK: Init and awake
override public init(frame: CGRect) {
override init(frame: CGRect) {
super.init(frame: frame)
postInitialize()
}
Expand All @@ -100,12 +90,12 @@ import UIKit
super.init(coder: aDecoder)
}

override open func awakeFromNib() {
override public func awakeFromNib() {
super.awakeFromNib()
postInitialize()
}

override open func prepareForInterfaceBuilder() {
override public func prepareForInterfaceBuilder() {
postInitialize()
}

Expand All @@ -114,42 +104,38 @@ import UIKit
}

//MARK: Overrides
override open func layoutSubviews() {
override public func layoutSubviews() {
layoutCharactersAndPlaceholders()
super.layoutSubviews()
}

override open var canBecomeFirstResponder: Bool {
override public var canBecomeFirstResponder: Bool {
return true
}

@discardableResult override open func becomeFirstResponder() -> Bool {
@discardableResult override public func becomeFirstResponder() -> Bool {
delegate?.textFieldDidBeginEditing(self)
return super.becomeFirstResponder()
}

@discardableResult override open func resignFirstResponder() -> Bool {
@discardableResult override public func resignFirstResponder() -> Bool {
delegate?.textFieldDidEndEditing(self)
return super.resignFirstResponder()
}

//MARK: Private
private func updateView() {
if needToRecreateBackgrounds() {
recreateBackgrounds()
}
if needToRecreateUnderlines() {
fileprivate func updateView() {
if (needToRecreateUnderlines()) {
recreateUnderlines()
}
if needToRecreateLabels() {
if (needToRecreateLabels()) {
recreateLabels()
}
updateLabels()

if needToUpdateUnderlines {
updateUnderlines()
}
updateBackgrounds()
setNeedsLayout()
}

Expand All @@ -161,10 +147,6 @@ import UIKit
return characterLimit != labels.count
}

private func needToRecreateBackgrounds() -> Bool {
return characterLimit != backgrounds.count
}

private func recreateUnderlines() {
underlines.forEach{ $0.removeFromSuperview() }
underlines.removeAll()
Expand All @@ -185,20 +167,10 @@ import UIKit
}
}

private func recreateBackgrounds() {
backgrounds.forEach{ $0.removeFromSuperview() }
backgrounds.removeAll()
characterLimit.times {
let background = createBackground()
backgrounds.append(background)
addSubview(background)
}
}

private func updateLabels() {
let textHelper = TextHelper(text: text, placeholder: placeholderText, isSecure: isSecureTextEntry)
for label in labels {
let index = labels.firstIndex(of: label) ?? 0
let index = labels.index(of: label) ?? 0
let currentCharacter = textHelper.character(atIndex: index)
label.text = currentCharacter.map { String($0) }
label.font = font
Expand All @@ -209,8 +181,8 @@ import UIKit

private func updateUnderlines() {
for label in labels {
let index = labels.firstIndex(of: label) ?? 0
if (!highlightInputUnderline || !isInput(index)) && isPlaceholder(index) {
let index = labels.index(of: label) ?? 0
if isPlaceholder(index) {
underlines[index].backgroundColor = underlineColor
}
else{
Expand All @@ -219,13 +191,6 @@ import UIKit
}
}

private func updateBackgrounds() {
for background in backgrounds {
background.backgroundColor = characterBackgroundColor
background.layer.cornerRadius = characterBackgroundCornerRadius
}
}

private func labelColor(isPlaceholder placeholder: Bool) -> UIColor {
return placeholder ? placeholderColor : textColor
}
Expand All @@ -235,11 +200,6 @@ import UIKit
return i >= inputTextCount
}

private func isInput(_ i: Int) -> Bool {
let inputTextCount = text?.count ?? 0
return i == inputTextCount
}

private func createLabel() -> UILabel {
let label = UILabel(frame: CGRect())
label.font = font
Expand All @@ -254,14 +214,6 @@ import UIKit
return underline
}

private func createBackground() -> UIView {
let background = UIView()
background.backgroundColor = characterBackgroundColor
background.layer.cornerRadius = characterBackgroundCornerRadius
background.clipsToBounds = true
return background
}

private func layoutCharactersAndPlaceholders() {
let marginsCount = characterLimit - 1
let totalMarginsWidth = underlineHSpacing * CGFloat(marginsCount)
Expand All @@ -273,11 +225,8 @@ import UIKit
let totalLabelHeight = font.ascender + font.descender
let underlineY = bounds.height / 2 + totalLabelHeight / 2 + underlineVMargin

for i in 0..<underlines.count {
let underline = underlines[i]
let background = backgrounds[i]
underline.frame = CGRect(x: currentUnderlineX, y: underlineY, width: underlineWidth, height: underlineHeight)
background.frame = CGRect(x: currentUnderlineX, y: 0, width: underlineWidth, height: bounds.height)
underlines.forEach{
$0.frame = CGRect(x: currentUnderlineX, y: underlineY, width: underlineWidth, height: underlineHeight)
currentUnderlineX += underlineWidth + underlineHSpacing
}

Expand All @@ -292,7 +241,7 @@ import UIKit
}

//MARK: Touches
override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
Expand Down