how to add a string after each comma in UITextfield - swift

Im lookin a way to add # at the beginig of each word written in uiTextField with swift, i tryes to check using this code
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField.text!.first != "#" {
print(textField.text!.first)
print(textField.text!)
textField.text = ""
}
}
but the firsrt character is nil when the input on keyboard is # so what should be the way to achive this having all the words begins with # and separated by ,

You can make it easier checking the text after editing changed control event and clean your string when the user types a space after each word. You can subclass your UITextField and it should look something like this:
class TagsField: UITextField, UITextFieldDelegate {
override func didMoveToSuperview() {
delegate = self
keyboardType = .alphabet
autocapitalizationType = .none
autocorrectionType = .no
addTarget(self, action: #selector(editingChanged), for: .editingChanged)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
prepareString()
if text!.hasSuffix(", #") { text!.removeLast(3) } // clean the residue on end
resignFirstResponder()
return false
}
func prepareString() {
text = text!.components(separatedBy: CharacterSet.letters.inverted) // filtering non letters and grouping words
.filter{!$0.isEmpty} // filtering empty components
.map{ "#" + $0 + ", " } // add prefix and sufix to each word and append # to the end of the string
.string + "#"
}
override func deleteBackward() {
let _ = text!.popLast() // manually pops the last character when deliting
}
#objc func editingChanged(_ textField: UITextField) {
if text!.last == " " {
prepareString()
} else if !text!.hasPrefix("#") { // check if the first word being typed has the # prefix and add it if needed.
text!.insert("#", at: text!.startIndex)
}
}
}
extension Collection where Element: StringProtocol {
var string: String {
return String(joined())
}
}

Related

How to not add to textfield when the input character is already found in the text

How could you get a TextField to only append to the text variable when the input is not already present?
So if you had already typed "abc" and you tried to type 'a' again, it wouldn't do anything
Starting point:
TextField("list of characters", text: $characterSequence)
.onReceive{
}
You can check strings like this and input only a new char.
Note: This code is a case-sensitive input string.
TextField("list of characters", text: $characterSequence)
.onReceive(Just(characterSequence), perform: { char in
print(char)
let oldString = characterSequence.dropLast()
if let last = characterSequence.last, oldString.contains(last) {
characterSequence = String(characterSequence.dropLast())
}
})
For, without a case-sensitive input string.
TextField("list of characters", text: $characterSequence)
.onReceive(Just(characterSequence), perform: { char in
print(char)
let oldString = characterSequence.dropLast()
if let last = characterSequence.last, oldString.uppercased().contains(last.uppercased()) {
characterSequence = String(characterSequence.dropLast())
}
})
Edit
For the disabled characters in the middle, use UITextField with UIViewRepresentable. This one is also handled copy-past to the text field.
Here is solution
struct UniqueTextField: UIViewRepresentable {
private var placeholder: String?
#Binding private var text: String
init(_ placeholder: String? = nil, text: Binding<String>) {
_text = text
self.placeholder = placeholder
}
func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.placeholder = placeholder
textField.text = text
textField.delegate = context.coordinator
return textField
}
func updateUIView(_ uiView: UITextField, context: Context) {
uiView.text = text
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
class Coordinator: NSObject, UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
/**
// case-sensitive
string.filter({(textField.text ?? "").contains($0)}).isEmpty
*/
//without case-sensitive
string.filter({(textField.text?.uppercased() ?? "").contains($0.uppercased())}).isEmpty
}
}
}
Usage:
UniqueTextField("list of characters", text: $characterSequence)

UITextLabel updating in different order during backspace of UITextField than other entries.

