Check UIButton position after rotating UIView - iphone

I have a buttonsthat I add on a UIImageView. With a method when the user touch the screen
the UIImageView will rotate, I want to know if there is a way to get the new position of the button after the rotation is done.
Right now I'm getting all the time the original position with this method :
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"Xposition : %f", myButton.frame.origin.x);
NSLog(#"Yposition : %f", myButton.frame.origin.y);
}
Thanks,

This is a tricky question. Referring to the UIView documentation on the frame property it states:
Warning: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
So the trick is finding a workaround, and it depends on what exactly you need. If you just need an approximation, or if your rotation is always a multiple of 90 degrees, the CGRectApplyAffineTransform() function might work well enough. Pass it the (untransformed) frame of the UIButton of interest, along with the button's current transform and it will give you a transformed rect. Note that since a rect is defined as an origin, width and height, it can't define a rectangle with sides not parallel to the screen edges. In the case that it isn't parallel, it will return the smallest possible bounding rectangle for the rotated rect.
Now if you need to know the exact coordinates of one or all of the transformed points, I've written code to compute them before, but it's a bit more involved:
- (void)computeCornersOfTransformedView:(UIView*)transformedView relativeToView:(UIView*)parentView {
/* Computes the coordinates of each corner of transformedView in the coordinate system
* of parentView. Each is corner represented by an independent CGPoint. Doesn't do anything
* with the transformed points because this is, after all, just an example.
*/
// Cache the current transform, and restore the view to a normal position and size.
CGAffineTransform cachedTransform = transformedView.transform;
transformedView.transform = CGAffineTransformIdentity;
// Note each of the (untransformed) points of interest.
CGPoint topLeft = CGPointMake(0, 0);
CGPoint bottomLeft = CGPointMake(0, transformedView.frame.size.height);
CGPoint bottomRight = CGPointMake(transformedView.frame.size.width, transformedView.frame.size.height);
CGPoint topRight = CGPointMake(transformedView.frame.size.width, 0);
// Re-apply the transform.
transformedView.transform = cachedTransform;
// Use handy built-in UIView methods to convert the points.
topLeft = [transformedView convertPoint:topLeft toView:parentView];
bottomLeft = [transformedView convertPoint:bottomLeft toView:parentView];
bottomRight = [transformedView convertPoint:bottomRight toView:parentView];
topRight = [transformedView convertPoint:topRight toView:parentView];
// Do something with the newly acquired points.
}
Please forgive any minor errors in the code, I wrote it in the browser. Not the most helpful IDE...

Related

How can I transform a UIView from being parallel to the ground to being perpendicular?

I want to transform a view from a plane that is parallel to the ground to a plane that is perpendicular to the ground.
Any ideas?
To rotate a view in 3D, you need to modify the transform property of the view's layer.
It sounds like you want to rotate the view around the X axis. And I guess you want perspective, where the edge closer to the viewer appears larger and the edge farther from the viewer appears smaller.
We'll write a helper function that makes the transform. We start with the identity transform:
static CATransform3D transformWithAngle(CGFloat angle) {
CATransform3D transform = CATransform3DIdentity;
Then set the m34 component of the transform based on the distance from the viewer to the view:
transform.m34 = -1.0f / 300.0f;
If you use a number larger than 300, the perspective will be weaker. If you use a number smaller than 300, the perspective will be stronger.
Next, add the rotation:
transform = CATransform3DRotate(transform, angle, 1, 0, 0);
Then return the transform:
return transform;
}
Let's say you want to apply this to a view named self.riseView. You probably want the rotation to be around the bottom edge of the view, so set the layer's anchor point to the bottom edge:
- (void)viewDidLoad {
CGRect frame = self.riseView.frame;
self.riseView.layer.anchorPoint = CGPointMake(0.5f, 1.0f);
self.riseView.frame = frame;
and initialize the layer's transform with an angle of 0:
self.riseView.layer.transform = transformWithAngle(0);
}
You can make the view “set” (rotate down to the ground plane) by rotating it to an angle of -M_PI_2:
- (IBAction)setButtonWasTapped:(id)sender {
[self animateRiseViewToAngle:-M_PI_2];
}
and you can make it “rise” (rotate up) by rotating it to an angle of zero:
- (IBAction)riseButtonWasTapped:(id)sender {
[self animateRiseViewToAngle:0];
}
Here's how we actually do the animation:
- (void)animateRiseViewToAngle:(CGFloat)angle {
[UIView animateWithDuration:2 animations:^{
self.riseView.layer.transform = transformWithAngle(angle);
}];
}

Finding Bounding Box of Rotated Image that has Transparent Background

