I have added UIView at some angle. Now at the run time i want to move that view to up (say 20px).
At start
dragView = [[DragbleView alloc] initWithFrame:CGRectMake(10, 200, 200, 90)];
dragView.transform = CGAffineTransformMakeRotation (-0.663247);
At Run Time
NSLog(#"Before : %#",NSStringFromCGRect(dragView.frame));
[dragView setCenter:CGPointMake(dragView.frame.origin.x, dragView.center.y+20)];
NSLog(#"After : %#",NSStringFromCGRect(dragView.frame));
Console O/p
Before : {{3.4947295778412979, 147.97225037436704}, {213.0105408443174, 194.05549925126593}}
After : {{-103.0105408443174, 167.97225037436704}, {213.0105408443174, 194.05549925126593}}
As you can see it goes to the wrong place. How to place above 20 px
If you read the docs on the UIView frame property, they say:
If the transform property is not the identity transform, the value of
this property is undefined and therefore should be ignored.
So you can't change the frame once you've changed your view's transform. It doesn't work any more.
Instead you should use the view's center property, as #GaryRiches suggests in his answer.
It is doing as you have specified. You would get better results using the center point for both the X and Y:
[dragView setCenter:CGPointMake(dragView.center.x, dragView.center.y + 20)];
Also, as noted below. iOS uses 0,0 as top left, so to move something "up" would require subtracting 20 from the dragView.center.y.
To move UP, it requires to minimize Y location values, whereas you are adding.
Try this:
[dragView setCenter:CGPointMake(dragView.frame.origin.x, dragView.center.y - 20)];
Related
I have a problem. I'm working on making a game. As part of my game I need images to be rotated and then moved in the direction of the rotated angle inside a game loop (using an NSTimer). In essence I'm trying to create the effect of launching a projectile. The code works fine when moving in perpendicular directions such as 0, 90, 180, 270, and 360 degrees, but any other angle and the image starts to glitch out. The object on the screen maintains its correct bounds and contents, but the actual displayed image disappears. Does anybody know what the problem is or someway I could get around it? If needed, I can make and post a video of my problem so you can see what I'm talking about.
Here is a sample of the code I'm using. The "background" variable is just a UIImageView:
angle = 60;
background.transform = CGAffineTransformRotate(object.transform, angle*M_PI/180); //converts degrees to radians and rotates the image
background.frame = CGRectMake( background.frame.origin.x + cos(angle*m_PI/180)*32; background.frame.origin.y -sin(angle*M_PI/180)*32, background.frame.size.width, background.frame.size.height); //moves the image in the direction of the angle
For starters, there is a semicolon after the x origin in your CGRect instead of a comma. Was that just a typo?
The UIView documentation for frame states:
Warning: If the transform property is not the identity transform, the
value of this property is undefined and therefore should be ignored.
Changes to this property can be animated. However, if the transform
property contains a non-identity transform, the value of the frame
property is undefined and should not be modified. In that case, you
can reposition the view using the center property and adjust the size
using the bounds property instead.
So there you have it, you should not be trying to change the frame when setting a custom transform. You are only trying to adjust the position of the view anyway so just modify your code to adjust center instead of the origin coordinates.
To change the size, you can use the bounds.
CGRect bounds = myView.bounds;
bounds.size.width = whatever;
bounds.size.height = whatever;
myView.bounds = bounds;
My transform does not draw after the frame is redrawn with setFrame.
I'm scaling a view when the orientation changes using setFrame. But this view also needs to change position depending on a BOOL argument: On = up in view, off = down off screen. I use setTransform to adjust the position of the view.
First I draw the view by doing a setFrame. This draws the view 100 points just off screen at the bottom. Then I set a transform (-100 on the ty) to bring it up into the view points (as long as the BOOL is set to TRUE, else the transform is 0 and the view is off screen).
[view setFrame:CGRectMake(0, self.view.bounds.size.height, self.view.bounds.size.width, 100)];
[view setTransform:CGAffineTransformMake(1, 0, 0, 1, 0, -100)]
This works fine, as expected, but when change orientation, and this code is re-run (to re-size the view) the transform does not draw, even though it is Logged as being the transform of the view. In other words the view is just off screen, as if the transform.ty was 0.
Log message before re-draw: view.transform.ty -10.000000
Log message after re-draw: view.transform.ty -10.000000
Any help?
A view's frame is a value that is derived from three fundamental values: bounds, center, and transform. The setter for frame tries to do the right thing by reversing the process, but it can't always work correctly when a non-identity transform is set.
The documentation is pretty clear on this point:
If the transform property is not the identity transform, the value of
this property is undefined and therefore should be ignored.
...
If the transform property contains a non-identity transform, the value
of the frame property is undefined and should not be modified. In that
case, you can reposition the view using the center property and adjust
the size using the bounds property instead.
Since your transform only translates the view, I don't see any reason to use it at all. Just change the origin of the view's frame:
[view setFrame:CGRectMake(0, self.view.bounds.size.height - 100, self.view.bounds.size.width, 100)];
I have a need for rotating UISlider via its superclass property transform, by changing the rotation matrix with:
self.slider.transform = CGAffineTransformMakeRotation(45);
In the documentation for UIView I can see a warning saying:
Warning If this property is not the identity transform, the value of
the frame property is undefined and therefore should be ignored.
So, the question is, how can I rotate a standard UIKit element without messing up frame rect? (By undefined I understand, they mean, totally messed up, not just enlarged enough to contain entire rotated object)
Basically when you set the transform on a view, you will want to set its size and position by using the bounds and center properties. When setting the frame on a view, UIKit will actually set the bounds/center/transform on your view.
So without setting a transform you can set the frame like this.
self.slider.frame = CGRectMake(10, 20, 300, 50);
When using a transform, you would have to do it like this:
self.slider.transform = CGAffineTransformMakeRotation(45);
self.slider.bounds = CGRectMake(0, 0, 300, 50);
self.slider.center = CGRectMake(10, 20, 300, 50);
I am an iOS newbie, so please bear with me. I have a blank page with a button centered in it. I want to add another button to the view, just below the centered button. How would I do that? I have added the first button like this -
float x=60, y=200, dy=50;
CGRect frame = CGRectMake(x, y, 200, dy);
UIButton *inboxButton = [[[UIButton alloc] initWithFrame:frame]autorelease];
inboxButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
inboxButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
....
[theView addSubview:inboxButton];
Just calculate the second frame based on the first
CGRect secondFrame = CGRectMake(CGRectGetMinX(frame),
CGRectGetMaxY(frame) + 8.0, // some y padding
CGRectGetWidth(frame),
CGRectGetHeight(frame));
You are not saying if you need that second one centered by itself - I am assuming that you just want it below, left-aligned with the first one:
UIButton *outboxButton = [[[UIButton alloc] initWithFrame:CGRectMake(frame.x,
frame.y + dy + 10.0f,
frame.width,
frame.height)] autorelease];
[theView addSubview:outboxButton];
What I am doing here is simply reusing the horizontal coordinate from the first button. For the vertical coordinate, I am using the original coordinate and add the height and an offset (10.0f) to it. Both, the width and the height are taken from the first button, assuming that they should match in size.
As you will see, there is no way to have this calculation done implicitly, which I assume you actually wanted to find out - that is, by simply supplying some ordering arguments.
I'd like to have the coordinates relative to the main window, therefore I'm using convertRect:toView: with nil as second parameter:
CGRect result = [self.view convertRect:addToList.frame toView:nil];
But as a result, I always get
NSRect: {{5, 30}, {35, 35}}
and that are exactly the values I use for generating the addToList-view:
addToList = [UIButton buttonWithType:UIButtonTypeCustom];
addToList.frame = CGRectMake(5.0f, 30.0f, 35.0f, 35.0f);
If I'm adding the addToList-button to a view which is placed in the bottom right corner of the screen, I would expect a CGRect like for example 950, 700, 35, 35 on an iPad landscape, because those are the coordinates of the button relative to the topmost view.
First, I did the conversion in the initWithFrame:-method of a UIViewController; but now, I'm doing it afterwards... what am I missing?
Thanks!
EDIT: is it important that "self.view" is inside a UIScrollView?
If you look at convertRect:toView:'s documentation, you will see that the rect that you pass is defined as within the bounds of the view on which you are calling the method. Since self.view presumably takes the entire window, the rect doesn't change with respect to the window. You should message the parent of the button to get the actual location of the button w.r.t the window.