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
Related
I'm working on an app that lets the user resize and rotate a photo using UIGestureRecognizers. I have this code which adjusts the anchorPoint based on where the user is applying touches (to make it look like they're scaling the image at the point where their fingers actually are):
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
UIView *gestureRecognizerView = gestureRecognizer.view;
CGPoint locationInView = [gestureRecognizer locationInView:gestureRecognizerView];
CGPoint locationInSuperview = [gestureRecognizer locationInView:gestureRecognizerView.superview];
gestureRecognizerView.layer.anchorPoint = CGPointMake(locationInView.x / gestureRecognizerView.bounds.size.width, locationInView.y / gestureRecognizerView.bounds.size.height);
gestureRecognizerView.center = locationInSuperview;
}
Later on, I'm simply wanting to calculate the origin based on the center and bounds with this code:
CGRect transformedBounds = CGRectApplyAffineTransform(view.bounds, view.transform);
CGPoint origin = CGPointMake(view.center.x - (transformedBounds.size.width * view.layer.anchorPoint.x), view.center.y - (transformedBounds.size.height * view.layer.anchorPoint.y));
And it's coming out incorrectly (I'm comparing against the frame value which ironically is supposed to be invalidated but actually does have the correct value).
So all in all I'm wondering, what am I not taking into account here? How is the anchorPoint influencing the center in a way I'm not able to determine?
I think the problem is that the origin you are calculating is not really an origin, but rather an offset of the origin of your transformedBounds rect.
I haven't fully tested it, but if you do something like this you should get the correct frame:
CGRect transformedBounds =
CGRectApplyAffineTransform(view.bounds, view.transform);
CGSize originOffset = CGSizeMake(
view.center.x - (transformedBounds.size.width * view.layer.anchorPoint.x),
view.center.y -
(transformedBounds.size.height * view.layer.anchorPoint.y));
transformedBounds.origin.x += originOffset.width;
transformedBounds.origin.y += originOffset.height;
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
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.
I have an imageview that I am scaling and rotating via UISliders. Here is how I do so:
- (IBAction)sizeSlider:(UISlider *)sender
{
int SCALE_MAX = 200;
int SCALE_MIN = 10;
CGAffineTransform transform = image.transform;
float scale = sqrt(transform.a*transform.a + transform.c*transform.c);
if (scale > SCALE_MAX)
image.transform = CGAffineTransformScale(transform, SCALE_MAX/scale*sender.value, SCALE_MAX/scale*sender.value);
else if (scale < SCALE_MIN)
image.transform = CGAffineTransformScale(transform, SCALE_MIN/scale*sender.value, SCALE_MIN/scale*sender.value);
}
- (IBAction)angleSlider:(UISlider *)sender
{
CGAffineTransform t = CGAffineTransformMakeRotation(sender.value);
image.transform = t;
}
This works, however if the imageview has been scaled from its original size then rotating the image resets it to its original size. How can I prevent this?
Use the similar approach of your scale in your rotation. Meaning, use:
CGAffineTransformRotate(image.transform, theAngle);
CGAffineTransformMakeRotation
creates a new transform based on identity. Scaling is part of a transform--you want to use
CGAffineTransformRotate
instead which adds rotation to the existing, scaled transform.
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];
}];