I have a UIImage that has a transparent background. When rotating this image, I'd like to find the bounding box around the graphic (ie the nons transparent part, if you rotate it in a UIImageView, it will find the bounding box around the entire UIImage including the transparent part).
Is there an Apple library that might do this for me? If not, does anyone know how this can be done?
If I understood your questions correctly, you can retrieve the frame (not bounds) of uiimageview then get the individual cgpoints and explicitly transform these points to get a transformed rectangle. Because in Apple's documentation it says: You can operate on a CGRect structure by calling the function CGRectApplyAffineTransform. This function returns the smallest rectangle that contains the transformed corner points of the rectangle passed to it. Transforming points 1 by 1 should avoid this auto-correcting behavior.
CGRect originalFrame = UIImageView.frame;
CGPoint p1 = originalFrame.origin;
CGPoint p2 = p1; p2.x += originalFrame.width;
CGPoint p3 = p1; p3.y += originalFrame.height;
//Use the same transformation that you applied to uiimageview here
CGPoint transformedP1 = CGPointApplyAffineTransform(p1, transform);
CGPoint transformedP2 = CGPointApplyAffineTransform(p2, transform);
CGPoint transformedP3 = CGPointApplyAffineTransform(p3, transform);
Now you should be able to define a new rectangle from these 3 points (4th one is optional because width and height can be calculated from 3 points. One point to note is that you cannot store this new rectangle in a cgrect because cgrect is defined by an origin and a size so its edges are always parallel to x and y axis. Apple's cgrect definition does not allow rotated rectangles to be stored.

ios MapKit, following a path

I have a path rendering on my map. I have a series of waypoints and I'm simulating movement through them.
When changing the display region it appears that my coordinate when converted to a CGPoint is floorf by Apple's implementation. This causes a very jittery appearance instead of a smooth one.
Is there anyway to get around this?
[m_mapView setRegion: MKCoordinateRegionMake(coord, MKCoordinateSpanMake(0, 0))];
The map then tries to center on this given point. However the point may not be pixel aligned as can be view by the following function.
CGPoint point = [m_mapView convertCoordinate:coord toPointToView:m_mapView];
Thus the mapview floors the centerpoint's result to align all pixels for the underlying map.
I make the view larger than the screen to account for the offset and to avoid clipping.
I simulate a point moving along the route and place it using an annotation and then center on that point at 30 frames per second.
Assume coord is the position of the moving point.
[m_mapView setCenterCoordinate: coord];
CGPoint p = [m_mapView convertCoordinate:m_mapView.centerCoordinate toPointToView:m_mapView];
CGPoint p1 = [m_mapView convertCoordinate:coord toPointToView:m_mapView];
CGPoint offset = CGPointMake(p.x - p1.x, p.y - p1.y);
CGRect frame = CGRectInset(CGRectMake(0.0f, 0.0f, 1024.0f, 1024.0f), -50.0f, -50.0f);
frame.origin.x+= offset.x;
frame.origin.y+= offset.y;
[m_mapView setFrame: frame];

Co-ordinates of the four points of a uiview which has been rotated

Is it possible to get this? If so, can anyone please tell me how?
Get the four points of the frame of your view (view.frame)
Retrieve the CGAffineTransform applied to your view (view.transform)
Then apply this same affine transform to the four points using CGPointApplyAffineTransform (and sibling methods of the CGAffineTransform Reference)
CGPoint topLeft = view.bounds.origin;
topLeft = [[view superview] convertPoint:topLeft fromView:view];
CGPoint topRight = CGPointMake(view.bounds.origin.x + view.bounds.width, view.bounds.origin.y);
topRight = [[view superview] convertPoint:topRight fromView:view];
// ... likewise for the other points
The first point is in the view's coordinate space, which is always "upright". Then the next statement finds the point that point corresponds to in the parent view's coordinate space. Note for an un-transformed view, that would be equal to view.frame.origin. The above calculations give the equivalent of the corners of view.frame for a transformed view.

Cocos2D Rotation and Anchor point

The problem that I have is that when ever I change the anchor point sprite automatically rotates with respect to the current anchor point. And I don't want that to happen.
The steps that I followed
create a sprite with anchor point (0.5, 0.5)
Changed the anchor point to (0,1)
Rotated the sprite to 90 degree. (Using CCRotateBy. Sprite rotated correctly)
Changed the anchor point to (0.5, 0.5) (Every thing is fine till now. And this is the position that I need to keep). Now sprite.rotation is 90.
I changed the anchor point to (1,0) (Sprite automatically rotates to 90 degree with respect to the given anchor point - I need to stop this behavior)
Is there any way to reset the rotation of sprite to 0, without actually rotating the texture(ie., to keep the texture in its current form - actual texture rotated to 90 degrees) and changing anchor point or position along with step 4, so that I can continue from point 5.
As Lukman says, the anchor point will always affect rotation, since your goal is to be able to specify the sprite position with a different anchor point from the rotation I would suggest making an empty CCNode as a parent of your sprite.
This way, you can set the position on sprite to be relative to this parent node to compensate for your anchor point change and then keep the anchor point for rotation on the sprite but use the parent node for position.
anchorPoint affects both position and rotation. You cannot stop it from affecting either one of them.
But from reading your question, since you want to prevent anchorPoint from affecting the rotation, I'm assuming here that the reason you change the anchorPoint is for the position, for example you are setting it to be ccp(1, 0) because you want to the sprite bottom right corner, instead of the sprite center, to be where you set the position is.
My suggestion is: don't change the anchorPoint at all, but change the way you set the sprite position. You can use this small function to adjust the position:
CGPoint adjustedPosition(const CGPoint position, const CGPoint anchor, const CGSize size) {
return CGPointMake(position.x - (anchor.x - 0.5) * size.width, position.y - (anchor.y - 0.5) * size.height);
}
Now, assuming you wanted to use anchorPoint of (1,0) when doing the positioning, instead of sprite.position = ccp(200, 300), you just need to do:
sprite.position = adjustedPosition(ccp(200, 300), ccp(1.0, 0.0), sprite.contentSize);
If you want, I'll post the logic behind the math later. Otherwise, I hope this will help.
Maybe it will help you to install an anchor for the sprites in the correct coordinate.
void SetAnchorPosition(CCSprite * sprite, const CCPoint & point)
{
static CCSize winSize = CCDirector::sharedDirector()->getWinSize();
double x = ((double)1/(double)winSize.width)*(double)point.x;
double y = ((double)1/(double)winSize.height)*(double)point.y;
sprite->setAnchorPoint(ccp(x,y));
sprite->setPosition(point);
}
You can add a line in the touchEnded method as a forceful alternative:
-(void)touchEnded:(UITouch *)touch withEvent:(UIEvent *)event{
_yourSprite.rotation = 90;
}