Reputation: 332
I have a problem with swift type casting, let's say I have this variables
let anim = CABasicAnimation(keyPath: "position")
let sublayersCount : NSInteger = 4
let sublayerIdx :NSInteger = 2
let instanceDelay : CGFloat = 0.1;
I want to do some math on them. In objective c, i'd write it like this
anim.beginTime += (sublayersCount - sublayerIdx - 1) * instanceDelay;
But in swift, the same feels very clumsy, is there any way to improve it?
anim.beginTime = CFTimeInterval(CGFloat(anim.beginTime) + CGFloat(sublayersCount - sublayerIdx - 1) * instanceDelay)
Upvotes: 0
Views: 294
Reputation: 686
Type casting in Swift is a nightmare and a joke honestly. I can't believe Apple thought this was good idea anyone wanted. Luckily Swift has powerful operator overloads and type extensions so we can hack around this problem.
This isn't a complete example of all types but the idea is we have operator overloads to cast types up towards larger sizes and/or signs or floats.
For example UInt8 + Float returns a Float (the largest signed type from the operation). UInt + Int returns Int (the same size but casting towards signs). UInt8 + Uint returns UInt (the larger size). The assumption is that we want to preserve signs of numbers and prevent overflowing by casting up to larger sizes (so UInt8(255) + 1.5 returns Float(256.5) instead of truncating or crashing).
Note: I wouldn't recommend putting this in an existing code base because it undermines many implicit assumptions in Swift.
/*
Arithmetic on integers (signed or unsigned) & floats casts up to Float
Arithmetic on signed integers & unsigned integers (of any size) casts up to Int
Arithmetic on unsigned integers & unsigned integers (of any size) casts up to UInt
Float + Int = Float
Float + UInt = Float
UInt + Int = Int
UInt + UInt = UInt
UInt8 + UInt = UInt
UInt8 + Int = Int
*/
// MARK: Floating Point Arithmetic Operators
protocol FloatingPointArithmetic {
var toFloat: Float { get }
}
func + <T: FloatingPointArithmetic> (lhs: T, rhs: Float) -> Float { return lhs.toFloat + rhs }
func - <T: FloatingPointArithmetic> (lhs: T, rhs: Float) -> Float { return lhs.toFloat - rhs }
func * <T: FloatingPointArithmetic> (lhs: T, rhs: Float) -> Float { return lhs.toFloat * rhs }
func / <T: FloatingPointArithmetic> (lhs: T, rhs: Float) -> Float { return lhs.toFloat / rhs }
// MARK: Integer Arithmetic Operators
protocol IntegerArithmetic: FloatingPointArithmetic {
var toSigned: Int { get }
var toUnsigned: UInt { get }
}
protocol SignedIntegerArithmetic: IntegerArithmetic {}
protocol UnsignedIntegerArithmetic: IntegerArithmetic {}
func + <T: SignedIntegerArithmetic, U: UnsignedIntegerArithmetic> (lhs: T, rhs: U) -> Int { return lhs.toSigned + rhs.toSigned }
func + <T: UnsignedIntegerArithmetic, U: UnsignedIntegerArithmetic> (lhs: T, rhs: U) -> UInt { return lhs.toUnsigned + rhs.toUnsigned }
func + <T: UnsignedIntegerArithmetic, U: SignedIntegerArithmetic> (lhs: T, rhs: U) -> Int { return lhs.toSigned + rhs.toSigned }
func - <T: SignedIntegerArithmetic, U: UnsignedIntegerArithmetic> (lhs: T, rhs: U) -> Int { return lhs.toSigned - rhs.toSigned }
func - <T: UnsignedIntegerArithmetic, U: UnsignedIntegerArithmetic> (lhs: T, rhs: U) -> UInt { return lhs.toUnsigned - rhs.toUnsigned }
func - <T: UnsignedIntegerArithmetic, U: SignedIntegerArithmetic> (lhs: T, rhs: U) -> Int { return lhs.toSigned - rhs.toSigned }
func * <T: SignedIntegerArithmetic, U: UnsignedIntegerArithmetic> (lhs: T, rhs: U) -> Int { return lhs.toSigned * rhs.toSigned }
func * <T: UnsignedIntegerArithmetic, U: UnsignedIntegerArithmetic> (lhs: T, rhs: U) -> UInt { return lhs.toUnsigned * rhs.toUnsigned }
func * <T: UnsignedIntegerArithmetic, U: SignedIntegerArithmetic> (lhs: T, rhs: U) -> Int { return lhs.toSigned * rhs.toSigned }
func / <T: SignedIntegerArithmetic, U: UnsignedIntegerArithmetic> (lhs: T, rhs: U) -> Int { return lhs.toSigned / rhs.toSigned }
func / <T: UnsignedIntegerArithmetic, U: UnsignedIntegerArithmetic> (lhs: T, rhs: U) -> UInt { return lhs.toUnsigned / rhs.toUnsigned }
func / <T: UnsignedIntegerArithmetic, U: SignedIntegerArithmetic> (lhs: T, rhs: U) -> Int { return lhs.toSigned / rhs.toSigned }
func + <T: FloatingPointArithmetic, U: UnsignedIntegerArithmetic> (lhs: T, rhs: U) -> Float { return lhs.toFloat + rhs.toFloat }
func + <T: FloatingPointArithmetic, U: SignedIntegerArithmetic> (lhs: T, rhs: U) -> Float { return lhs.toFloat + rhs.toFloat }
func - <T: FloatingPointArithmetic, U: UnsignedIntegerArithmetic> (lhs: T, rhs: U) -> Float { return lhs.toFloat - rhs.toFloat }
func - <T: FloatingPointArithmetic, U: SignedIntegerArithmetic> (lhs: T, rhs: U) -> Float { return lhs.toFloat - rhs.toFloat }
func * <T: FloatingPointArithmetic, U: UnsignedIntegerArithmetic> (lhs: T, rhs: U) -> Float { return lhs.toFloat * rhs.toFloat }
func * <T: FloatingPointArithmetic, U: SignedIntegerArithmetic> (lhs: T, rhs: U) -> Float { return lhs.toFloat * rhs.toFloat }
func / <T: FloatingPointArithmetic, U: UnsignedIntegerArithmetic> (lhs: T, rhs: U) -> Float { return lhs.toFloat / rhs.toFloat }
func / <T: FloatingPointArithmetic, U: SignedIntegerArithmetic> (lhs: T, rhs: U) -> Float { return lhs.toFloat / rhs.toFloat }
// MARK: Supported Types
extension Float: FloatingPointArithmetic {
var toFloat: Float { return self }
}
extension UInt: FloatingPointArithmetic, UnsignedIntegerArithmetic {
var toSigned: Int { return Int(self) }
var toUnsigned: UInt { return self }
var toFloat: Float { return Float(self) }
}
extension UInt8: FloatingPointArithmetic, UnsignedIntegerArithmetic {
var toSigned: Int { return Int(self) }
var toUnsigned: UInt { return UInt(self) }
var toFloat: Float { return Float(self) }
}
extension Int: FloatingPointArithmetic, SignedIntegerArithmetic {
var toSigned: Int { return self }
var toUnsigned: UInt { return UInt(self) }
var toFloat: Float { return Float(self) }
}
Upvotes: 0
Reputation: 108169
I agree it's cumbersome. You can work around it by providing ad-hoc overloads, along the lines of
func * (lhs: Double, rhs: CGFloat) -> CGFloat {
return CGFloat(lhs * Double(rhs))
}
func + (lhs: Double, rhs: CGFloat) -> CGFloat {
return CGFloat(lhs) + CGFloat(rhs)
}
func - (lhs: Int, rhs: Int) -> Double {
return lhs - rhs
}
anim.beginTime = CFTimeInterval(anim.beginTime + (sublayersCount - sublayerIdx - 1) * instanceDelay)
Of course this is ridiculously specific to this particular example, but you get the idea.
That being said, there's a library providing a more general spectrum of handy overloads, allowing you to implicitly convert different numeric types. Check it out: https://github.com/seivan/ScalarArithmetic/blob/master/ScalarArithmetic/ScalarArithmetic.swift
Upvotes: 2