Is CAValueFunction only usable for properties of CATransform3D type? - core-animation

All the value functions provided by Core Animation map scale values to CATransform3D values and it doesn't offer any way to create CAValueFunction instances other than +functionWithName:.
So it seems CAPropertyAnimation.valueFunction is only usable for properties of CATransform3D type. Do I get it right?

Related

Cocos2d: How Can I Programatically Set Gid Properties To A CCTMXTiledMap

After making my TMX based map in Tiled, I would like to programatically change certain properties of the tiles as my game progresses.
This is the code I've tried but it doesn't work. But I include it here to demonstrate the logic of what I'm trying to achieve...
NSDictionary *currentProperties = [_tileMap propertiesForGID:tileToMarkFalling];
[currentProperties[#"Falling"] setString:#"True"];
[_tileMap propertiesForGID:tileToMarkFalling] = currentProperties;
The error thrown here is "Expression is not assignable".
How can I set these properties programatically, thanks.
The last line is not proper syntax, you can't assign a value to (the result of) a selector/message/function call. In fact cocos2d stores the TMX properties as immutable dictionary unfortunately, you can't modify them. You'll have to get the properties and store them yourself in your own class in a mutable dictionary or other way.
Note that the dictionary is not the ideal way to store logic info for tiles, if you check each tile's properties dict for "falling" and possibly other values every frame the dictionary lookup overhead will cost you performance, possibly quite severely if you have thousands of tiles.

Immutable alternative to CGPoint?

As the question states, I would prefer an immutable version of CGPoint, CGSize, and CGRect, although I would rather use a type which is supported by the iOS framework, to avoid converting between types.
So, is there any supported immutable version of CGPoint? If not, any recommendations?
Thanks!
You can just use constant CGPoints etc.
E.g.:
const CGSize size = (CGSize){6.0f, 4.0f};
Using CGSizeMake won't work as they are not compile time constants and you can't use variables, but this is the closest you'll get to an immutable CGSize. If that is not good enough then you'll need to use an object and convert when needed.
When a CGPoint (or any other struct - CGRect, etc.) is a property of an Objective-C object, the mechanics of getter/setter methods and the value semantics of C structs means that there is some level of protection against indirectly modifying the value of a CGPoint property of an object. For example, an expression like someObject.someCGPointProperty.x = 123 will not actually modify the x value of the CGPoint owned by someObject, because the someCGPointProperty accessor will return the CGPoint by value, which means that you are setting the x value on a copy of the CGPoint. To actually modify the CGPoint owned by that object, you would need to do something like:
CGPoint point = someObject.someCGPointProperty;
point.x = 123;
someObject.someCGPointProperty = point;
Regarding control over the number of instances you create: since CGPoint is a struct, it's passed around by value - effectively it's copied - in function arguments or Objective-C messages. So there's going to be plenty of copies of thees structs moving around anyway. However, these structs are created on the stack, not the heap, and a CGPoint is only 16 bytes (possibly smaller on 32-bit iOS devices?), so it's doubtful you need to worry about any performance implications.
Also, see #hypercrypt's more succinct answer regarding const values, as that may address your intentions well.
You can use the NSValue wrapper class. It can wrap a point, a size, a rect and more. It's immutable, so whatever you store inside it can't be changed (remember that if you store a pointer, the memory area to which points can still be changed).
Example
NSValue* value= [NSValue valueWithCGPoint: CGPointMake(x,y)];
If you try to set value.CGPointValue.x or y you get a compile error. The getter returns just a copy of the CGPoint held in the object, so there's no way to change it's value.

C99 designated initializers or CGMake Macros?

Is C99 designated initializers or the various CGSizeMake, CZRectMake, etc macros more preferable as convention in modern Objective-C?
It almost seems like a personal style preference but the one advantage I see with the C99 style is that the intent of values is clear and explicit.
CGRect rect = CGRectMake(x, y, width, height)
would give you heartache if you mixed up the order of the values where as with:
CGRect rect = (CGRect){.origin.x = x, .origin.y = y, .size.width = width, .size.height = height};
there's no doubt that .height is getting the value you're giving it. What's the reason people would continue using the existing macros?
There's nothing in the Cocoa Coding Convention docs about this and one of the style guide I've come across that notes it is the GitHub style guide: https://github.com/github/objective-c-conventions
From Apple's CGGeometry reference:
All functions described in this reference that take CGRect data structures as inputs implicitly standardize those rectangles before calculating their results. For this reason, your applications should avoid directly reading and writing the data stored in the CGRect data structure. Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics.
So, these CGRect functions (CGRectGetWidth, for example) will ensure height and width are always non-negative values. (CGRect functions that do not "take CGRect data structures as inputs", like CGRectMake, don't standardize the rect's dimensions.)
Additionally, the existing functions could be transitioned in the (admittedly extremely unlikely) event that the data structure ever changed.
Finally, despite the Github style guide you mentioned, the New York Times Objective-C Style Guide suggests using the CGRect functions (for the same reason I mentioned above).
Although it would be possible to use the inline functions when getting values from CGRects, and direct struct member access when creating them, this inconsistency could hurt code readability. Obviously, as you mentioned, it's a style question.
Basically, it doesn't matter, but use the functions.
Is C99 designated initializers or the various CGSizeMake, CZRectMake, etc macros more preferable as convention in modern Objective-C?
Neither style is objectively preferable in general. The formats of the CGRect, CGPoint, and CGSize structures are part of the public documentation, and thus effectively as unchangeable as the order of arguments to CGRectMake, so using a literal is fine. Whether to use literals or the Make functions is a matter of personal taste.
I tend to use CGRectMake instead of a literal when I have all the coordinates easily accessible. I prefer a literal when I'd otherwise have to either use an extra statement or send a message twice. For example, suppose I want to make a rectangle whose origin is (0,0) and whose size is the size of view. I could do that with CGRectMake like this:
CGRect rect = CGRectMake(0, 0, view.bounds.size.width, view.bounds.size.height);
But then I'm sending the bounds message twice. I could send it just once like this:
CGRect rect = view.bounds;
rect.origin = CGPointZero;
Or like this:
CGRect size = view.bounds.size;
CGRect rect = CGRectMake(0, 0, size.width, size.height);
But I prefer to use a literal like this:
CGRect rect = (CGRect){ .origin = CGPointZero, .size = view.bounds.size };
What's the reason people would continue using the existing macros?
Familiarity. Many people aren't familiar with the literal syntax, and they won't learn it by reading Apple's documentation.
A form of safety. If you use the macro, you're forced to provide all of the structure elements. You can't accidentally omit one.
On the other hand, you must put the elements in the correct order. So it's not strictly safer than a literal.

How do I update an annotation without using setCoordinate?

I have a subclassed NSManagedObject that conforms to the MKAnnotation protocol and it has NSNumber properties for latitude and longitude.
When I change any coordinates myself, I use setCoordinate: and update the latitude and longitude properties inside the implementation of setCoordinate:. Using this method, the map view updates the annotations. However, when I merge changes with another NSManagedObjectContext via mergeChangesFromContextDidSaveNotification:, setCoordinate: is not used because the latitude and longitude properties are explicitly changed. This prevents any KVO notifications going out about the coordinate changing.
I have tried to get the map view to realize the coordinate depends on the latitude and longitude properties with this code:
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([#"coordinate" isEqualToString:key]) {
NSSet *affectingKeys = [NSSet setWithObjects:#"latitude", #"longitude", nil];
keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys];
}
return keyPaths;
}
However that code produces this crash:
Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer for the key path "coordinate" from because it is not registered as an observer.'
Any ideas on how to prevent that crash or alternative methods to get the coordinate KVO notification sent out when the latitude or longitude is changed? Thanks for taking a look.
I fixed this by changing setCoordinate: so that it uses the primitive accessors that are automatically generated by Core Data to set the latitude and longitude properties.
I suspect that because my keyPathsForValuesAffectingValueForKey: made the coordinate key dependent on both the latitude and longitude keys, the MKMapView was getting confused when setCoordinate: used the public accessors instead of the primitive accessors.
When the public accessor methods were used to set the latitude and longitude, any object observing the coordinate key path observed three separate changes (for the key paths coordinate, latitude, and longitude) when there was only one change (coordinate) that should be observed, which confused the map view.
It's a little overhead, but you could observe the latitude and longitude params (resp. override their setters), and force-set the coordinate (with setCoordinate:) when the change.

How can I transform an array with double elements into an image I can work with?

I'm using XCode with Objective C and I'm wondering how I can transform an array with elements of type double into an image I can work with easily in xCode?
The array is defined as follows:
double x [50][100];
Also, I'm not sure how to deallocate this variable (x). Do I need to worry about deallocating it?
I'm using XCode with Objective C and I'm wondering how I can transform an array with elements of type double into an image I can work with easily in xCode?
The numbers need to represent components of pixels. For RGB color, you'll want 3 or 4 components (numbers) per pixel, depending on whether or not you want transparency (alpha). For monochrome (grayscale), you'll want 1 or 2. For CMYK color (for printing), you'll want 4 or 5. (I'm not sure whether you can do CMYKA, actually.)
Furthermore, they need to be in a defined order. Common orders for RGB color include RGBA (alpha last) and ARGB (alpha first).
Finally, they should be in a flat array. You may get away with a multidimensional array, but I'm not sure you can rely on that.
Once you've satisfied all of that, you'll need to pass the array of numbers to either CGImage or CGBitmapContext, depending on what you're doing. Both of these are part of Core Graphics.
Also, I'm not sure how to deallocate this variable (x). Do I need to worry about deallocating it?
No, because you didn't dynamically allocate it. A variable not otherwise declared has “automatic storage duration”, which means exactly what you'd expect, and in your declaration, the entire array is in the variable.
Contrast with a declaration like this:
double *x = …;
In that declaration, only a pointer is in the variable; the memory it points to lies elsewhere, and may or may not be automatically managed, depending entirely on how that was allocated.