Rotate UIButton randomly on screen - iphone

I want to rotate a button randomly on a screen. No specific path defined it can move randomly on a view.
I dont want to use CAKeyframeAnimation. It should be clean and simple.
Can anyone guide me ?

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];
Also see if this link is useful to you : Moving UIButton

Animate the button's transform property using CGAffineTransformMakeRotation() and combine it with an NSTimer with a random time interval.

#define RADIANS(degrees) ((degrees * M_PI) / 180.0)
CGAffineTransform rotateTransform = CGAffineTransformRotate(CGAffineTransformIdentity,
RADIANS(30.0));
myButton.transform = rotateTransform;
Use NSTimer to carry this process and use arc4random to randomly generate the angle in radians.

Related

Find Frame Coordinates After UIView Transform is Applied (CGAffineTransform)

I rotate my view with CGAffineTransform
[view setTransform:newTransform];
The frame values remain the same after transform is applied but how do I find "rotated" or transformed values of this frame?
(source: informit.com)
I want the exact coordinates of rotated frame edges, that is a, b, c, d points.
One thing to keep in mind is that the transform changes the coordinate system, so you will need to be able to convert between the parent 'view' and the child (transformed) view. Also, transforms preserve the center of the transformed object but not any of the other coordinates. So you need to calculate things in terms of the center. And there are several helpers you will need. (I got most of the following approach from Erica Sadun's book Core iOS Developer's Cookbook).
I usually add these as a category on UIView.
In order to transform the child's coordinates to those of the parent you need something like:
// helper to get pre transform frame
-(CGRect)originalFrame {
CGAffineTransform currentTransform = self.transform;
self.transform = CGAffineTransformIdentity;
CGRect originalFrame = self.frame;
self.transform = currentTransform;
return originalFrame;
}
// helper to get point offset from center
-(CGPoint)centerOffset:(CGPoint)thePoint {
return CGPointMake(thePoint.x - self.center.x, thePoint.y - self.center.y);
}
// helper to get point back relative to center
-(CGPoint)pointRelativeToCenter:(CGPoint)thePoint {
return CGPointMake(thePoint.x + self.center.x, thePoint.y + self.center.y);
}
// helper to get point relative to transformed coords
-(CGPoint)newPointInView:(CGPoint)thePoint {
// get offset from center
CGPoint offset = [self centerOffset:thePoint];
// get transformed point
CGPoint transformedPoint = CGPointApplyAffineTransform(offset, self.transform);
// make relative to center
return [self pointRelativeToCenter:transformedPoint];
}
// now get your corners
-(CGPoint)newTopLeft {
CGRect frame = [self originalFrame];
return [self newPointInView:frame.origin];
}
-(CGPoint)newTopRight {
CGRect frame = [self originalFrame];
CGPoint point = frame.origin;
point.x += frame.size.width;
return [self newPointInView:point];
}
-(CGPoint)newBottomLeft {
CGRect frame = [self originalFrame];
CGPoint point = frame.origin;
point.y += frame.size.height;
return [self newPointInView:point];
}
-(CGPoint)newBottomRight {
CGRect frame = [self originalFrame];
CGPoint point = frame.origin;
point.x += frame.size.width;
point.y += frame.size.height;
return [self newPointInView:point];
}
Swift 4
extension UIView {
/// Helper to get pre transform frame
var originalFrame: CGRect {
let currentTransform = transform
transform = .identity
let originalFrame = frame
transform = currentTransform
return originalFrame
}
/// Helper to get point offset from center
func centerOffset(_ point: CGPoint) -> CGPoint {
return CGPoint(x: point.x - center.x, y: point.y - center.y)
}
/// Helper to get point back relative to center
func pointRelativeToCenter(_ point: CGPoint) -> CGPoint {
return CGPoint(x: point.x + center.x, y: point.y + center.y)
}
/// Helper to get point relative to transformed coords
func newPointInView(_ point: CGPoint) -> CGPoint {
// get offset from center
let offset = centerOffset(point)
// get transformed point
let transformedPoint = offset.applying(transform)
// make relative to center
return pointRelativeToCenter(transformedPoint)
}
var newTopLeft: CGPoint {
return newPointInView(originalFrame.origin)
}
var newTopRight: CGPoint {
var point = originalFrame.origin
point.x += originalFrame.width
return newPointInView(point)
}
var newBottomLeft: CGPoint {
var point = originalFrame.origin
point.y += originalFrame.height
return newPointInView(point)
}
var newBottomRight: CGPoint {
var point = originalFrame.origin
point.x += originalFrame.width
point.y += originalFrame.height
return newPointInView(point)
}
}
You can find out the coordinates of the rotated view by using basic trigonometry. Here is how you can do it:
The first step is to know your view's width and height. Divide them by 2 and you get your triangle's adjacent and opposite sides (cyan and green respectively). In the example above width = 300 and height = 300. So adjacentSide = 150 and oppositeSice = 150.
Find the hypotenuse (red). For this you use the formula: h^2 = a^2 + b^2. After applying this formula we find the hypotenuse = 212.13
Find theta. This is the angle between the adjacentSide (cyan) and the hypotenuse (red). For this you use the formula cos(theta) = (h^2 + a^2 - o^2)/2*h*o. After applying this formula we find that theta = 0.785 (RADIANS). To convert this to degrees we apply the formula degrees = radians * 180 / PI = 45 (degrees). This is the initial (offset) angle of the hypotenuse. This is very important to realize. IF THE VIEW'S ROTATION OF YOUR VIEW IS ZERO THE HYPOTENUSE HAS AN OFFSET ANGLE OF 45(DEGREES). We're going to need theta shortly.
Now that we know the hypotenuse (red) we need the rotationAngle. In this example I used a UIRotationGestureRecognizer to rotate the square view. This class has a "rotation" property which tells us how much the view has rotated. This value is in RADIANS. In the example above the rotation is 0.3597 RADIANS. To convert it to degrees we use the formula degrees = radians * PI / 180. After applying the formula we find the rotation angle to be 20.61 degrees.
We can finally find the offset width (orange) and height (purple). For width we use the formula width = cos(rotationAngle - theta) * hypotenuse and for height we use the formula height = sen(rotationAngle - theta). WE HAVE TO SUBTRACT THETA (IN RADIANS!) FROM THE ROTATION ANGLE (IN RADIANS TOO!) BECAUSE THETA WAS THE INITIAL OFFSET. View it this way: the hypotenuse had an angle of 45(degrees) when the rotation angle was zero. After applying the formulas we find that width = 193.20 and height = -87.60
Finally, we can add those values (width and height) to the center of the square to find the coordinates of the blue point.
-Example-
// Get the center point
CGPoint squareCenter = self.squareView.center;
// Get the blue point coordinates
CGPoint bluePointCoordinates = CGPointMake(squareCenter.x + width, squareCenter.y + height);
The blue point coordinates are (963.20, 272.40)
To better understand the formulas please see the following links:
Trigonometry 1
Trigonometry 2
Also, if you want to play around with the test project I created (it's the one in the image) please feel free to download it from the following link.
UPDATE
Here is a condensed method that will calculate the offset top-right point (blue) you're looking for.
/* Params:
/ viewCenter: The center point (in superView coordinates) of your view
/ width: The total width of your view
/ height: The total height of your view
/ angleOfRotation: The rotation angle of your view. Can be either DEGREES or RADIANS
/ degrees: A boolean flag indicating whether 'angleOfRotation' is degrees
/ or radians. E.g.: If 'angleOfRotation' is expressed in degrees
/ this parameter must be 'YES'
*/
-(CGPoint)calculateTopRightCoordinatesFromViewCenter:(CGPoint)viewCenter viewWidth:(CGFloat)viewWidth viewHeight:(CGFloat)viewHeight angleOfRotation:(CGFloat)angleOfRotation degrees:(BOOL)degrees {
CGFloat adjacent = viewWidth/2.0;
CGFloat opposite = viewHeight/2.0;
CGFloat hipotenuse = sqrtf(powf(adjacent, 2.0) + pow(opposite, 2.0));
CGFloat thetaRad = acosf((powf(hipotenuse, 2.0) + powf(adjacent, 2.0) - pow(opposite, 2.0)) / (2 * hipotenuse * adjacent));
CGFloat angleRad = 0.0;
if (degrees) {
angleRad = angleOfRotation*M_PI/180.0;
} else {
angleRad = angleOfRotation;
}
CGFloat widthOffset = cosf(angleRad - thetaRad) * hipotenuse;
CGFloat heightOffset = sinf(angleRad - thetaRad) * hipotenuse;
CGPoint offsetPoint = CGPointMake(viewCenter.x + widthOffset, viewCenter.y + heightOffset);
return offsetPoint;
}
Hope this helps!
You should use:
CGPoint CGPointApplyAffineTransform (
CGPoint point,
CGAffineTransform t
);
To get a specific point, use the view's bounds and center, and then apply the view's transform to get a new point after transform. This is better than adding code specifically for rotation transform, as it can support any transform as well as chaining.
All of these answers are nuts, this is so simple...
CGPoint topLeft = [rotatedView convertPoint:CGPointMake(0, 0) toView:rotatedView.superview];
CGPoint topRight = [rotatedView convertPoint:CGPointMake(rotatedView.bounds.size.width, 0) toView:rotatedView.superview];
CGPoint bottomLeft = [rotatedView convertPoint:CGPointMake(0, rotatedView.bounds.size.height) toView:rotatedView.superview];
CGPoint bottomRight = [rotatedView convertPoint:CGPointMake(rotatedView.bounds.size.width, rotatedView.bounds.size.height) toView:rotatedView.superview];
Try this code
CGPoint localBeforeTransform = CGPointMake( view.bounds.size.width/2.0f, view.bounds.size.height/2.0f ); // lower left corner
CGPoint localAfterTransform = CGPointApplyAffineTransform(localBeforeTransform, transform);
CGPoint globalAfterTransform = CGPointMake(localAfterTransform.x + view.center.x, localAfterTransform.y + view.center.y);
Why the mess and fuss? Keep it simple? Where x was before the transform, it'll be q rads/degrees further just as every other point around the anchor is.
was going to explain it all, but this chap in this post explained it in even shorter context:
Get the current angle/rotation/radian for a UIview?
CGFloat radians = atan2f(yourView.transform.b, yourView.transform.a);
CGFloat degrees = radians * (180 / M_PI);
I wrote this class that can help us:
TransformedViewFrameCalculator.h
#import <Foundation/Foundation.h>
#interface TransformedViewFrameCalculator : NSObject
#property (nonatomic, strong) UIView *viewToProcess;
- (void)calculateTransformedCornersWithTranslation:(CGPoint)translation
scale:(CGFloat)scale
rotation:(CGFloat)rotation;
#property (nonatomic, readonly) CGPoint transformedTopLeftCorner;
#property (nonatomic, readonly) CGPoint transformedTopRightCorner;
#property (nonatomic, readonly) CGPoint transformedBottomLeftCorner;
#property (nonatomic, readonly) CGPoint transformedBottomRightCorner;
#end
TransformedViewFrameCalculator.m:
#import "TransformedViewFrameCalculator.h"
#interface TransformedViewFrameCalculator ()
#property (nonatomic, assign) CGRect viewToProcessNotTransformedFrame;
#property (nonatomic, assign) CGPoint viewToProcessNotTransformedCenter;
#end
#implementation TransformedViewFrameCalculator
- (void)setViewToProcess:(UIView *)viewToProcess {
_viewToProcess = viewToProcess;
CGAffineTransform t = _viewToProcess.transform;
_viewToProcess.transform = CGAffineTransformIdentity;
_viewToProcessNotTransformedFrame = _viewToProcess.frame;
_viewToProcessNotTransformedCenter = _viewToProcess.center;
_viewToProcess.transform = t;
}
- (void)calculateTransformedCornersWithTranslation:(CGPoint)translation
scale:(CGFloat)scale
rotation:(CGFloat)rotation {
double viewWidth = _viewToProcessNotTransformedFrame.size.width * scale;
double viewHeight = _viewToProcessNotTransformedFrame.size.height * scale;
CGPoint viewCenter = CGPointMake(_viewToProcessNotTransformedCenter.x + translation.x,
_viewToProcessNotTransformedCenter.y + translation.y);
_transformedTopLeftCorner = [self calculateCoordinatesForViewPoint:CGPointMake(0, 0)
fromViewCenter:viewCenter
viewWidth:viewWidth
viewHeight:viewHeight
angleOfRotation:rotation];
_transformedTopRightCorner = [self calculateCoordinatesForViewPoint:CGPointMake(0, viewHeight)
fromViewCenter:viewCenter
viewWidth:viewWidth
viewHeight:viewHeight
angleOfRotation:rotation];
_transformedBottomLeftCorner = [self calculateCoordinatesForViewPoint:CGPointMake(viewWidth, 0)
fromViewCenter:viewCenter
viewWidth:viewWidth
viewHeight:viewHeight
angleOfRotation:rotation];
_transformedBottomRightCorner = [self calculateCoordinatesForViewPoint:CGPointMake(viewWidth, viewHeight)
fromViewCenter:viewCenter
viewWidth:viewWidth
viewHeight:viewHeight
angleOfRotation:rotation];
}
- (CGPoint)calculateCoordinatesForViewPoint:(CGPoint)viewPoint
fromViewCenter:(CGPoint)viewCenter
viewWidth:(CGFloat)viewWidth
viewHeight:(CGFloat)viewHeight
angleOfRotation:(CGFloat)angleOfRotation {
CGPoint centeredViewPoint = CGPointMake(viewPoint.x - viewWidth/2.0, viewPoint.y - viewHeight/2.0);
CGPoint rotatedCenteredViewPoint = CGPointApplyAffineTransform(centeredViewPoint, CGAffineTransformMakeRotation(angleOfRotation));
CGPoint rotatedViewPoint = CGPointMake(rotatedCenteredViewPoint.x + viewCenter.x, rotatedCenteredViewPoint.y + viewCenter.y);
return rotatedViewPoint;
}
For example, I use it to restrict the move/scale/rotation of a sticker inside a container view in the following way:
#property (nonatomic, strong) TransformedViewFrameCalculator *transformedFrameCalculator;
...
self.transformedFrameCalculator = [TransformedViewFrameCalculator new];
self.transformedFrameCalculator.viewToProcess = someView;
...
- (BOOL)transformedView:(UIView *)view
withTranslation:(CGPoint)translation
scale:(double)scale
rotation:(double)rotation
isFullyInsideValidFrame:(CGRect)validFrame {
[self.transformedFrameCalculator calculateTransformedCornersWithTranslation:translation
scale:scale
BOOL topRightIsInsideValidFrame = CGRectContainsPoint(validFrame, self.transformedFrameCalculator.transformedTopRightCorner);
BOOL topLeftIsInsideValidFrame = CGRectContainsPoint(validFrame, self.transformedFrameCalculator.transformedTopLeftCorner);
BOOL bottomRightIsInsideValidFrame = CGRectContainsPoint(validFrame, self.transformedFrameCalculator.transformedBottomRightCorner);
BOOL bottomLeftIsInsideValidFrame = CGRectContainsPoint(validFrame, self.transformedFrameCalculator.transformedBottomLeftCorner);
return topRightIsInsideValidFrame && topLeftIsInsideValidFrame && bottomRightIsInsideValidFrame && bottomLeftIsInsideValidFrame;
}

CGPoint rotation changes distance from origin

I want to rotate a CGPoint(red rect) around another CGPoint(blue rect) but it changes distance from the origin(blue rect)...when i give 270 in angle it creates the point right above the origin but when i give 90 as angle value it comes down the origin BUT CHANGES THE DISTANCE ALSO almost three times more....I want to keep the distance same and want to rotate CGPoint around another. Please guide any approach for rotation of cgpoints...
distance = 100;
angle = 270*M_PI/180;
rotatedPoint.x = initialPoint.x+distance*cos(angle);
rotatedPoint.y = initialPoint.y+distance*sin(angle);
//rotatedPoint.x = initialPoint.x+tan(angle);
[test setCenter:rotatedPoint];
[test setBackgroundColor:[UIColor redColor]];
Thanks
CGAffineTransform is a handy tool when it comes to rotation, translation, and scaling. To make sure a point is rotated properly, you must translate it to the origin, rotate it, and then translate it back.
To complete your transformation, something like the following should do the trick:
CGPoint pointToRotate = CGPointMake(30, 30);
float angleInRadians = DEGREES_TO_RADIANS(90);
CGPoint distanceFromOrigin = CGPointMake(0 - pointToRotate.x, 0 - pointToRotate.y);
CGAffineTransform translateToOrigin = CGAffineTransformMakeTranslation(distanceFromOrigin.x, distanceFromOrigin.y);
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(angleInRadians);
CGAffineTransform translateBackFromOrigin = CGAffineTransformInvert(translateToOrigin);
CGAffineTransform totalTransform = CGAffineTransformConcat(translateToOrigin, rotationTransform);
totalTransform = CGAffineTransformConcat(totalTransform, translateBackFromOrigin);
pointToRotate = CGPointApplyAffineTransform(pointToRotate, totalTransform);
And here is the documentation on CGAffineTransform, if you'd like to review it further: http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/CGAffineTransform/Reference/reference.html
Please let me know if you need anything else if this doesn't solve your problem!

Retrieving all 4 coordinates of a rotated UIImageView

How does one get the 4 coordinates for a UIImageView?
I know the CGRect can be obtained and the origin.x and origin.y, but how can all 4 corners be found?
EDIT: I am rotating the UIImageViews, thats why I asked :P
You could add width and height of the rectangle to get the coordinates of the other 3 points.
CGRect rect = view.bounds;
CGPoint topLeft = rect.origin;
CGPoint topRight = CGPointMake(rect.origin.x + rect.size.width, rect.origin.y);
CGPoint bottomLeft =CGPointMake(rect.origin.x, rect.origin.y + rect.size.height);
CGPoint bottomRight = CGPointMake(rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height);
Then you could use CGPointApplyAffineTransform to get the transformed coordinates of them under your specified transform.
CGPoint center = view.center;
CGAffineTransform transf = CGAffineTransformMakeTranslation(-rect.size.width/2,
-rect.size.height/2);
transf = CGAffineTransformConcat(transf, view.transform);
transf = CGAffineTransformTranslate(transf, center.x, center.y);
topLeft = CGPointApplyAffineTransform(topLeft, transf);
//...
(note: not tested.)
This is my solution:
[self] is a subclass of UIImageView
[self.transform] is the transform i make on [self]:
CGAffineTransform transform = CGAffineTransformMakeTranslation(-center.x, -center.y);
transform = CGAffineTransformConcat(transform, self.transform);
CGAffineTransform transform1 = CGAffineTransformMakeTranslation(center.x, center.y);
transform = CGAffineTransformConcat(transform, transform1);
CGPoint leftTopPoint = CGPointApplyAffineTransform(leftTopPoint, transform);
CGPoint rightTopPoint = CGPointApplyAffineTransform(rightTopPoint, transform);
CGPoint rightBottomPoint = CGPointApplyAffineTransform(rightBottomPoint, transform);
CGPoint leftBottomPoint = CGPointApplyAffineTransform(leftBottomPoint, transform);
You can get the size.width and size.height. Adding those to the x and y will give you the other coordinates.
Whilst these are (of course) relative to the superview, you can use the frame property to obtain a CGRect containing the origin and size of the UIImageView. You can then simply add the relevant size to the relevant origin point to obtain the full set of coordinates.
See the frame section in the UIView class reference for more information.
Construct a rotation matrix http://en.wikipedia.org/wiki/Rotation_matrix. You should calculate the initial positions of the corners relative to the point which is the center of rotation. Store those positions in an array and keep them all the time. You calculate new positions by passing the angle in a 2x2 rotation matrix and multiplying them with initial positions.
Well, given you know the angle of rotation, this is the maths to get the y coordinate of the top right corner:
Sin (angle of rotation) = height difference y / width
Therefore if you're rotating the rectangle by 10 deg and it has a width of 20pt:
Sin 10 = yDiff / 20
Which means you can do this:
yDiff = Sin 10 * 20
This gives you the difference in y from the y coordinate of the origin to the y coordinate of the top right corner. Add this value to the current y origin of your rectangle to get the actual y coordinate of your top right corner. The next step is to use pythagoras on your width and the yDiff to get the xDiff and do the same (add it to the x coordinate) to get the x coordinate of your right hand corner. I hope this makes sense.
Now you just need to do it again for each other corner - imagine, if you will, that the rectangle has rotated through 90 deg, you can just reapply the logic, however x is y and vice versa. :) etc

Set center point of UIView after CGAffineTransformMakeRotation

I have a UIView that I want the user to be able to drag around the screen.
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint pt = [[touches anyObject] locationInView:self];
CGPoint center = self.center;
CGFloat adjustmentX = pt.x - startLocation.x;
CGFloat adjustmentY = pt.y - startLocation.y;
center.x += adjustmentX;
center.y += adjustmentY;
[self setCenter:center];
}
works perfectly until I apply a transform to the view as follows:
self.transform = CGAffineTransformMakeRotation(....);
However, once the transform has been applied, dragging the UIView results in the view being moved in a exponential spiral effect.
I'm assuming I have to make an correction for the rotation in the touchesMoved method, but so far all attempts have eluded me.
You need to translate your transform to your new coordinates because a transform other than the identity is assigned to your view and moving its center alters the transform's results.
Try this instead of altering your view's center
self.transform = CGAffineTransformTranslate(self.transform, adjustmentX, adjustmentY);

