2

I have an NSValue object that can "box" an instance of an unknown type (CGPoint, CGRect, etc.) and I need to determine this type in runtime. How would I do it in Swift?

I tried to do something like this:

if ((value as Any) is CGPoint) {}
else if (((value as Any) is CGRect)) {}
...

When value is a NSValue object containing a CGPoint, it does not get into the if clause. Then I when I printed value it gives me NSPoint: {150, 150} which I assume is why it never gets in the clause.

Any ideas why this happens and how to solve it?

Thanks a lot!

Bruno Morgado
  • 507
  • 1
  • 8
  • 26
  • There's a toll free bridging between NS* and CG*. Have you tried to use NSPoint instead of CGPoint in the if clause? Eg. `if value is NSPoint {` – qwerty_so Dec 22 '14 at 15:28
  • I tried that yes, but the compiler does not accept that comparison and I get an error 'Use of undeclared type NSPoint' – Bruno Morgado Dec 22 '14 at 15:53

2 Answers2

4

Ah, NSValue, you so crazy. Even though NSRect etc aren't supposed to be a thing on iOS, if you put a CGRect inside an NSValue, internally it displays it has an NSRect inside it.

Anyway, while they serve similar purposes I don't think you can use Any to convert an NSValue to or from a CGPoint or CGRect. You have to call value.CGRectValue() etc. But to your point, you need to know which it is. Which also isn't easy, but you can do this:

let type = String.fromCString(val.objCType) ?? ""
if type.hasPrefix("{CGRect") {
    let rect = val.CGRectValue()
}
else if type.hasPrefix("{CGPoint") {
    let point = val.CGPointValue()
} //etc...

But another question would be, do you really need to use NSValue? It's kind of an Obj-C specific workaround. Is it being given to you by something you can’t change or might you be better off using Any (or even better, generics) instead?

Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • That is a good point, but I want to simulate the behavior of CAKeyFrameAnimation which has a property 'values' of type [AnyObject]! But anyway, thanks for your solution. That will work. – Bruno Morgado Dec 22 '14 at 17:25
2

To start with, iOS doesn't use NSPoint, so that would be a loss. My best guess for something like this would be to fall back to using objCType, which is how it would be handled in objective-C. Unfortunately, you don't have access to @encode from swift, so you wind up having to hardcode the possibilities:

switch String(CString: value.objCType, encoding: NSUTF8StringEncoding) {
case "{CGPoint=dd}":  // handle CGPoint here
case "{CGRect={CGPoint=dd}{CGSize=dd}}": // handle CGRect here
}
David Berry
  • 40,941
  • 12
  • 84
  • 95