Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MeasurementFormatter to display feet and inches

I have been playing with MeasurementFormatter to try and display imperial lengths as 10'6" or 10 ft 6 in unsuccessfully. LengthFormatter does this correctly when isForPersonHeightUse is set to true, but it does not adapt to the user's locale well (i.e. countries where length is measured in metric, except for when referring to height). Is there any way to force this behaviour in a formatter?

EDIT This question is to determine how to choose the units for a measurement. I am able to choose feet or inches, but want to display fractional feet as inches as in: 6'3" instead of 6.25 ft.

like image 705
jjatie Avatar asked Feb 15 '17 14:02

jjatie


2 Answers

public struct LengthFormatters {

    public static let imperialLengthFormatter: LengthFormatter = {
        let formatter = LengthFormatter()
        formatter.isForPersonHeightUse = true
        return formatter
    }()

}

extension Measurement where UnitType : UnitLength {

    var heightOnFeetsAndInches: String? {
        guard let measurement = self as? Measurement<UnitLength> else {
            return nil
        }
        let meters = measurement.converted(to: .meters).value
        LengthFormatters.imperialLengthFormatter.string(fromMeters: meters)
    }

}

Example of using:

    let a = Measurement(value: 6.34, unit: UnitLength.feet)
    print(a.heightOnFeetsAndInches ?? "")

    let b = Measurement(value: 1.5, unit: UnitLength.feet)
    print(b.heightOnFeetsAndInches ?? "")

Will print:

6 ft, 4.08 in
1 ft, 6 in
like image 96
maslovsa Avatar answered Oct 26 '22 16:10

maslovsa


I modified (simplified) @maslovsa's answer to meet my needs. I have a Core Data object called "Patient". It has a height parameter in inches that is an Int64. I want a string that I display to the user, so here's my property on my patient object for doing so:

var heightInFeetString : String {
    let measurement = Measurement(value: Double(self.height) / 12.0, unit: UnitLength.feet)
    let meters = measurement.converted(to: .meters).value
    return LengthFormatter.imperialLengthFormatter.string(fromMeters: meters)
}

Of course, I had to implement the imperialLengthFormatter as well, but I did it as an extension to LengthFormatter itself, like this:

extension LengthFormatter {
    public static let imperialLengthFormatter: LengthFormatter = {
        let formatter = LengthFormatter()
        formatter.isForPersonHeightUse = true
        return formatter
    }()    
}

This actually doesn't kill performance as suggested in the comments for @maslova's answer. Due to the property being static, it only gets initialized once.

// When creating the Patient object
let patient = Patient(...) // Create in maanged object context
patient.height = 71

// Later displays in a collection view cell in a view controller
cell.heightLabel.Text = patient.heightInFeetString

Displays this in my table cell:

5 ft, 11 in

like image 23
Matt Long Avatar answered Oct 26 '22 17:10

Matt Long