What's this {{0,0},{w,h}} doing? - objective-c

I stumbled upon this piece of code today:
CGRect rect = {{0,0},{w,h}};
Here, I would have used a CGRectMake. But what does this thing in rambled brackets do? What kind of special-syntax is that? None of my objective-c books ever mentioned that.

It's a standard C structure initialization construct. Any structure can be initialized at declaration time by providing its contents in order within curly braces like this. Because a CGRect contains a CGPoint and a CGSize, you use one set of braces for the CGRect, then another set for each of CGPoint & CGSize.

Related

Manipulating origin of a CGRect property through self. notation does not work but manipulating through ivar works [duplicate]

I know that I can't use this:
myView.frame.origin.x = 25.0;
and that I have to use this instead:
CGRect myFrame = myView.frame;
myFrame.origin.x = 25.0;
myView.frame = myFrame;
And I'm doing it all the time, but I don't know why I must do it that way. I would like to fill that gap in my understanding. Can someone explain ?
Nowadays Xcode gives you "Expression not assignable". Some time ago you got a compile error "Lvalue required as left operand of assignment".
There are two distinct dot syntaxes being used here. They look the same, but they do different things depending on what they are operating on and what is being done with it:
The first myView.frame is a shorthand for [myView frame], a method call that returns a CGRect struct by value.
myFrame.origin.x is accessing ordinary struct members in the traditional C fashion.
The second myView.frame is again a shorthand, but because the statement is an assignment it translates to calling a different method, [myView setFrame:myFrame].
In your single-line top example, you get a copy of the rect and set its x, but never copy it back to the view. You have to explicitly differentiate between the method calls, the dot syntax sugar can't magic them into a single call.
The reason this does not work is due to the mixing of two syntaxes.
First you have "." as a shortcut for calling the accessor functions (an Objective-C feature).
So
a.b becomes [a getB];
a.b = 5 becomes [a setB:5];
And then theres "." as direct struct member access (pure C). So
a.b really is a.b;
a.b really is a.b = 5;
Combining this in a set-value-case like this, doesn't work.
Because ...
If you could call
myView.frame.origin.x = 25.0;
The "myView.frame" part equals [myView getFrame] and you get a copied CGRect frame (a C struct)
The "myView.frame.origin" gives you a CGPoint origin (also a struct) of the copied CGRect
The "myView.frame.origin.x = 25.0" gives you a CGFloat x of the origin and now you want to assign something to it and here comes the problem...
You try to set a variable of a struct of a struct, which is ok, but there is no pointer from the UIView to the struct, so it is copied instead. So you copy and then you set and then you expect that the set action is somehow forwarded through the initial get to the UIView, well and this just doesn't work.
Of course you could wonder why Apple hasn't just created a shortcut, so that in the end your copied frame is automatically reinjected into a auto appended setFrame call, I guess you just have to live with how it is.
So remember, it would work if you'd get a pointer to the frame, but you don't, you get a copied struct instead.
So if you expect myView.frame.origin.x = 25.0 to work you indirectly expect your call to be automagically translated into some sort of
[myView setFrame:[myView getFrame:frame].origin.x = 25.0].
Well I guess you can admit that this looks wrong.
Also imagine if you'd get a direct pointer to the CGRect frame and you'd change something through that pointer, how would the UIView know that it's size has changed and that it has to update itself ? If on the other hand a [myView setFrame:newFrame] call is made, then UIView can do all the necessary readjusting itself.
A CGRect is a struct, which is something from standard C. A CGRect is not an Objective C object, so when you assign to one of its members, no setter method is called. Without a setter method being called, UIKit will not be able to know that anything has changed, and so will not be able to update the screen display.
Edit: as has been pointed out, the assignment will be to a copy of the struct.
When you manipulate the data directly, no accessor is called, so the UI cannot update itself - or inform any other component that wants to know about changes.
Edit: As pointed out by walkytalky, you will get a copy of the data, so changing it doesn't have any effect on the original anyway. The following example will show this:
UIView *aView = [[UIView alloc] initWithFrame:CGRectMake(50,50,100,100)];
NSLog(#"%f", aView.frame.origin.x); // will give 50
aView.frame.origin.x = 17; // operates on a copy of the rect only
NSLog(#"%f", aView.frame.origin.x); // will still give 50

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.

A bracket + dot syntax issue

I have been working with Objective C for a couple of months now and this has been my first time seeing this type of syntax in objective c. I think I know the equivalent of it but I wanted to be sure.
Specifically, the code looks like this
float imageHeight = [player texture].contentSize.height;
What is the '[player texture].contentSize.height equivalent to?
I know that the dot and bracket syntax can be the same thing
something.backgroundColor = [UIColor redColor];
[something setBackgroundColor:[UIColor redColor]];
The above two are pretty much the same..
So is this what's happening in my first example? I just have never seen a mix of bracket and dot syntax into one.
Thank's.
float imageHeight = [player texture].contentSize.height;
This is probably the same thing as:
float imageHeight = [[player texture] contentSize].height;
Dot syntax translates to a call of the appropriate property accessor method when it's applied to object pointers. When applied to structures, it's a direct access of one of the fields of that structure.
So, assuming that [player texture] returns a pointer to an object (probably an image), the first use of dot syntax accesses that object's contentSize property. If -contentSize also returns an object pointer, then the next use would also translate to a call of an accessor. More likely, though, the contentSize property is a NSSize, and NSSize is a structure with width and height fields. So the second use of dot syntax most likely retrieves the height field from that structure.
Mixing the two different meanings of dot syntax works fine for getting a value, but you'll get an error if you try to use it to set values. If my assumptions in the previous paragraph are correct, you can't do this:
[player texture].contentSize.height = 50;

Need help with CGRect & CGPoint for ios dev

Hey, I'm trying to implement double tap zooming and I'm trying to work with code from Apple's ScrollViewSuite example and several of the lines are giving me errors.
The first one is
tapLocation = midpointBetweenPoints(tapLocation, [touch locationInView:touch.view]);
And it says incompatible types in assignment. I haven't been able to find much information about midpointBetweenPoints other than that it compares two CGPoints and I believe that's what I'm passing it.
Second section that gives an error is
CGRect zoomRect = [self zoomRectForScale:newScale withCenter:tapPoint];
And it gives me invalid initializer.
Does anyone know what I'm doing wrong?
Both midpointBetweenPoints and zoomRectForScale:withCenter: are part of the example code rather than part of iOS itself.
midpointBetweenPoints returns a CGPoint (relevant to the error about assignment), but is not declared in a header anywhere. You'll see that in the sample in which it is used, 3_Tiling, it is defined at line 176 of TapDetectingView.m, declared at line 54 and used at 118 and 139. My guess would be that you're either not including the code in your project at all, or are including the definition but omitting the declaration. Objective-C is a superset of C, so follows the C rules. In C, any function for which the declaration cannot be found is assumed to return 'int'. Quite probably you need to add the declaration:
CGPoint midpointBetweenPoints(CGPoint a, CGPoint b);
Somewhere before your use.
At a guess, your use of zoomRectForScale:withCenter: is likely to be a similar issue. If declarations aren't found for Objective-C methods then they're assumed to return 'id', which is a pointer to a generic Objective-C object. CGRect is a C struct, so casting a pointer to it makes no sense. Assuming you've included the code from lines 401 to 416 of RootViewController.m, you also need to ensure the declaration is visible to the calling code. In the sample code, this is achieved on line 80 with the declaration:
#interface RootViewController (UtilityMethods)
- (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center;
#end
Declaring additions like that inside a source file is a way of approximating private methods in Objective-C. If you want it to be public, you can just add it to the interface declaration proper.

myView.frame.origin.x = value; does not work - But why?

I know that I can't use this:
myView.frame.origin.x = 25.0;
and that I have to use this instead:
CGRect myFrame = myView.frame;
myFrame.origin.x = 25.0;
myView.frame = myFrame;
And I'm doing it all the time, but I don't know why I must do it that way. I would like to fill that gap in my understanding. Can someone explain ?
Nowadays Xcode gives you "Expression not assignable". Some time ago you got a compile error "Lvalue required as left operand of assignment".
There are two distinct dot syntaxes being used here. They look the same, but they do different things depending on what they are operating on and what is being done with it:
The first myView.frame is a shorthand for [myView frame], a method call that returns a CGRect struct by value.
myFrame.origin.x is accessing ordinary struct members in the traditional C fashion.
The second myView.frame is again a shorthand, but because the statement is an assignment it translates to calling a different method, [myView setFrame:myFrame].
In your single-line top example, you get a copy of the rect and set its x, but never copy it back to the view. You have to explicitly differentiate between the method calls, the dot syntax sugar can't magic them into a single call.
The reason this does not work is due to the mixing of two syntaxes.
First you have "." as a shortcut for calling the accessor functions (an Objective-C feature).
So
a.b becomes [a getB];
a.b = 5 becomes [a setB:5];
And then theres "." as direct struct member access (pure C). So
a.b really is a.b;
a.b really is a.b = 5;
Combining this in a set-value-case like this, doesn't work.
Because ...
If you could call
myView.frame.origin.x = 25.0;
The "myView.frame" part equals [myView getFrame] and you get a copied CGRect frame (a C struct)
The "myView.frame.origin" gives you a CGPoint origin (also a struct) of the copied CGRect
The "myView.frame.origin.x = 25.0" gives you a CGFloat x of the origin and now you want to assign something to it and here comes the problem...
You try to set a variable of a struct of a struct, which is ok, but there is no pointer from the UIView to the struct, so it is copied instead. So you copy and then you set and then you expect that the set action is somehow forwarded through the initial get to the UIView, well and this just doesn't work.
Of course you could wonder why Apple hasn't just created a shortcut, so that in the end your copied frame is automatically reinjected into a auto appended setFrame call, I guess you just have to live with how it is.
So remember, it would work if you'd get a pointer to the frame, but you don't, you get a copied struct instead.
So if you expect myView.frame.origin.x = 25.0 to work you indirectly expect your call to be automagically translated into some sort of
[myView setFrame:[myView getFrame:frame].origin.x = 25.0].
Well I guess you can admit that this looks wrong.
Also imagine if you'd get a direct pointer to the CGRect frame and you'd change something through that pointer, how would the UIView know that it's size has changed and that it has to update itself ? If on the other hand a [myView setFrame:newFrame] call is made, then UIView can do all the necessary readjusting itself.
A CGRect is a struct, which is something from standard C. A CGRect is not an Objective C object, so when you assign to one of its members, no setter method is called. Without a setter method being called, UIKit will not be able to know that anything has changed, and so will not be able to update the screen display.
Edit: as has been pointed out, the assignment will be to a copy of the struct.
When you manipulate the data directly, no accessor is called, so the UI cannot update itself - or inform any other component that wants to know about changes.
Edit: As pointed out by walkytalky, you will get a copy of the data, so changing it doesn't have any effect on the original anyway. The following example will show this:
UIView *aView = [[UIView alloc] initWithFrame:CGRectMake(50,50,100,100)];
NSLog(#"%f", aView.frame.origin.x); // will give 50
aView.frame.origin.x = 17; // operates on a copy of the rect only
NSLog(#"%f", aView.frame.origin.x); // will still give 50