Okay so I have a UITextLabel that is being updated to whatever and whenever UITextField is changed.
So I have the following method.
func textField(_ textField: UITextField, shouldChangeCharactersInrange: NSRange, replacementString string: String) -> Bool {
payment_amount_label.text = payment_amount_tf.text!
return true
}
and I have the textfield set up as a currency field. here is the code for that.
class CurrencyField: UITextField {
var string: String { return text ?? "" }
var decimal: Decimal {
return string.digits.decimal /
Decimal(pow(10, Double(Formatter.currency.maximumFractionDigits)))
}
var decimalNumber: NSDecimalNumber { return decimal.number }
var doubleValue: Double { return decimalNumber.doubleValue }
var integerValue: Int { return decimalNumber.intValue }
let maximum: Decimal = 9_999.99
private var lastValue: String?
override func willMove(toSuperview newSuperview: UIView?) {
// you can make it a fixed locale currency if needed
// Formatter.currency.locale = Locale(identifier: "pt_BR") // or "en_US", "fr_FR", etc
addTarget(self, action: #selector(editingChanged), for: .editingChanged)
keyboardType = .numberPad
textAlignment = .right
editingChanged()
}
override func deleteBackward() {
text = string.digits.dropLast().string
print("inside method delete" + text!)
editingChanged()
//backspace not working for editingchanged. not sure why.
}
#objc func editingChanged(_ textField: UITextField? = nil) {
guard decimal <= maximum else {
text = lastValue
return
}
text = Formatter.currency.string(for: decimal)
lastValue = text
print("inside method editing" + text! + "last value is " + lastValue!)
return
}
}
extension NumberFormatter {
convenience init(numberStyle: Style) {
self.init()
self.numberStyle = numberStyle
}
}
extension Formatter {
static let currency = NumberFormatter(numberStyle: .currency)
}
extension String {
var digits: [UInt8] {
return map(String.init).compactMap(UInt8.init)
}
}
extension Collection where Iterator.Element == UInt8 {
var string: String { return map(String.init).joined() }
var decimal: Decimal { return Decimal(string: string) ?? 0 }
}
extension Decimal {
var number: NSDecimalNumber { return NSDecimalNumber(decimal: self) }
}
When I enter in numbers into the keypad everything updates correctly. However, when I hit backspace the textfield keeps the old value. I put in some print statements to see what was happening and when backspace is hit the UITextLabel is being updated before the delete happens inside the CurrencyField code. When a number is pressed the UITextLabel is updated AFTER the the CurrencyField code. I have no idea how to fix this issue any help is appreciated.
Try below:
Add a custom method for change in value to UITextField same as UITextView.
In viewDidLoad:
payment_amount_tf.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
Declare textFieldDidChange as:
#objc func textFieldDidChange(_ sender: UITextField) {
payment_amount_label.text = payment_amount_tf.text
}

Save the last letter when typing in a textField

I have this function in my application:
func typingName(textField:UITextField){
if let typedText = textField.text {
tempName = typedText
print(tempName)
}
}
In viewDidLoad() I have written this:
textField.addTarget(self, action: #selector(typingName), for: .editingChanged)
All works good, but I want to save only the letter typed by the user.
With this function if I write "hello" it prints:
"h"
"he"
"hel"
"hell"
"hello".
Instead, I want to have this:
"h"
"e"
"l"
"l"
"o".
If you want to get last character which user entered with keyboard.
You can detect it with delegate method of UITextField as shown in below code:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tfName: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
//Need to confirm delegate for textField here.
tfName.delegate = self
}
//UITextField Delegate Method
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//This will print every single character entered by user.
print(string)
return true
}
}
For any Swift String, You could get the latest letter from a string like this:
let myString = "Hello, World"
let lastCharacter = myString.characters.last // d
Note that the data type of lastCharacter is Character? (optional character), you might want to do it as optional binding:
let myString = "Hello, World"
if let lastCharacter = myString.characters.last {
print(lastCharacter) // d
}
Since you are listening to editingChanged event, all you have to do in your typingName function is:
func typingName(textField:UITextField){
if let typedText = textField.text {
tempName = typedText
print(tempName)
if let lastCharacter = tempName.characters.last {
print(lastCharacter)
}
}
}

Switching to next UITextField when character limit is reached

