0

I want to restrict what is entered into a UITextView to Doubles, each Double separated by a space. Of course this means that only one decimal point is allowed.

The following code removes letters and symbols, but does not work for decimals. After entering a decimal point, the next character typed deletes the decimal point.

What am I doing wrong???

import UIKit class ViewController: UIViewController, UITextViewDelegate { @IBOutlet weak var dataInputField: UITextView! var currentEntryHasDecimalPoint:Bool = false var validChars: Set<Character> = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", " "] override func viewDidLoad() { super.viewDidLoad() dataInputField.delegate = self } func textViewDidChange(_ textView: UITextView ) { if let str = dataInputField.text, !str.isEmpty { let validChar:Bool = Set(str).isSubset(of: validChars) if validChar { let newChar = str.last! switch newChar { case ".": currentEntryHasDecimalPoint = true validChars.remove(".") case " ": currentEntryHasDecimalPoint = false validChars.insert(".") default: print("default") } } else { dataInputField.deleteBackward() } } } } 
6
  • You are assuming the user is simply typing single characters. What happens if they copy and paste? Also this will have a rather annoying effect for the user to type a character only to have it deleted. You might be better to look at textView(shouldChangetextIn:replacementText:). As a matter of style, textViewDidChange already passes the textView as a parameter, why not use that rather than a class field member? Commented Jan 7, 2019 at 4:16
  • I suggest you use textView(_:shouldChangeTextIn:replacementText:) instead of textViewDidChange Commented Jan 7, 2019 at 6:13
  • Had't got to copy and paste yet, just trying to figure this one out. Deleting the offending character happens so quickly that it simply appears to just be ignored. When typing a letter into a list of numbers, the user should not really expect a letter to be allowed. Commented Jan 10, 2019 at 17:48
  • @Zonily Jame, can you show me how to use that? (I am very new to Swift.) Commented Jan 10, 2019 at 17:49
  • This so answer and this so answer can help you Commented Jan 11, 2019 at 6:38

1 Answer 1

1

Since deleteBackward() does make change to the textView, it also trigger textViewDidChange. As "." has already been removed from validChars when you input "." followed by a letter, the letter will be deleted and trigger textViewDidChange, then the "." will be deleted. You should validate the text using the Regular Expression /^(?:[0-9]+(?:\.$|\.[0-9]+)?(?:\s+|\s*$))+$/ instead of using Set operations.

func textViewDidChange(_ textView: UITextView ) { if let str = textView.text, !str.isEmpty, let regex = try? NSRegularExpression(pattern: "^(?:[0-9]+(?:\\.$|\\.[0-9]+)?(?:\\s+|\\s*$))+$", options: []), regex.numberOfMatches(in: str, options: [], range: NSRange(location: 0, length: str.count)) == 0 { textView.deleteBackward() } } 
Sign up to request clarification or add additional context in comments.

6 Comments

This solution will fail in many cases. It doesn't handle a user pasting text in the middle of the text view. It assumes a single character caused the change.
Yes this is just to answer OP why that "unexpected" situation happens and how to fix it. Of course OP should choose a completely different solution.
So, textFieldDidChange does not trigger itself when a change is made programmatically, but textViewDidChange does? Interesting. ..
Good point to everyone who mentioned it, what would be a good way to handle it for cut and paste?
@Ricky Mo, this doesn't work. Decimals do not show up at all. Also, can you refer me to a good place to learn how to use Regex?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.