Skip to content

Commit 2631acd

Browse files
author
Ahmet Sina Ustem
committed
Added Initial files
1 parent 460863c commit 2631acd

39 files changed

Lines changed: 2100 additions & 1 deletion

ALFormInput.podspec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@ TODO: Add long description of the pod here.
3838

3939
# s.public_header_files = 'Pod/Classes/**/*.h'
4040
# s.frameworks = 'UIKit', 'MapKit'
41-
# s.dependency 'AFNetworking', '~> 2.3'
41+
s.dependency 'SkyFloatingLabelTextField', '~> 3.0'
42+
s.dependency 'PhoneNumberKit', '~> 3.1'
4243
end
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by AppLogist on 10.04.2020.
6+
//
7+
8+
import UIKit
9+
10+
public class ALTextFieldConfig {
11+
static let shared = ALTextFieldConfig()
12+
13+
// MARK: - Fonts
14+
public var titleFont : UIFont = UIFont.systemFont(ofSize: 13, weight: .semibold)
15+
16+
public var placeholderFont: UIFont = UIFont.systemFont(ofSize: 13)
17+
18+
public var isTitleVisible: Bool = true
19+
20+
public var tintColor: UIColor = .systemBlue
21+
22+
public var textColor: UIColor = .systemGray
23+
24+
public var titleColor: UIColor {
25+
if #available(iOS 13.0, *) {
26+
return .label
27+
} else {
28+
return UIColor.lightGray
29+
}
30+
}
31+
32+
public var placeholderColor: UIColor = .lightGray
33+
34+
public var lineColor: UIColor = .systemGray
35+
36+
public var selectedTitleColor: UIColor = .systemBlue
37+
38+
public var selectedLineColor: UIColor = .systemBlue
39+
40+
// MARK: - Error Colors
41+
42+
public var errorColor: UIColor = .systemRed
43+
44+
public var textErrorColor: UIColor? {
45+
errorColor
46+
}
47+
48+
public var lineErrorColor: UIColor? {
49+
errorColor
50+
}
51+
52+
public var titleErrorColor: UIColor? {
53+
errorColor
54+
}
55+
56+
// MARK: - TextField UIControl Event
57+
58+
public var validateEventType: UIControl.Event = .editingDidEnd
59+
60+
61+
public init () {}
62+
}
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
2+
import UIKit
3+
import SkyFloatingLabelTextField
4+
import PhoneNumberKit
5+
6+
public class ALValidatableTextField: SkyFloatingLabelTextField {
7+
8+
private let validator = Validator()
9+
private var config = ALTextFieldConfig()
10+
private var type = ALValidatableTextFieldType.optional
11+
12+
private lazy var phoneNumberKit = PhoneNumberKit()
13+
private lazy var phoneFormatter = PartialFormatter()
14+
15+
// 3. And another stored property which will only be accessible in IB (because the "unavailable" attribute prevents its use in code)
16+
@available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'shape' instead.")
17+
@IBInspectable var textFieldType: String? {
18+
willSet {
19+
// Ensure user enters a valid shape while making it lowercase.
20+
// Ignore input if not valid.
21+
if let newShape = ALValidatableTextFieldType(rawValue: newValue?.lowercased() ?? "") {
22+
type = newShape
23+
}
24+
}
25+
didSet {
26+
setConfig(type)
27+
}
28+
}
29+
30+
// MARK: - Properties
31+
private var padding: UIEdgeInsets? {
32+
didSet {
33+
setNeedsDisplay()
34+
}
35+
}
36+
37+
var paddingNeeded: Bool = false {
38+
didSet {
39+
if paddingNeeded {
40+
addTarget(self, action: #selector(setPaddingForEditingTextFields), for: .editingChanged)
41+
} else {
42+
removeTarget(self, action: #selector(setPaddingForEditingTextFields), for: .editingChanged)
43+
}
44+
}
45+
}
46+
47+
public init(rules: [Rule], config: ALTextFieldConfig) {
48+
super.init(frame: .zero)
49+
self.config = config
50+
validator.registerField(self, rules: rules)
51+
}
52+
53+
public override init(frame: CGRect) {
54+
super.init(frame: frame)
55+
// Common init
56+
setupDefaultLook()
57+
}
58+
59+
public required init?(coder: NSCoder) {
60+
super.init(coder: coder)
61+
// Common init
62+
setupDefaultLook()
63+
}
64+
65+
66+
public func setConfig(_ type: ALValidatableTextFieldType = .optional,
67+
_ config: ALTextFieldConfig = ALTextFieldConfig(),
68+
rules: [Rule] = [],
69+
validateWhileTyping: Bool = false
70+
) {
71+
self.config = config
72+
self.type = type
73+
let tmpRules = rules.isEmpty ? type.rules : rules
74+
keyboardType = type.keyboardType
75+
textContentType = type.contentType
76+
isSecureTextEntry = type == .password
77+
title = type.title
78+
placeholder = type.placeholder
79+
if let maxLength = type.maxLength {
80+
self.maxLength = maxLength
81+
}
82+
if type == .phoneNumber {
83+
addTarget(self, action: #selector(formatPhoneNumber(_:)), for: .editingChanged)
84+
}
85+
if type == .creditCardNumber {
86+
addTarget(self, action: #selector(formatCreditCardString(_:)), for: .editingChanged)
87+
}
88+
validator.registerField(self, rules: tmpRules)
89+
}
90+
91+
92+
private func setupDefaultLook() {
93+
titleFont = config.titleFont
94+
placeholderFont = config.placeholderFont
95+
setTitleVisible(config.isTitleVisible)
96+
tintColor = config.tintColor
97+
textColor = config.textColor
98+
placeholderColor = config.placeholderColor
99+
lineColor = config.lineColor
100+
selectedTitleColor = config.selectedTitleColor
101+
selectedLineColor = config.selectedLineColor
102+
titleColor = config.titleColor
103+
104+
errorColor = config.errorColor
105+
textErrorColor = config.textErrorColor
106+
lineErrorColor = config.lineErrorColor
107+
titleErrorColor = config.titleErrorColor
108+
addTarget(self, action: #selector(resignFirstResponder), for: .editingDidEndOnExit)
109+
addTarget(self, action: #selector(textFieldValidateAction(_:)), for: config.validateEventType)
110+
setupValidator()
111+
}
112+
113+
@objc private func formatCreditCardString(_ textField: UITextField) {
114+
let creditCardString = textField.text ?? ""
115+
let trimmedString = creditCardString.components(separatedBy: .whitespaces).joined()
116+
117+
let arrOfCharacters = Array(trimmedString)
118+
var modifiedCreditCardString = ""
119+
120+
if !arrOfCharacters.isEmpty {
121+
for index in 0...arrOfCharacters.count-1 {
122+
modifiedCreditCardString.append(arrOfCharacters[index])
123+
if (index+1) % 4 == 0 && index+1 != arrOfCharacters.count {
124+
modifiedCreditCardString.append(" ")
125+
}
126+
}
127+
}
128+
textField.text = modifiedCreditCardString
129+
}
130+
131+
@objc private func formatPhoneNumber(_ textField: SkyFloatingLabelTextField) {
132+
self.text = phoneFormatter.formatPartial(self.text ?? "")
133+
}
134+
135+
@objc private func textFieldValidateAction(_ textfield: UITextField) {
136+
guard let textField = textfield as? ALValidatableTextField else { return }
137+
138+
validator.validateField(textField){ error in
139+
if error == nil {
140+
dump("Success")
141+
textField.errorMessage = nil
142+
} else {
143+
dump("Error occurred")
144+
// Validation error occurred
145+
}
146+
}
147+
}
148+
149+
private func setupValidator() {
150+
validator.styleTransformers(success: { (validationRule) in
151+
if let textField = validationRule.field as? SkyFloatingLabelTextField {
152+
if textField.errorMessage == nil {
153+
return
154+
}
155+
self.titleLabel.alpha = 0.0
156+
UIView.animate(withDuration: 0.4) {
157+
self.titleLabel.alpha = 1.0
158+
textField.errorMessage = nil
159+
}
160+
}
161+
162+
if let textField = validationRule.field as? UITextView {
163+
textField.layer.borderColor = self.config.errorColor.cgColor
164+
textField.layer.borderWidth = 0.5
165+
}
166+
}) { (validationError) in
167+
print(validationError.errorMessage)
168+
169+
if let textField = validationError.field as? SkyFloatingLabelTextField {
170+
if textField.errorMessage == validationError.errorMessage {
171+
textField.errorMessage = validationError.errorMessage
172+
return
173+
}
174+
self.titleLabel.alpha = 0.0
175+
UIView.animate(withDuration: 0.4) {
176+
self.titleLabel.alpha = 1.0
177+
textField.errorMessage = validationError.errorMessage
178+
}
179+
}
180+
if let textField = validationError.field as? UITextView {
181+
textField.layer.borderColor = self.config.errorColor.cgColor
182+
textField.layer.borderWidth = 1.0
183+
}
184+
}
185+
}
186+
187+
public func setFormattedPhoneNumber(_ phoneNumber: String) {
188+
if type != .phoneNumber {
189+
return
190+
}
191+
guard let parsed = try? phoneNumberKit.parse(phoneNumber) else {
192+
return
193+
}
194+
let formattedPhoneNumber = phoneNumberKit.format(parsed, toType: .national)
195+
text = formattedPhoneNumber
196+
}
197+
198+
public func get(_ phoneNumber: String, for region: String = "TR") -> String? {
199+
if type != .phoneNumber { return nil }
200+
do {
201+
let formatted = phoneFormatter.formatPartial(text ?? "")
202+
let phoneNumber = try phoneNumberKit.parse(formatted, withRegion: region, ignoreType: true)
203+
return "\(phoneNumber.nationalNumber)"
204+
} catch {
205+
print("Generic parser error")
206+
}
207+
return nil
208+
}
209+
210+
211+
func validationSuccessful() {
212+
// submit the form
213+
}
214+
215+
func validationFailed(_ errors:[(Validatable ,ValidationError)]) {
216+
// turn the fields to red
217+
for (field, error) in errors {
218+
if let field = field as? UITextField {
219+
field.layer.borderColor = config.errorColor.cgColor
220+
field.layer.borderWidth = 1.0
221+
}
222+
error.errorLabel?.text = error.errorMessage // works if you added labels
223+
error.errorLabel?.isHidden = false
224+
}
225+
}
226+
227+
/// Gets called when editing starts on a paddingNeeded enabled textfield
228+
@objc private func setPaddingForEditingTextFields() {
229+
let editingPad = UIEdgeInsets(top: 8, left: 0, bottom: -8, right: 0)
230+
!(text?.isEmpty ?? true) ? setPadding(editingPad) : setPadding()
231+
}
232+
233+
/// Default padding is --> top: 4, left: 0, bottom: 4, right: 0
234+
private func setPadding(_ padding: UIEdgeInsets = UIEdgeInsets(top: 4, left: 0, bottom: 4, right: 0)) {
235+
self.padding = padding
236+
}
237+
}
238+
239+
extension ALValidatableTextField: UITextFieldDelegate {
240+
public func textFieldDidEndEditing(_ textField: UITextField) {
241+
//
242+
}
243+
}
244+
245+
// MARK: - TextField Padding
246+
extension ALValidatableTextField {
247+
248+
override open func textRect(forBounds bounds: CGRect) -> CGRect {
249+
paddingNeeded ? bounds.inset(by: padding ?? .zero) : super.textRect(forBounds: bounds)
250+
}
251+
252+
override open func placeholderRect(forBounds bounds: CGRect) -> CGRect {
253+
paddingNeeded ? bounds.inset(by: padding ?? .zero) : super.textRect(forBounds: bounds)
254+
}
255+
256+
override open func editingRect(forBounds bounds: CGRect) -> CGRect {
257+
paddingNeeded ? bounds.inset(by: padding ?? .zero) : super.textRect(forBounds: bounds)
258+
}
259+
}

0 commit comments

Comments
 (0)