speg
speg

Reputation: 2019

How to override NumberFormatter string and number methods

NumberFormatter has a couple methods, number and string which seem to imply they are responsible for converting the value to and from each type. However, when trying to override these methods I can't get them to fire.. (the print statements are never seen).

Am I missing something? The only progress I made was with the getObjectValue(_:for:range:) method in setting the numerical value, but never the string.

import SwiftUI

class NumberProxy : NumberFormatter {
    override func string(from number: NSNumber) -> String? {
        print("hello from", number)
        return "HELLO!"
    }

    override func number(from string: String) -> NSNumber? {
        print("to number...", string)
        return NSNumber(value: 123)
    }
    
//    override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, range rangep: UnsafeMutablePointer<NSRange>?) throws {
//        try super.getObjectValue(obj, for: string, range: rangep)
//        print(obj)
//        obj?.pointee = NSNumber(value: 4.0) // this worked
//    }
}

struct Test: View {
    @State private var myNumber: Int = 0
            
    var body : some View {
        TextField("Current Balance", value: $myNumber, formatter: NumberProxy())
                
    }
}

Upvotes: 0

Views: 449

Answers (1)

Leo Dabus
Leo Dabus

Reputation: 236498

What you need to override is Formatter's string(for:) method:

override func string(for obj: Any?) -> String? {

class NumberProxy: NumberFormatter {
    override func string(for obj: Any?) -> String? {
        guard let value = (obj as? NSNumber)?.intValue else { return nil }
        return .init(value)
    }
}

let number = NSNumber(value: 123.456)

let string = NumberProxy().string(from: number)  // "123"

edit/update:

To input Double as integers:

override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, range rangep: UnsafeMutablePointer<NSRange>?) throws {
    try super.getObjectValue(obj, for: string, range: rangep)
    obj?.pointee = Int(string.filter(\.isWholeNumber)) as AnyObject?
}

let integer = NumberProxy().number(from: "123.45")!.intValue  // 12345

Upvotes: 3

Related Questions