UIRotationGestureRecognizer changes with CGAffineTransformMakeScale - ios

A view is flipped using this:
self.transform = CGAffineTransformMakeScale(-1, 1); // self is an UIView
To rotate this view:
-(void)handleRotate:(UIRotationGestureRecognizer *)recognizer
{
recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);
recognizer.rotation = 0;
}
The issue is that after the view is flipped so is the rotation's direction. Any solution how to fix this?
Edit: My current solution is using a boolean and negate the recognizer.rotation value in handleRotate method. But I am still looking for the technical solution.

Have you try self.transform.a * recognizer.rotation ?
If I understand correctly the AffineTransform, the X scaling is store at a.
CGAffineTransform Reference Look for CGAffineTransformMakeScale and CGAffineTransformMake

Related

Combining a UIView perspective transform with other CATranform3Ds

I'm writing an app such that sprites (subclasses of UIImageView) can be rotated, resized, and panned across the screen using gestures. I also would like to be able to apply a 3D perspective transformation to the sprites.
I have the rotate/resize/pan functionality working correctly, as well as the perspective transform. However, they don't seem to work together correctly. If I rotate an unmodified sprite, then try to skew it, the sprite 'resets' it's rotation, then applies the perspective. The opposite works though; if I skew first, I can apply any 2D transformation after without it resetting.
Here is the code I'm using: (rotate, resize, and pan are done using UIGestureRecognizers, whereas the skew uses a UISlider).
Rotate:
- (void)didRotateSprite:(UIRotationGestureRecognizer *)rotate
{
CGFloat angle = rotate.rotation;
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DRotate(spriteView.layer.transform, angle, 0, 0, 1);
spriteView.layer.transform = transform;
rotate.rotation = 0.0;
}
Resize:
- (void)didPinchSprite:(UIPinchGestureRecognizer *)pinch
{
CGFloat scale = pinch.scale;
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DScale(spriteView.layer.transform, scale, scale, 1);
view.layer.transform = transform;
pinch.scale = 1.0;
}
Perspective:
- (IBAction)perspectiveChanged:(UISlider *)slider
{
CATransform3D transform = CATransform3DIdentity;
transform.m34 = 1.0 / -100;
transform = CATransform3DRotate(transform, (1 - (slider.value * 2)) * M_PI_2, 1, 0, 0);
spriteView.layer.transform = transform;
}
Thank you!
Found the answer with a lot of debugging and the help of this question. The trick was to transform the view's perspective using:
spriteView.superview.layer.sublayerTransform = transform;
This recursively applies the transformation to the view's superview and any subviews contained in it. For more information about this, check out the documentation and Apple's Layer Style Properties guide too.

Objective-C Transform to X and Y

I have been researching CGAffineTransforms and was wondering if there was a way to take your view and zoom in on an x,y coordinate. I have the scaling portion down with the function:
CGAffineTransformMakeScale(4.00 ,4.00);
However I am uncertain how to tie the scaling with a possible x,y coordinate. Has anyone ever done something like this? Am I incorrect in the use of these function possibly?
-(void)buttonSelected:(id)sender
{
UIButton *b = sender;
CGPoint location = b.frame.origin;
[UIView animateWithDuration:1.3f delay:0.0f options:UIViewAnimationCurveEaseIn animations:^{
CGAffineTransform totalTransform =
CGAffineTransformMakeTranslation(-location.x , -location.y );
totalTransform = CGAffineTransformScale(totalTransform, 4.0f, 4.0f);
totalTransform = CGAffineTransformTranslate(totalTransform, location.x , location.y );
[self.view setTransform:totalTransform];
}completion:^(BOOL finished) {
}];
}
You would either construct a transform that performed the three steps:
move point you want to scale around to the centre of the layer;
scale;
move the object back so that the original centre is back in the centre.
So, e.g.
// to use a point that is (109, 63) from the centre as the point to scale around
CGAffineTransform totalTransform =
CGAffineTransformMakeTranslation(-109.0f, -63.0f);
totalTransform = CGAffineTransformScale(totalTransform, 4.0f, 4.0f);
totalTransform = CGAffineTransformTranslate(totalTransform, 109.0f, 63.0f);
Or, arguably more simply adjust the view.layer's anchorPoint. The gotcha with the second idea is that when you first adjust the anchor point you'll get an immediate transform because all other positioning is in terms of the centre.

ImageView rotation using slider

I am trying to rotate an image using slider..its working well but when i zoom in or zoom out that imageView using Pinch gesture and then try to rotate it using Slider then it resizes the imageView's frame to its original frame and then rotate it..i Want it to rotate the new imageview after performing zoom operation...heres my code.
//for rotation
- (IBAction)sliderChanged:(id)sender
{
imageView.transform = CGAffineTransformMakeRotation(Slider.value * 2*M_PI / Slider.maximumValue);
}
//for zoom in zoom out
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer
{
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
recognizer.scale = 1;
}
In handlePinch you correctly modify existing transform, but in sliderChanged you create new transform, losing scaling.
You are not maintaining the scale. an example of something that works:
-(IBAction)zoomIn:(id)sender{
x += 0.3;
CGAffineTransform t;
t=CGAffineTransformMakeRotation(degrees*M_PI/180);
imageView.transform=CGAffineTransformScale(t, x, x);
}
you are using the recognizer.view.transform in the CGAffineTransformScale. Try using the imageView.transform

UIScrollView setZoomScale sets the applied rotation back to Zero