Convert CGPoint between iPhone & CA planes

I have a UIView subclass that I'm drawing a PDF onto (using a CATiledLayer). I also need to draw on a specific region of that PDF, however the coordinate plane of the CATiledLayer when using CG to draw is way screwy.
See image:
I have a point (200,200), that I need to convert to the CATiledLayer's coordinate system, which is the 2nd plane shown above. I've tried doing this with some transforms, but nothing seems to work.
Thanks!
Here is what I had to do (using the example points/plane above):
//rotatation origin
CGPoint rotateOrigin = CGPointMake(0,0);
//rotatation transform
CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(rotateOrigin.x, rotateOrigin.y);
//rotate the plane 90 degrees
float radians = 90 * (M_PI / 180);
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(radians); CGAffineTransform customRotation = CGAffineTransformConcat(CGAffineTransformConcat( CGAffineTransformInvert(translateTransform), rotationTransform), translateTransform);
CGAffineTransform m1 = CGAffineTransformIdentity;
CGPoint startPoint = CGPointApplyAffineTransform(CGPointMake(200,200),m1);
//rotated point
CGPoint rotatedPoint = CGPointApplyAffineTransform(startPoint, customRotation);
//final rotated point- after adjusting for the rotation
rotatedPoint = CGPointApplyAffineTransform(rotatedPoint, CGAffineTransformMakeTranslation(500,-500));