CGAffineTransformScale modified after device rotation - iphone

I have a view that I am performing a transform on
originalTransform = self.appContainer.transform;
self.appContainer.transform = CGAffineTransformMakeScale(.8, .8);
If I do not rotate the device I can revert this back to the original by using
self.appContainer.transform = CGAffineTransformScale(originalTransform, 1, 1);
Unfortunately, if I rotate the device, the transformation matrix for the view gets modified by the view rotation and breaks the original scale. If I undo the scale after rotation I am left with the UIView not returning to its original full size.
I'm sure that I need to make calculations between the original transformation matrix and the new one but I am unsure how to do this. The only thing I have been able to do to make this work is to revert the scale back to 1 before rotation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
self.appContainer.transform = CGAffineTransformScale(originalTransform, 1, 1);
}
And then rescale it with the newly modified transformation after the
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
self.appContainer.transform = CGAffineTransformMakeScale(.8, .8);
}
This makes it work as I expect, but instead of going back to full screen, and then back to .8 scale, I'd like it to go from its new transformation to the correct .8 scale.

If you want to revert it to original, try setting the transform to CGAffineTransformIdentity.
self.appContainer.transform = CGAffineTransformIdentity;

I think you have to remove all autoresizing masks using.....
[self.appContainer setAutoresizingMask:UIViewAutoresizingNone];

Related

Rotate an image and get back to its original position - opengles glkit

I need to rotate an image in opengles GLkit and get it back to its original position in GLkit.
rotation += 5;
_modelViewMatrix = GLKMatrix4Rotate( _modelViewMatrix, GLKMathDegreesToRadians(5), 1, 0, 0);
_modelViewMatrix = GLKMatrix4Rotate( _modelViewMatrix, GLKMathDegreesToRadians(rotation), 1,0,0);
I need to move it in x axis for certain amount and getting back to its original position from where it started. How should i do it?

CATransform3D transform

I'm trying to add perspective to a view by using CATransform3D. Currently, this is what I'm getting:
And this is what I wanna get:
I'm having a hard time doing that. I'm completely lost here. Here's my code:
CATransform3D t = CATransform3DIdentity;
t.m11 = 0.8;
t.m21 = 0.1;
t.m31 = -0.1;
t.m41 = 0.1;
[[viewWindow layer] setTransform:t];
Matrix element .m34 is responsible for perspective. It's not discussed much in the documentation, so you'll have to toy with it. This answer talks a little bit about how to use it: https://stackoverflow.com/a/7596326/1228525
To actually see the effects of that matrix you need to do two things:
1. Apply that perspective matrix to the parent view's sublayer transform
2. Rotate the child view (the one on which you want perspective) - otherwise it will remain flat and you won't be able to tell it now has a 3D perspective.
The numbers are arbitrary, make them whatever looks best:
CATransform3D t = CATransform3DIdentity;
t.m34 = .005;
parentView.layer.sublayerTransform. = t;
childview.layer.transform = CATransform3DMakeRotation(45,1,0,0);
The perspective will look different depending on where the child is in the parent view. If the child is in the center of the parent it will be like you are looking at the child view in 3D straight on. The further from the center it is, the more it will be like you are viewing from a glancing angle.
This is what I got using the above code and centering the child view: (apparently I'm not allowed to post pictures since I'm new, so you'll have to see the link) http://i.stack.imgur.com/BiYCS.png
It's very hard to tell what you're going for based on those pictures; a bit more explanation might be helpful if my answer isn't what you want. From what I can tell from the picture, the bottom one isn't perspective...
I was able to easily achieve the right CATransform3D using AGGeometryKit.
#import <AGGeometryKit/AGGeometryKit.h>
UIView *view = ...; // create a view
// setting anchorPoint to zero
view.layer.anchorPoint = CGPointZero;
view.layer.transform = CATransform3DMakeTranslation(-view.layer.bounds.size.width * .5, -view.layer.bounds.size.height * .5, 0);
// setting a trapezoid transform
AGKQuad quad = view.layer.quadrilateral;
quad.tl.x -= 10; // shift top left x-value with 10 pixels
view.layer.quadrilateral = quad; // the quad is converted to CATransform3D and applied

Moving/Rotating an object using it's updated position and tilt?

This is basically a simple issue, which I can't get around ...
So, I have an object of UIImageView of a certain frame over which, I implement CAAnimation with rotation and translation, and it is at new coordinates (x,y), and has been rotated by some degrees.
This animation works beautifully. But if I again do a rotation and movement from THAT state, I want the object to use the new frame and new properties from STEP 1 after its rotation and again rotate by the new angle.
As of now, when I try rotation again, it uses its standard state & frame size(during initialization) and performs rotation on it...
And by this I mean... If I have a square of frame (100, 200, 10, 10), and I rotate it by 45 degrees, the shape is now a different square, with different frame size and end points compared to the original square, and I implement a new rotation by (say) 152 degrees on it and it needs to perform a rotation on the newer square... But it turns out that it uses the same frame size as the previous one (x,y, 10, 10).
How can I continue rotating / moving the object with its updated position and state ??
Note: (if you need to see the code for animation)
This is the code for my animation, which involves simple rotation and movement ! http://pastebin.com/cR8zrKRq
You need to save the rotation step and update object rotation in animationDidStop: method. So, in your cas, you should apply:
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
//angle is global
CATransform3D rotationTransform = CATransform3DMakeRotation((angle%4)*M_PI_4, 0, 0, 1);
[object.layer setTransform:rotationTransform];
object.center = tempFrame; //already there
}
where angle is an integer counter of animations(steps) with values 0,1,2,3. My step is M_PI_4. There is probably a better solution to the problem, but this should do the trick