I've been working on a maps replacement for quite a while now. The whole thing works with a UIScrollView backed by a CATiledLayer.
To rotate my map, i rotate the layer itself. (Using CATransform3DMakeRotation) Works pretty well so far =)
But if I ever call setZoomScale method the CATransform3D that is going to be submitted to my layer is resetting the rotation to 0.
My question is, is there any way to set the zoomscale of my scrollView without loosing the applied rotation?
The same problem exists for the pinch gestures.
//Additional Infos
To rotate around the current Position, i have to edit the anchor point. Maybe this is a problem for the scaling, too.
- (void)correctLayerPosition {
CGPoint position = rootView.layer.position;
CGPoint anchorPoint = rootView.layer.anchorPoint;
CGRect bounds = rootView.bounds;
// 0.5, 0.5 is the default anchorPoint; calculate the difference
// and multiply by the bounds of the view
position.x = (0.5 * bounds.size.width) + (anchorPoint.x - 0.5) * bounds.size.width;
position.y = (0.5 * bounds.size.height) + (anchorPoint.y - 0.5) * bounds.size.height;
rootView.layer.position = position;
}
- (void)onFinishedUpdateLocation:(CLLocation *)newLocation {
if (stayOnCurrentLocation) {
[self scrollToCurrentPosition];
}
if (rotationEnabled) {
CGPoint anchorPoint = [currentConfig layerPointForLocation:newLocation];
anchorPoint.x = anchorPoint.x / rootView.bounds.size.width;
anchorPoint.y = anchorPoint.y / rootView.bounds.size.height;
rootView.layer.anchorPoint = anchorPoint;
[self correctLayerPosition];
}
}
You can implement scrollViewDidZoom: delegate method and concatenate the two transforms to achieve desired effect:
- (void) scrollViewDidZoom:(UIScrollView *) scrollView
{
CATransform3D scale = contentView.layer.transform;
CATransform3D rotation = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
contentView.layer.transform = CATransform3DConcat(rotation, scale);
}
EDIT
I've got simpler idea! How about adding another view to the hierarchy with the rotation transform attached? Here's the proposed hierarchy:
ScrollView
ContentView - the one returned by viewForZoomingInScrollView:
RotationView - the one with rotation transform
MapView - the one with all the tiles
I don't think that performance should be any concern here and it's worth trying.

rotate UIImageView around an arbitrary point

I have a UIImageView that I rotate around its center:
imageHorizon.layer.anchorPoint = CGPointMake(0.5, 0.5);
imageHorizon.transform = CGAffineTransformRotate(imageHorizon.transform, angleToRotate*(CGFloat)(M_PI/180));
Sometimes I also move this image to the left or right and then rotate again. I would like to keep the rotation center all the time on the same point (which is actually the center of the super view). How can I do that ?
cheers,
self.imgView.layer.anchorPoint = CGPointMake(0.0,1.0);
self.imgView.layer.position = CGPointMake(100,200.0);
CGAffineTransform cgaRotateHr = CGAffineTransformMakeRotation(-(3.141/4));
[self.imgView setTransform:cgaRotateHr];
This is an older question, but the other solutions did not work well for me, so I came up with another solution:
Rotating an image is essentially just a normal rotation with a translation applied, ensuring that the point you want to rotate around is still in the same spot after the rotation. To do this, calculate the position's CGPoint in your image before the rotation, get the position after the rotation, and apply the difference as a translation on the image, "snapping" it into the right position. Here is the code that I've been using:
Keep in mind that the translation should be applied via CGAffineTransform, not moving the .center, because the translation will need to be relative to the rotation, and CGAffineTransformTranslate() takes care of that.
// Note: self is the superview of _imageView
// Get the rotation point
CGPoint rotationPointInSelf = self.center; // or whatever point you want to rotate around
CGPoint rotationPointInImage = [_imageView convertPoint:rotationPointInSelf fromView:self];
// Rotate the image
_imageView.transform = CGAffineTransformRotate(_imageView.transform, angle);
// Get the new location of the rotation point
CGPoint newRotationPointInImage = [_imageView convertPoint:rotationPointInSelf fromView:self];
// Calculate the difference between the point's old position and its new one
CGPoint translation = CGPointMake(rotationPointInImage.x - newRotationPointInImage.x, rotationPointInImage.y - newRotationPointInImage.y);
// Move the image so the point is back in it's old location
_imageView.transform = CGAffineTransformTranslate(_imageView.transform, -translation.x, -translation.y);
You can make the image a subview of another view and then rotate the superview to get that effect. Another approach is to set the anchorPoint property as described in the docs.
I'm using this code to rotate around the point (0,0).
Maybe it help you figure out how to active what you want.
float width = self.view.frame.size.width;
float height = self.view.frame.size.height;
CGRect frame_smallView = CGRectMake(-width, -height, width, height);
UIView *smallView = [[UIView alloc] initWithFrame:frame_smallView];
smallView.backgroundColor = darkGrayColor;
// Select x and y between 0.0-1.0.
// The default is (0.5f,0.5f) that is the center of the layer
// (1.0f,1.0f) is the right bottom corner
smallView.layer.anchorPoint = CGPointMake(1.0f, 1.0f);
// Rotate around this point
smallView.layer.position = CGPointMake(0, 0);
[self.view insertSubview:smallView belowSubview:self.navBar];
[UIView animateWithDuration:1
animations:^{
smallView.transform = CGAffineTransformMakeRotation(M_PI);
}
completion:^(BOOL finished){
[self.navigationController popViewControllerAnimated:NO];
}];

Resources