Hi,
This involves four fields on a view. Three of the fields are controlled to present a currency value with the sum of two of the fields (paidFld and shippingFld) been put into a third (totalFld) field. This part works fine. I'm just including it for clarity purposes.
The fourth field (conditionFld) adds a % to the end of the input and the length of the input is restricted to 3 or less characters. This is handeled in the extension.
My question is, how to restrict the input of the conditionFld to be a number between 1 and 100? The keyboard type of this field is set to number pad.
Thanks for the help.
Tom
class ViewController: UIViewController
{
@IBOutlet weak var conditionFld: UITextField!
@IBOutlet weak var paidFld: UITextField!
@IBOutlet weak var shippingFld: UITextField!
@IBOutlet weak var totalField: UITextField!
let Condition_Field_Length = 3
override func viewDidLoad()
{
super.viewDidLoad()
hideKeyboard()
}
func convert(theField: UITextField)
{
let paidStr: String = theField.text!
let tempPaid = (paidStr as NSString).doubleValue
if theField.text! == ""
{
return
} else if let theDouble = convertCurrencyToDouble(input: theField.text!) {
theField.text = convertDoubleToCurrency(amount: theDouble)
} else {
theField.text = convertDoubleToCurrency(amount: tempPaid)
}
}
func convertCurrencyToDouble(input: String) -> Double?
{
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency
numberFormatter.locale = Locale.current
return numberFormatter.number(from: input)?.doubleValue
}
func convertDoubleToCurrency(amount: Double) -> String
{
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency
numberFormatter.locale = Locale.current
return numberFormatter.string(from: NSNumber(value: amount))!
}
@IBAction func conditionEditingBegin(_ sender: UITextField)
{
let theString: String = sender.text!
if sender.keyboardType == .numberPad
{
if theString != ""
{
let newString = String.init(theString.dropLast() )
sender.text = newString
}
}
}
@IBAction func conditionEditingEnded(_ sender: UITextField)
{
let theString: String? = sender.text
if sender.keyboardType == .numberPad
{
if theString != ""
{
if theString?.suffix(1) != "%"
{
sender.text = "\(theString!)%"
}
} else {
sender.text = ""
}
}
}
@IBAction func paidEditingEnded(_ sender: UITextField)
{
convert(theField: sender)
let paidStr: String = paidFld.text!
let shippingStr: String = shippingFld.text!
let tPaid = convertCurrencyToDouble(input: paidStr)
let tShip = convertCurrencyToDouble(input: shippingStr)
let theTotalText = (Double(tPaid ?? 0.0) + Double(tShip ?? 0.0))
if theTotalText != 0.0 {
totalField.text = convertDoubleToCurrency(amount: theTotalText)
} else {
totalField.text = ""
}
}
}
extension UIViewController
{
func hideKeyboard()
{
let tap: UITapGestureRecognizer = UITapGestureRecognizer(
target: self,
action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
@objc func dismissKeyboard()
{
view.endEditing(true)
}
}
extension ViewController: UITextFieldDelegate
{
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
// This is for the condition field
if textField.tag == 1
{
// How do I restrict the number values to be between 1 and 100
return (textField.text?.count ?? 0) + string.count - range.length <= Condition_Field_Length
} else {
// This if for the paid and shipping fields
guard let text = textField.text, let decimalSeparator = NSLocale.current.decimalSeparator else {
return true
}
var splitText = text.components(separatedBy: decimalSeparator)
let totalDecimalSeparators = splitText.count - 1
let isEditingEnd = (text.count - 3) < range.lowerBound
splitText.removeFirst()
// Check if it will exceed 2 dp
if splitText.last?.count ?? 0 > 1 && string.count != 0 && isEditingEnd
{
return false
}
// If there is already a decimalSeparator we don't want to allow further decimalSeparator
if totalDecimalSeparators > 0 && string == decimalSeparator
{
return false
}
// Keyboard is set to deciaml pad
return true
}
}
}
A few words of warning here:
If you check the validity of numbers use APIs that only support standard ASCII digits, like
, things will fail if the user enters non-ASCII digits (most notably, Eastern Arabic digits). A better option here is to useInt("123")
to parse the number.NumberFormatter
Preventing folks from typing a ‘wrong’ value can be quite annoying. For example, imagine I want to enter 15 but I accidentally transpose the digits. Depending on the specific environment it may be more convenient to go from 51 to 151 to 15, but that’ll fail with your setup. IMO a better UI is to accept anything in the field and then show a status value indicating whether the value is valid or not (and, if not, explaining the error).
As a side benefit, that code is much easier to write.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"