Rotating UILabels from any position to upright

I have a label that I rotate using
pieceBlack.transform = CGAffineTransformMakeRotation((M_PI * (180) / 180.0));
and that works perfectly, EXCEPT:
I rotate this label during the game to either right side up or upside down. How do I say, "Whatever angle you are at, go back to upright." I'm thinking maybe like an:
int PreviousAngle = ?;
pieceBlack.transform = CGAffineTransformMakeRotation(degreesToRadian(0-PreviousAngle));
so I guess what I'm asking is how you ask for the rotation angle. Or, alternately, maybe there is a sort of
pieceBlack.transform = CGAffineTransformMakeRotation(RotateToUpright);
From what I remember transform is always relative from the upright position (original), so 0.0f? So you can just do pieceBlack.transform = CGAffineTransformIdentity
What I did was to first position the (in my case) view in the "straight up" orientation. Then I used the CGAffineTransformMakeRotation to create the somewhat off-kilter view. Finally, I applied the identity transform to bring the view back to its straight-up position.
you don't want to set the transform, you want to modify it,
view.transform = CGAffineTransformRotate(view.transform, angle);
if you need to keep the old one around, then do

Two-finger rotation gesture on the iPhone?

I'm working on an iPhone app with a lot of different gesture inputs that you can do. Currently there is single finger select / drag, two finger scroll, and two finger pinch zoom-in / zoom-out. I want to add in two finger rotation (your fingers rotate a point in between them), but I can't figure out how to get it to work right. All the other gestures were linear so they were only a matter of using the dot or cross product, pretty much.
I'm thinking I've got to store the slope between the previous two points of each finger, and if the angle between the vectors is near 90, then there is the possibility of a rotation. If the next finger movement angle is also near 90, and the direction of the vector on one finger changed positively and changed negatively, then you've got a rotation. The problem is, I need a really clean distinction between this gesture and the other ones - and the above isn't far enough removed.
Any suggestions?
EDIT: Here's how I did it in a vector analysis manner (as opposed to the suggestion below about matching pixels, note that I use my Vector struct in here, you should be able to guess what each function does):
//First, find the vector formed by the first touch's previous and current positions.
struct Vector2f firstChange = getSubtractedVector([theseTouches get:0], [lastTouches get:0]);
//We're going to store whether or not we should scroll.
BOOL scroll = NO;
//If there was only one touch, then we'll scroll no matter what.
if ([theseTouches count] <= 1)
{
scroll = YES;
}
//Otherwise, we might scroll, scale, or rotate.
else
{
//In the case of multiple touches, we need to test the slope between the two touches.
//If they're going in roughly the same direction, we should scroll. If not, zoom.
struct Vector2f secondChange = getSubtractedVector([theseTouches get:1], [lastTouches get:1]);
//Get the dot product of the two change vectors.
float dotChanges = getDotProduct(&firstChange, &secondChange);
//Get the 2D cross product of the two normalized change vectors.
struct Vector2f normalFirst = getNormalizedVector(&firstChange);
struct Vector2f normalSecond = getNormalizedVector(&secondChange);
float crossChanges = getCrossProduct(&normalFirst, &normalSecond);
//If the two vectors have a cross product that is less than cosf(30), then we know the angle between them is 30 degrees or less.
if (fabsf(crossChanges) <= SCROLL_MAX_CROSS && dotChanges > 0)
{
scroll = YES;
}
//Otherwise, they're in different directions so we should zoom or rotate.
else
{
//Store the vectors represented by the two sets of touches.
struct Vector2f previousDifference = getSubtractedVector([lastTouches get:1], [lastTouches get:0]);
struct Vector2f currentDifference = getSubtractedVector([theseTouches get:1], [theseTouches get:0]);
//Also find the normals of the two vectors.
struct Vector2f previousNormal = getNormalizedVector(&previousDifference);
struct Vector2f currentNormal = getNormalizedVector(&currentDifference );
//Find the distance between the two previous points and the two current points.
float previousDistance = getMagnitudeOfVector(&previousDifference);
float currentDistance = getMagnitudeOfVector(&currentDifference );
//Find the angles between the two previous points and the two current points.
float angleBetween = atan2(previousNormal.y,previousNormal.x) - atan2(currentNormal.y,currentNormal.x);
//If we had a short change in distance and the angle between touches is a big one, rotate.
if ( fabsf(previousDistance - currentDistance) <= ROTATE_MIN_DISTANCE && fabsf(angleBetween) >= ROTATE_MAX_ANGLE)
{
if (angleBetween > 0)
{
printf("Rotate right.\n");
}
else
{
printf("Rotate left.\n");
}
}
else
{
//Get the dot product of the differences of the two points and the two vectors.
struct Vector2f differenceChange = getSubtracted(&secondChange, &firstChange);
float dotDifference = getDot(&previousDifference, &differenceChange);
if (dotDifference > 0)
{
printf("Zoom in.\n");
}
else
{
printf("Zoom out.\n");
}
}
}
}
if (scroll)
{
prinf("Scroll.\n");
}
You should note that if you're just doing image manipulation or direct rotation / zooming, then the above approach should be fine. However, if you're like me and you're using a gesture to cause something that takes time to load, then it's likely that you'll want to avoid doing the action until that gesture has been activated a few times in a row. The difference between each with my code is still not perfectly separate, so occasionally in a bunch of zooms you'll get a rotation, or vise versa.
I've done that before by finding the previous and current distances between the two fingers, and the angle between the previous and current lines.
Then I picked some empirical thresholds for that distance delta and angle theta, and that has worked out pretty well for me.
If the distance was greater than my threshold, and the angle was less than my threshold, I scaled the image. Otherwise I rotated it.
2 finger scroll seems easy to distinguish.
BTW in case you are actually storing the values, the touches have previous point values already stored.
CGPoint previousPoint1 = [self scalePoint:[touch1 previousLocationInView:nil]];
CGPoint previousPoint2 = [self scalePoint:[touch2 previousLocationInView:nil]];
CGPoint currentPoint1 = [self scalePoint:[touch1 locationInView:nil]];
CGPoint currentPoint2 = [self scalePoint:[touch2 locationInView:nil]];
Two fingers, both moving, opposit(ish) directions. What gesture conflicts with this?
Pinch/zoom I guess comes close, but whereas pinch/zoom will start off moving away from a center point (if you trace backwards from each line, your lines will be parallel and close), rotate will initially have parallel lines (tracing backwards) that will be far away from each other and those lines will constantly change slope (while retaining distance).
edit: You know--both of these could be solved with the same algorithm.
Rather than calculating lines, calculate the pixel under each finger. If the fingers move, translate the image so that the two initial pixels are still under the two fingers.
This solves all two-finger actions including scroll.
Two-finger scroll or Zoom might look a little wobbly at times since it will do other operations as well, but this is how the map app seems to work (excluding the rotate which it doesn't have).