Making NSPoint/NSRect/NSSize Objects
Updated 2010/12/9 to improve the solution (see below). Updated 2016/8/25 to add Swift.
In Cocoa the common NSPoint/NSRect/NSSize/etc. data types are C structures. But what if you need to pass one as an id data type (an object), as I needed to do this while implementing undo for a Cocoa application? The solution is NSValue, which serves as a basic wrapping/container object for these structures:
NSPoint point = NSMakePoint(x, y);
NSValue *pointValue = [NSValue valueWithPoint:point];
[undoManager registerUndoWithTarget:obj
selector:@selector(setPositionObject:) object:pointValue];
In Swift:
let point = NSPoint(x: ..., y: ...)
let pointValue = NSValue(point: point)
undoManager.registerUndoWithTarget(obj,
selector: #selector(setPositionObject(_:)), object: pointValue)
On the receiving end you would extract the value like this:
- (void)setPositionObject:(NSValue *)positionValue {
NSPoint position = positionValue.pointValue;
...
}
@objc func setPositionObject(positionValue: NSValue) {
let position = positionValue.pointValue
...
}
If you’re working on iOS, you’ll be using CGPoint instead: NSValue(CGPoint:) and CGPointValue().
Prior to December 9th, 2010 this post suggested that the following was your best approach (“a few extra steps, but it gets the job done,” I said). Thanks to Fred G for the (embarrassing) correction. I reproduce it here because it does demonstrate a useful technique but is hardly the most sane way to go about it.
NSPoint point = NSMakePoint(x, y);
NSValue *pointValue = [[[NSValue alloc] initWithBytes:&point;
objCType:@encode(NSPoint)] autorelease];
[undoManager registerUndoWithTarget:obj
selector:@selector(setPositionObject:) object:pointValue];
I suspect that this escaped me at the time because of Apple’s habit of separating documentation for category methods (which valueWithPoint: is) from the normal class documentation. Thanks for improving upon that, Apple.