Created
June 11, 2020 13:01
-
-
Save amrit42087/98750b2e22b299368b07ed6e78d530a8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// BottomSheet.swift | |
// Squirrel-SwiftUI | |
// | |
// Created by Amritpal Singh on 24/05/20. | |
// Copyright © 2020 sidhu.com. All rights reserved. | |
// | |
import SwiftUI | |
class TextFieldFormatter: ObservableObject { | |
enum Formatter { | |
case expiry | |
case cardNumber | |
case non // No formatter is required | |
} | |
@Published var text = "" { | |
didSet { | |
if oldValue != text { | |
if text.count > characterLimit && oldValue.count <= characterLimit { | |
text = oldValue | |
} else { | |
text = formattedText() | |
} | |
} | |
} | |
} | |
let characterLimit: Int | |
let formaterType: Formatter | |
init(limit: Int = 5, type: Formatter = .non){ | |
characterLimit = limit | |
formaterType = type | |
} | |
func formattedText() -> String { | |
switch formaterType { | |
case .expiry: | |
return formattedExpiry() | |
case .cardNumber: | |
return formattedCardNumber() | |
case .non: | |
return text | |
} | |
} | |
func formattedExpiry() -> String { | |
let allDigits = self.text.digits | |
var formattedText = "" | |
if allDigits.count == 1 { | |
formattedText = "\(allDigits[0])" | |
} else if allDigits.count == 2 { | |
if let number = Int("\(allDigits[0])\(allDigits[1])"), | |
number <= 12 { | |
formattedText = "\(allDigits[0])\(allDigits[1])" | |
} else { | |
formattedText = "\(allDigits[0])" | |
} | |
} else if allDigits.count == 3 { | |
formattedText = "\(allDigits[0])\(allDigits[1])/\(allDigits[2])" | |
} else if allDigits.count >= 4 { | |
formattedText = "\(allDigits[0])\(allDigits[1])/\(allDigits[2])\((allDigits[3]))" | |
} | |
return formattedText | |
} | |
func formattedCardNumber() -> String { | |
let trimmedString = text.components(separatedBy: .whitespaces).joined() | |
let arrOfCharacters = Array(trimmedString) | |
var modifiedCreditCardString = "" | |
if(arrOfCharacters.count > 0) { | |
for i in 0...arrOfCharacters.count-1 { | |
modifiedCreditCardString.append(arrOfCharacters[i]) | |
if((i+1) % 4 == 0 && i+1 != arrOfCharacters.count) { | |
modifiedCreditCardString.append(" ") | |
} | |
} | |
} | |
return modifiedCreditCardString | |
} | |
} | |
extension String { | |
var digits: [Int] { | |
var result = [Int]() | |
for char in self { | |
if let number = Int(String(char)) { | |
result.append(number) | |
} | |
} | |
return result | |
} | |
func widthWithConstrainedHeight(height: CGFloat, font: UIFont = UIFont(name: "Avenir-normal", size: 14) ?? .systemFont(ofSize: 14)) -> CGFloat { | |
let constraintRect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: height) | |
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, | |
attributes: [NSAttributedString.Key.font: font], context: nil) | |
return boundingBox.width | |
} | |
} | |
struct CustomTextField: UIViewRepresentable { | |
class Coordinator: NSObject, UITextFieldDelegate { | |
@Binding var text: String | |
var didBecomeFirstResponder = false | |
var textChanged: ((_ text: String?) -> Void)? | |
var textColor: UIColor = UIColor.black | |
var didTapReturnButton: (() -> Void)? = nil | |
init(text: Binding<String>, | |
textColor: UIColor?, | |
didChangeText: ((_ text: String?) -> Void)?, | |
didTapReturnButton: (() -> Void)?) { | |
_text = text | |
self.textColor = textColor ?? .black | |
textChanged = didChangeText | |
self.didTapReturnButton = didTapReturnButton | |
} | |
@objc func didChangeText(_ textField: UITextField) { | |
DispatchQueue.main.async { | |
self.text = textField.text ?? "" | |
} | |
print("Text changed") | |
textChanged?(text) | |
} | |
func textFieldShouldReturn(_ textField: UITextField) -> Bool { | |
didTapReturnButton?() | |
return true | |
} | |
} | |
@Binding var text: String | |
@Binding var isFirstResponder: Bool | |
var placeholder: String = "Search" | |
var font: UIFont? = UIFont(name: "Avenir-normal", size: 17) | |
var placeholderFont: UIFont? = nil | |
var textColor: UIColor = UIColor.black | |
var keyboardType: UIKeyboardType = .numberPad | |
var returnType: UIReturnKeyType = .default | |
var textChanged: ((_ text: String?) -> Void)? = nil | |
var didTapReturnButton: (() -> Void)? = nil | |
func makeUIView(context: UIViewRepresentableContext<CustomTextField>) -> UITextField { | |
let textField = UITextField(frame: .zero) | |
textField.addTarget(context.coordinator, action: #selector(context.coordinator.didChangeText(_:)), for: .editingChanged) | |
textField.tintColor = textColor | |
textField.textColor = textColor | |
textField.keyboardType = keyboardType | |
textField.returnKeyType = returnType | |
textField.delegate = context.coordinator | |
textField.placeholder = placeholder | |
textField.clearButtonMode = .whileEditing | |
textField.font = font | |
if let font = placeholderFont { | |
let attributes = [ | |
NSAttributedString.Key.font : font | |
] | |
textField.attributedPlaceholder = NSAttributedString(string: placeholder, attributes:attributes) | |
} | |
return textField | |
} | |
func makeCoordinator() -> CustomTextField.Coordinator { | |
return Coordinator(text: $text, | |
textColor: textColor, | |
didChangeText: textChanged, | |
didTapReturnButton: didTapReturnButton) | |
} | |
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomTextField>) { | |
uiView.text = text | |
if isFirstResponder && !context.coordinator.didBecomeFirstResponder { | |
uiView.becomeFirstResponder() | |
context.coordinator.didBecomeFirstResponder = true | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment