Search code examples
swiftnstextfieldnsnumberformatter

Swift: NSTextField numberStyle not calling action


I have a NSTextField that I have setup with the following code:

func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView?
{

    let cell : NSTableCellView? = tableView.makeViewWithIdentifier("AutomaticTableColumnIdentifier.0", owner: self) as! NSTableCellView?

    cell!.textField!.floatValue = 4.55
    let numberFormatter = NSNumberFormatter()
    numberFormatter.numberStyle = .CurrencyStyle
    numberFormatter.maximumFractionDigits = 0
    cell!.textField! = numberFormatter

    return cell
}

I have also set up an action set up for when the field is modified in the NSTextFieldDelegate.

@IBAction func saveCell(sender: AnyObject)
{
    print("saveCell")
}

My problem is that the saveCell action is only triggered when I type in a value that exactly matches the currency style. i.e. typing £45 is OK but nothing happens when I enter a plain 45.

If I remove the NSNumberFormatter then it will accept any entry, but doesn't format the textField in the way that I want it.

How can I accept the 45 value without the currency symbol? What's the best way of doing this?


Solution

  • You can subclass your NSTextField as follow:

    import Cocoa
    
    class CurrencyField: NSTextField {
    
        var percent: Double {
            return Double(Int(numbers) ?? 0) / 100
        }
    
        var currency: String {
            return Number.currency.string(from: percent.number) ?? ""
        }
    
        var numbers: String {
            return stringValue.components(separatedBy: CharacterSet(charactersIn: "0123456789").inverted).joined()
        }
    
        override func awakeFromNib() {
            super.awakeFromNib()
            alignment = .right
            stringValue = currency
    
        }
    
        override func textDidChange(_ notification: Notification) {
            Swift.print("--> textDidChange")
            stringValue = currency
        }
    
        override func draw(_ dirtyRect: NSRect) {
            super.draw(dirtyRect)
            // Drawing code here.
        }
    }
    

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

    struct Number {
        static let currency = NumberFormatter(numberStyle: .currency)
    }
    

    extension Double {
        var number: NSNumber {
            return NSNumber(value: self)
        }
    }
    

    Edit/Update:

    If you need an Integer Field you can do as Follow:

    import Cocoa
    
    class IntegerField: NSTextField {
    
        var value: Int { return Int(numbers) ?? 0 }
    
        var currency: String { return Number.currency.string(from: value.number) ?? "" }
    
        var numbers: String { return stringValue.components(separatedBy: CharacterSet(charactersIn: "0123456789").inverted).joined() }
    
        override func awakeFromNib() {
            super.awakeFromNib()
            alignment = .right
            stringValue = currency
        }
        override func textDidChange(_ notification: Notification) {
            Swift.print("--> textDidChange")
            stringValue = currency
        }
        override func draw(_ dirtyRect: NSRect) {
            super.draw(dirtyRect)
            // Drawing code here.
        }
    }
    
    extension NumberFormatter {
        convenience init(numberStyle: NumberFormatter.Style, maximumFractionDigits: Int = 0) {
            self.init()
            self.numberStyle = numberStyle
            self.maximumFractionDigits = maximumFractionDigits
    
        }
    }
    
    struct Number {
        static let currency = NumberFormatter(numberStyle: .currency)
    }
    
    extension Int {
        var number: NSNumber {
            return NSNumber(value: self)
        }
    }