2

I'm trying to make it so an NSTextField will only accept numbers and periods like 12.4 and 3.6 in a Mac app.

I feel like I'm getting pretty close after reviewing other SO questions, but I can't quite get it. The below code works except that it won't allow . characters. It returns true and doesn't beep at me when I type a . but it won't let the character appear in the field.

class decimalFormatter: NumberFormatter {

  override func isPartialStringValid(_ partialString: String, newEditingString newString: AutoreleasingUnsafeMutablePointer<NSString?>?, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {

    //Allows the text to be deleted
    if partialString.isEmpty {
      return true
    }

    //Check for #.# numbers
    let charSet = NSCharacterSet(charactersIn: "1234567890.").inverted

    if partialString.rangeOfCharacter(from: charSet) != nil{
      NSBeep()
      return false
    }else{
      return true
    }

  }
}

Any idea what I'm doing wrong?

Clifton Labrum
  • 13,053
  • 9
  • 65
  • 128

2 Answers2

2

I found a simpler way to do it. Inside controlTextDidChange I just did this:

let charSet = NSCharacterSet(charactersIn: "1234567890.").inverted
let chars = fieldDuration.stringValue.components(separatedBy: charSet)
fieldDuration.stringValue = chars.joined()

It works great!

Clifton Labrum
  • 13,053
  • 9
  • 65
  • 128
2

@Clifton Labrum solution is really great but it doesn't reduce the field to Decimal (#.#), you can stil put some inputs as 1.2.4 which would lead to an error when trying tu cast it to Float.

Here is a draft of an extension that worked fine for me ( In Swift 4 )

public override func controlTextDidChange(_ obj: Notification) {
    if let textfield = obj.object as? NSTextField,
        textfield == self.quantityTextField {

        var stringValue = textfield.stringValue

        // First step : Only '1234567890.' - @Clifton Labrum solution
        let charSet = NSCharacterSet(charactersIn: "1234567890.").inverted
        let chars = stringValue.components(separatedBy: charSet)
        stringValue = chars.joined()

        // Second step : only one '.'
        let comma = NSCharacterSet(charactersIn: ".")
        let chuncks = stringValue.components(separatedBy: comma as CharacterSet)
        switch chuncks.count {
        case 0:
            stringValue = ""
        case 1:
            stringValue = "\(chuncks[0])"
        default:
            stringValue = "\(chuncks[0]).\(chuncks[1])"
        }

        // replace string
        textfield.stringValue = stringValue

    }
}

This prevent multiple occurences of . , even if I know that's not the best algorithmic way to do this. For instance 1.2.4 becomes 1.2 when pasted, and by keyboard you can't add another .

Olympiloutre
  • 2,268
  • 3
  • 28
  • 38