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

馋奶兔 提交于 2019-12-17 10:27:09

问题


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".


回答1:


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.




回答2:


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.




回答3:


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.




回答4:


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


来源:https://stackoverflow.com/questions/2983832/myview-frame-origin-x-value-does-not-work-but-why

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!