I have three text fields to enter in a phone number. I am trying to set the character limit for each textfield to three characters and once this is reached switch to a new textfield.
I saw online to use this code to limit the characters:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentCharacterCount = textField.text?.characters.count ?? 0
if (range.length + range.location > currentCharacterCount){
return false
}
let newLength = currentCharacterCount + string.characters.count - range.length
return newLength <= 25
}
and to use this to switch to typing on a new textfield:
.didbecomefirstresponder()
but I am not sure how to limit a text field to 3 characters and then switch to the next field.
This is my code, how I solve this:
The three textfields are:
#IBOutlet var txtField1: UITextField!
#IBOutlet var txtField2: UITextField!
#IBOutlet var txtField3: UITextField!
import UITextFieldDelegate in to your class and set the delegate to self for all the textfields.
And use this method to change focus.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentCharacterCount = ((textField.text?.characters.count)! + string.characters.count) - 1
switch (textField, currentCharacterCount) {
case (self.txtField1, 3):
self.txtField2.becomeFirstResponder()
case (self.txtField2, 7):
self.txtField3.becomeFirstResponder()
default:
break
}
return true
}
Here I set character count 3 for the first textfield, and 7 for second textfield.
You could use the delegate method for UITextField shouldChangeCharactersInRange. You'd have to do a little setup work though. Here's an example that creates 3 textFields, conforms to the UITextFieldDelegate, and does something similar to what you're describing.
class ViewController: UIViewController, UITextFieldDelegate {
var max = 3
var fields:[UITextField] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// This is just an example to layout 3 text fields.
for i in 0 ..< 3 {
let field = UITextField()
view.addSubview(field)
field.delegate = self
field.borderStyle = .roundedRect
field.font = UIFont(name: "AvenirNext-Regular", size: 15.0)
field.textColor = UIColor.black
field.frame = CGRect(x: view.bounds.width/2 - 100, y: 100 + (150*CGFloat(i)), width: 200, height: 50)
fields.append(field)
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Make sure the field has non-nil text
if let text = textField.text {
// Check if the text length has reached max limit
if text.characters.count > (max-1) {
// Get index of field from fields array
if let index = fields.index(of: textField) {
// Make sure it's not the last field in the array else return false
if index < fields.count-1 {
// Make the next field in the array become first responder if it's text is non-nil and it's text's character count is less than desired max
if fields[index+1].text != nil && fields[index+1].text!.characters.count < (max-1) {
fields[index+1].becomeFirstResponder()
} else {
return false
}
} else {
return false
}
}
}
}
return true
}
}
I find Pierce's answers too complex and anas.p's answer doesn't work for me like it's supposed to. My Swift 5 solution:
override func viewDidLoad() {
textField1.addTarget(self, action: #selector(textFieldDidChange1), for:.editingChanged)
textField2.addTarget(self, action: #selector(textFieldDidChange2), for:.editingChanged)
}
#objc func textFieldDidChange1() {
// After 2 characters are typed, immediately jump to textField2
if (textField1.text?.count == 2) {
self.textField2.becomeFirstResponder()
}
}
#objc func textFieldDidChange2() {
if (textField2.text?.count == 2) {
self.textField3.becomeFirstResponder()
}
}

Prevent special characters in UITextField

How can I prevent the user from entering special characters in UITextField?
I solved the problem using this code:
let validString = NSCharacterSet(charactersInString: " !##$%^&*()_+{}[]|\"<>,.~`/:;?-=\\¥'£•¢")
// restrict special char in test field
if (textField == self.txt_firstName || textField == self.txt_lastName)
{
if let range = string.rangeOfCharacterFromSet(validString)
{
print(range)
return false
}
else
{
}
}
Swift 4.2
for emoji and special character
override func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField.isFirstResponder {
let validString = CharacterSet(charactersIn: " !##$%^&*()_+{}[]|\"<>,.~`/:;?-=\\¥'£•¢")
if (textField.textInputMode?.primaryLanguage == "emoji") || textField.textInputMode?.primaryLanguage == nil {
return false
}
if let range = string.rangeOfCharacter(from: validString)
{
print(range)
return false
}
}
return true
}
One more answer with default CharacterSet
Restrict all Special characters and also, this will support if any string you dont want to restrict.
extension String {
var containsValidCharacter: Bool {
guard self != "" else { return true }
let noNeedToRestrict = CharacterSet(charactersIn: " _") // NOT RESTRICT "Underscore and Space"
if noNeedToRestrict.containsUnicodeScalars(of: self.last!) {
return true
} else {
return CharacterSet.alphanumerics.containsUnicodeScalars(of: self.last!)
}
}
}
extension CharacterSet {
func containsUnicodeScalars(of character: Character) -> Bool {
return character.unicodeScalars.allSatisfy(contains(_:))
}
}
Usage:
extension ViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return string.containsValidCharacter
}
}
Here's an example of how you can allow users to only type alphanumeric characters using RxSwift.
Add the RxSwift pod
pod 'RxSwift', '~> 5'
Create a String extension to remove characters in a set
extension String {
func removeChars(in set: CharacterSet) -> String {
let filtered = self.unicodeScalars.filter { (scalarElement) -> Bool in
if (set.contains(scalarElement)) {
return false
}
return true
}
let trimmed = String(filtered.map({ (scalar) -> Character in
return Character(scalar)
}))
return trimmed
}
}
Use the RxSwift extension to remove invalid chars when the text changes
import UIKit
import RxSwift
class MyViewController: UIViewController {
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
setUpView()
}
private func setUpView() {
//Do view set up stuff here
//Use the extension here to listen to text changes and remove chars
myTextField.rx.text
.asDriver(onErrorJustReturn: "")
.drive(onNext: { [weak self] (text: String?) in
//Here you would change the character set to what you need
let newText = text?.removeChars(in: CharacterSet.alphanumerics.inverted)
self?.myTextField.text = newText
})
.disposed(by: disposeBag)
}
}
try with this
self.DataTblView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: false)