0

I've got this textfield function that limits the length to 3. One of the two text fields this goes to allows a minus sign "-".

If I type numerals with the numberpad they stop at 3 characters.
If I type a "-" with numerals behind it, they stop at 3 characters.

But,

If I type only "-"'s they'll go on longer than I care to admit.

So, if I combine character sets to include decimal digits and whatever set the minus comes in----would that allow me to limit the textfields to a "singe" minus and a max of 3 numerals?

  • Resuming. Are you trying to create an integer field and would like to limit their input from -999 up to 999? – Leo Dabus Aug 22 '20 at 03:23
  • yes, that would be good. I neglected to include that. Only one textfield allows the minus. so it's only capable (correctly) to go from 0 to 999. The second field allows the minus and can give effectively -999 to 999. they are just dollars as Integers. The reference they go to doesn't deal with decimals in this case. In my second example, I should refine: if I type "- 2 3 4 5" it only produces "-23"---three characters. –  Aug 22 '20 at 03:28

2 Answers2

0

For your unsigned text field you can use the same approach shown in this post. For you will need to do some changes to allow negative input as well. First add a toolbar to allow the user to switch positive/negative input and use a range instead of a maximum value. You don't need to check how many digits the user is entering just make sure the value entered is contained in that range. To allow the user to finish editing the field with a return key you can make the field its delegate and implement textFieldShouldReturn method. Last but not least when filtering the digits make sure you don't remove the minus sign prefix when present:


Your SignedIntegerField should look like this:

import UIKit

class SignedIntegerField: UITextField, UITextFieldDelegate {
    var value: Int { string.digits.integer ?? 0 }
    var range = -999...999
    private var lastValue: Int = 0
    override func willMove(toSuperview newSuperview: UIView?) {
        precondition(range ~= 0)
        
        delegate = self
        
        addToolbar()
        
        addTarget(self, action: #selector(editindDidEnd), for: .editingDidEnd)
        addTarget(self, action: #selector(editingChanged), for: .editingChanged)
        keyboardType = .numberPad
        textAlignment = .right
        sendActions(for: .editingChanged)
    }
    
    func addToolbar() {
        let toolbar = UIToolbar()
        toolbar.sizeToFit()
        toolbar.barStyle = .default
        toolbar.items = [
            .init(title: "+/-",
                  style: .plain,
                  target: self,
                  action: #selector(minusAction)),
            .init(barButtonSystemItem: .flexibleSpace,
                  target: self,
                  action: nil),
            .init(title: "Clear",
                  style: .plain,
                  target: self,
                  action: #selector(clearAction)),
            .init(barButtonSystemItem: .flexibleSpace,
                  target: self, action: nil),
            .init(title: "Done",
                  style: .plain,
                  target: self,
                  action: #selector(doneAction))]
        
        inputAccessoryView = toolbar
    }

    @objc func minusAction() {
        if text!.hasPrefix("-") {
            text!.removeFirst()
        } else {
            text!.insert("-", at: text!.startIndex)
        }
    }
    
    @objc func clearAction() { text = "0" }
    @objc func doneAction() {
        if text == "-" { text = "0" }
        resignFirstResponder()
    }

    override func deleteBackward() {
        text!.remove(at: text!.index(before: text!.endIndex))
        sendActions(for: .editingChanged)
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        resignFirstResponder()
        return true
    }
    @objc func editingChanged() {
        guard range ~= value else {
            text = Formatter.decimal.string(for: lastValue)
            return
        }
        text = Formatter.decimal.string(for: value)
        print("Value:", value)
        lastValue = value
    }
    @objc func editindDidEnd() {
        if text == "-" { text = "0" }
    }
}

fileprivate extension UITextField {
    var string: String { text ?? "" }
}

fileprivate extension Formatter {
    static let decimal = NumberFormatter(numberStyle: .decimal)
}

fileprivate extension NumberFormatter {
    convenience init(numberStyle: Style) {
        self.init()
        self.numberStyle = numberStyle
    }
}

fileprivate extension StringProtocol where Self: RangeReplaceableCollection {
    var digits: Self {
        first == "-" ?
            "-" + dropFirst().filter("0123456789".contains) :
            filter("0123456789".contains)
    }
    var integer: Int? { Int(self) }
}

sample project

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
0

Thanks for all of that!

I had a toolbar already made and didn't want to change what I worked on.

So, my textfield were limited by range depending on which field it was.

The unsigned one was limited to length of 3. The signed was a length of 4---only if there was a "-".

It's (again) not the "swiftiest", but I made it work:

 var maxLength = 3   //checks to allow 3 digits in per1k and 4 digits in fecocollector
        if (textField == fecoCollector) && fecoCollector.text!.contains("-") {
             maxLength = 4
        } else {
        
            maxLength = 3
        }

and

my button on the toolbar (a negative button I made) runs this as the selector...calling the main math function at the end.

 @objc func preloadTheMinus() {
        

        let char: Character = "-" //something to compare against
        let fecoCollectorFilter = fecoCollector.text!
      
        
        minusCount = fecoCollectorFilter.filter { $0 == char }.count //count the -'s
        print("fecoCollector has \(minusCount) minus in it")

        if fecoCollector.text!.hasPrefix("-") {
            fecoCollector.text!.removeFirst()
                    } else {
            fecoCollector.text!.insert("-", at: fecoCollector.text!.startIndex)
                    
                }
       doTheRemainingMath()
    }