views:

314

answers:

4

I know that I can't use that

myView.frame.origin.x = 25.0;

that I have to use instead

CGRect myFrame = myView.frame;
myFrame.origin.x = 25.0;
myView.frame = myFrame;

And I'm doing it all the time (f.e. in animations), 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 ?

+1  A: 

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] intiWithFrame: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
Eiko
I think you should get a"Lvalue required as left operand of assignment"from the compiler first.
Allisone
still it got me understanding. thank you
Allisone
No compiler warnings here...
Eiko
+1  A: 

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.

Emil
+9  A: 

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.

walkytalky
great explanation!
Reflog
+1  A: 

The reason this does not work is due to the mixing of two syntaxes. Once "." is a shortcut for calling the accessor functions (an Objective-C feature). Once "." is a direct struct member access (pure C). Combining this in a set-value-case like this, doesn't work. Because theoretically...

When you could call

myView.frame.origin.x = 25.0;
  • The "myView.frame" part would get you the CGRect frame (calling sth.like [myView getFrame])

  • The "myView.frame.origin" part would get you the CGPoint origin

  • The "myView.frame.origin.x = 25.0" would set the x Value of the origin to 25.0 by directly accessing the CGPoint origin and directly accessing the CGFloat x value (both are struct's as I see now)... but (so I think to understand now) the myView.frame set-accessor wouldn't be called, thus myView wouldn't be notified about the changes.


So what normally happens behind the scenes is that:

myView.frame = newFrame;

will call the accessor function (call being translated to [myView setFrame:newFrame]) which might look like this:

@implementation UIView
- (void) setFrame:(CGRect) frame{
    _frame = frame;
    [self updateMySelfOrInformOtherComponentThatWantsToKnowAboutChanges];
}

So if you do the "myView.frame = myFrame;" call the other components or whatever will get informed. But if you say:

myView.frame.origin.x = 25.0;

only the getFrame:frame would get called, returned, and then it's origin would get modified and all this without setFrame function ever being called, cause actually you're not setting it thus the dubious [self updateMySelfOrInformOtherComponentThatWantsToKnowAboutChanges] wouldn't get called.

Well instead you get the "Lvalue required as left operand of assignment" from the compiler which I guess is XCodes way of telling you exactly this :D (no I don't know honestly). Or it must be the mixing of direct struct member access with this accessor methods called with the "dot" syntax.

Allisone