Skip to content

Commit

Permalink
πŸ”€ Merge pull request #4 from goergisn/develop
Browse files Browse the repository at this point in the history
πŸ”€ Updating Main with Develop
  • Loading branch information
goergisn authored Oct 28, 2020
2 parents aff08e1 + 3e11a6f commit fffb5b8
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 59 deletions.
5 changes: 3 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ let package = Package(
.target(
name: "CodeInputField",
dependencies: [],
exclude: ["Resources/*"]),
exclude: ["Resources"]),
.testTarget(
name: "CodeInputFieldTests",
dependencies: ["CodeInputField"]),
dependencies: ["CodeInputField"],
exclude: ["Resources"]),
]
)
68 changes: 35 additions & 33 deletions Sources/CodeInputField/CodeInputField+DefaultSegment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import UIKit

public extension CodeInputField {
/// A rounded rect segment showing an outline when focussed
class DefaultSegment: UIView, CodeInputFieldSegment {

// MARK: - UI Elements
Expand All @@ -17,16 +18,17 @@ public extension CodeInputField {

// MARK: - Public Accessors

public var value: Digit? {
didSet {
label.text = text(for: value)

if value == oldValue { return }

UIView.animate(withDuration: 0.2, delay: 0, options: .beginFromCurrentState, animations: {
self.update()
})
}
public func update(value: Digit?, isFocussed: Bool) {
guard self.value != value || self.isFocussed != isFocussed else { return }

self.value = value
self.isFocussed = isFocussed

label.text = text(for: value)

UIView.animate(withDuration: 0.2, delay: 0, options: .beginFromCurrentState, animations: {
self.update()
})
}

public var inputFont: UIFont {
Expand Down Expand Up @@ -63,41 +65,41 @@ public extension CodeInputField {
}
}

private var inputOverride: String?

private var sanitizedCornerRadius: CGFloat {
return min(min(bounds.size.width * 0.5, bounds.size.height * 0.5), cornerRadius)
override public var tintColor: UIColor? {
didSet {
update()
}
}

func text(for value: Digit?) -> String? {
// MARK: - Privates

private var value: Digit?

private var isFocussed: Bool = false

private var inputOverride: String?

/**
Applies the `inputOverride` if provided. Otherwise returns the string representation off the value

- parameters:
- value: the value we want to have a String representation for.
*/
private func text(for value: Digit?) -> String? {
if let value = value {
return inputOverride ?? "\(value.rawValue)"
} else {
return nil
}
}

public var isSelected: Bool = false {
didSet {
if isSelected == oldValue { return }

UIView.animate(withDuration: 0.2, delay: 0, options: .beginFromCurrentState, animations: {
self.update()
})

}
}

override public var tintColor: UIColor? {
didSet {
update()
}
/// Making sure the corner radius isn't larger than 0.5 x the shortest side
private var sanitizedCornerRadius: CGFloat {
return min(min(bounds.size.width * 0.5, bounds.size.height * 0.5), cornerRadius)
}

// MARK: - Privates

private func update() {
layer.borderWidth = isSelected ? borderWidth : 0
layer.borderWidth = isFocussed ? borderWidth : 0
layer.borderColor = tintColor?.cgColor
label.alpha = value != nil ? 1 : 0
label.textColor = inputColor
Expand Down
64 changes: 42 additions & 22 deletions Sources/CodeInputField/CodeInputField.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@

import UIKit


/**
An input field allowing the user to enter digits

- Sends `.editingChanged` event on input change
- Sends `.editingDidEnd` event when input is complete (all fields are filled)
*/
public class CodeInputField: UIControl, UIKeyInput, UITextInputTraits {

// MARK: Public Accessors

public var input: String {
get {
return values.map { (value) -> String in
return "\(String(describing: value))"
return values.compactMap { (value) -> String? in
guard let value = value else { return nil }
return "\(value.rawValue)"
}.joined(separator: "")
}
}

/**
Clears the input when becoming first responder
Useful for obfuscated code input
*/
public var shouldClearInputWhenBecomingFirstResponder: Bool = false

public var preferredSegmentSize = CGSize(width: 50, height: 70) {
Expand All @@ -31,27 +43,28 @@ public class CodeInputField: UIControl, UIKeyInput, UITextInputTraits {
private var values: [Digit?] {
didSet {
updateSegments()

if input.count == numberOfDigits {
sendActions(for: .editingDidEnd)
}
}
}

func clearInput() {
values = Array(repeating: nil, count: numberOfDigits)
selectedIndex = 0
focussedSegmentIndex = 0
}

// MARK: Private Vars

private var segments: [CodeInputFieldSegment]

private var selectedIndex: Int = 0 {
private var focussedSegmentIndex: Int = 0 {
didSet {
guard selectedIndex != oldValue else { return }
// We always want to send the event even though the index might not have changed
sendActions(for: .editingChanged)

if selectedIndex == highestIndex {
sendActions(for: .editingDidEnd)
} else {
sendActions(for: .editingChanged)
}
guard focussedSegmentIndex != oldValue else { return }

UIView.transition(with: self, duration: 0.1, options: [.transitionCrossDissolve]) {
self.updateSegments()
Expand Down Expand Up @@ -113,7 +126,7 @@ public class CodeInputField: UIControl, UIKeyInput, UITextInputTraits {
guard let values = pasteboardValues, values.count == numberOfDigits else { return }

self.values = values
selectedIndex = highestIndex
focussedSegmentIndex = highestIndex
}

// MARK: - UIView
Expand Down Expand Up @@ -178,7 +191,7 @@ public class CodeInputField: UIControl, UIKeyInput, UITextInputTraits {
addTarget(self, action: #selector(fieldTapped), for: .touchUpInside)
}

convenience init(segments: [CodeInputFieldSegment],
public convenience init(segments: [CodeInputFieldSegment],
shouldClearInputWhenBecomingFirstResponder: Bool = false) {
self.init(segments: segments)
self.shouldClearInputWhenBecomingFirstResponder = shouldClearInputWhenBecomingFirstResponder
Expand All @@ -195,20 +208,27 @@ extension CodeInputField {
public var hasText: Bool { return self.input.count > 0 }

public func insertText(_ text: String) {
guard selectedIndex <= highestIndex, let intValue = Int(text) else { return }
guard focussedSegmentIndex <= highestIndex, let intValue = Int(text) else { return }

values[selectedIndex] = Digit(rawValue: intValue)
selectedIndex = min(highestIndex, selectedIndex + 1)
values[focussedSegmentIndex] = Digit(rawValue: intValue)
focussedSegmentIndex = min(highestIndex, focussedSegmentIndex + 1)
}

public func deleteBackward() {
let fieldWasEmpty = values[selectedIndex] == nil
let fieldWasEmpty = values[focussedSegmentIndex] == nil

values[selectedIndex] = nil // Making sure the current field content is reset
values[focussedSegmentIndex] = nil // Making sure the current field content is reset

if selectedIndex < highestIndex || fieldWasEmpty {
selectedIndex = max(0, selectedIndex - 1)
values[selectedIndex] = nil // Making sure the newly selected field is reset
if focussedSegmentIndex < highestIndex || fieldWasEmpty {
let updatedFocussedSegmentIndex = max(0, focussedSegmentIndex - 1)
values[updatedFocussedSegmentIndex] = nil // Making sure the newly selected field is reset

// Setting the index after the change so the editing events are sent in the correct order
focussedSegmentIndex = updatedFocussedSegmentIndex
} else {
// We're not changing the index but want to trigger the .editingChanged event
let updatedFocussedSegmentIndex = focussedSegmentIndex
focussedSegmentIndex = updatedFocussedSegmentIndex
}
}
}
Expand Down Expand Up @@ -276,8 +296,8 @@ private extension CodeInputField {

func updateSegments() {
segments.enumerated().forEach { (index, segment) in
segment.isSelected = (index == selectedIndex)
segment.value = values[index]
segment.update(value: values[index],
isFocussed: index == focussedSegmentIndex)
}
}

Expand Down
11 changes: 9 additions & 2 deletions Sources/CodeInputField/CodeInputFieldSegment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ import UIKit

/// The minimal requirements for an input field segment
public protocol CodeInputFieldSegment: UIView {
var value: Digit? { get set }
var isSelected: Bool { get set }

/**
Update the value and focussed state

- parameters:
- value: The new value to show
- isFocussed:flag indicating whether or not the segment should show as focussed
*/
func update(value: Digit?, isFocussed: Bool)
}
1 change: 1 addition & 0 deletions Sources/CodeInputField/Digit.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

/// Allowed input
public enum Digit: Int {
case one = 1
case two = 2
Expand Down

0 comments on commit fffb5b8

Please sign in to comment.