Conversion between CGFloat and NSNumber without unnecessary promotion to Double
Update: One can cast a CGFloat
value to NSNumber
and back:
let c1 = CGFloat(12.3)
let num = c1 as NSNumber
let c2 = num as CGFloat
This preserves the precision of CGFloat
and works with Swift 2
and Swift 3.
(Previous answer – far too complicated): There are two solutions that I found. The first uses the toll-free bridging
between NSNumber
and CFNumber
(as in What is most common and correct practice to get a CGFloat from an NSNumber?
for Objective-C). It uses the fact that CFNumber
has a dedicated
conversion mode for CGFloat
values:
extension NSNumber {
// CGFloat -> NSNumber
class func numberWithCGFloat(var value: CGFloat) -> NSNumber {
return CFNumberCreate(nil , .CGFloatType, &value)
}
// NSNumber -> CGFloat
var cgFloatValue : CGFloat {
var value : CGFloat = 0
CFNumberGetValue(self, .CGFloatType, &value)
return value
}
}
That is simple and nice. The only drawback: I could not figure out
how to make the constructor an init
method instead of a class method
.
The second possible solution is a bit longer:
extension NSNumber {
// CGFloat -> NSNumber
private convenience init(doubleOrFloat d : Double) {
self.init(double : d)
}
private convenience init(doubleOrFloat f : Float) {
self.init(float : f)
}
convenience init(cgFloat : CGFloat) {
self.init(doubleOrFloat: cgFloat.native)
}
// NSNumber -> CGFloat
private func doubleOrFloatValue() -> Double {
return self.doubleValue
}
private func doubleOrFloatValue() -> Float {
return self.floatValue
}
var cgFloatValue : CGFloat {
return CGFloat(floatLiteral: doubleOrFloatValue())
}
}
There are two private "helper" init methods with the same external
parameter name doubleOrFloat
but different parameter types. From the actual
type of cgFloat.native
the compiler determines which one to call
in
convenience init(cgFloat : CGFloat) {
self.init(doubleOrFloat: cgFloat.native)
}
Same idea in the accessor method. From the type of self.native
the compiler determines which of the two doubleOrFloatValue()
methods to call in
var cgFloatValue : CGFloat {
return CGFloat(floatLiteral: doubleOrFloatValue())
}