I'm trying to rotate UIImageView for certain degrees with CGAffineTransformMakeRotation() function, but it end with imageView only rotates, but when I moved to another coordinates then imageView stretched or change height and width. I have no clue why this happens.
Here is a code:
double a = atan2(dx,dy);
bowImageView.transform = CGAffineTransformMakeRotation(a);
WHen you apply a transform to a view the geometry changes. If you wish to move that view rotated, the simplest solution is to set the transform back to CGAffineTransformIdentity, move the view and reapply the rotation.
The other way is to work out the movement based on the current rotation.....but I find the first solution easier!
Related
I have a custom view, which I use for markup (draw lines and other figures on it). The view controller recognizes touches and gestures and passes info to the view so it can draw itself properly. Each figure has a label (CATextLayer) with some figure info on it (line length for example).
I added a rotation gesture recognizer to the view controller to rotate this drawing view. I want to rotate the view, but prevent labels from rotation (so they stay 0.0 degrees relative to the superview). For this I calculate the new drawing view's angle relative to the superview and set label's angle property to the negative value, so I can rotate them oppositely. For example, the view is rotated 30 degrees, then I rotate labels -30 degrees.
In the view drawing method which is responsible for drawing figures and setting the labels, I create new transform for each label each time the view needs to be redrawn:
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformRotate(transform, self.angle); //self.angle - rotation angle in radians
transform = CGAffineTransformScale(transform, scale, scale); //scale label (need this for pinch gesture, works as expected)
label.transform = transform;
All this stuff works if I rotate labels 90 degrees. If the degree is not 0, 90, 180, etc, the label's frame changes: shrinks or enlarges, with large angles it even disappears. I understand, that when you rotate the rectangle, it's frame should get bigger as it's pointed here: Why after rotating UIImageView size is getting changed?
Is there a way to prevent CATextLayer from changing its shape when rotating?
Normal position of label:
When rotated 90 degrees (everything is nice)
Rotated to some arbitrary angle (label frame is misshapen)
The issue was occurring because I was resetting CATextLayer's origin after setting its transform. According to the transform property documentation:
When the value of this property is anything other than the identity transform, the value in the frame property is undefined and should be ignored.
To change the position of a layer, use its position property.
The frame isn't that what you expected after a non-rectangular rotation.
I see three possible solutions for your problem:
Calculate the correct size of your text, and set the frame by assigning appropriate values to position and bounds.
Create a plain CALayer for the background of each text layer, if the first solution doesn't help.
But it should be much easier if you're rotate the image only and keep the rotation of the text layers unchanged. You can achieve this by adding the text layers to the super layer of the image layer. With this setup you have just to calculate the new position of the text layers after rotation.
I'm very unsure whether the first two solutions are practicable, and I would prefer the third one.
I'm using a variant of this code to slighty rotate a UIButton about its center:
CGFloat jiggleAngle = (-M_PI * 2.0) * (1.0 / 64.0);
self.transform = CGAffineTransformRotate(self.transform, jiggleAngle);
This code generally works as expected and rotates the button in-place, about 12 degrees counter-clockwise. However, this only works if I do not reposition the button inside my layoutSubviews method. If I do any repositioning of the button at all from its initially laid-out location, the attempt to rotate above results in the button's disappearance. If the angle I choose for rotation is an exact multiple of 90 degrees, the rotation works somehow even after a move in layoutSubviews.
I understand that my button's transform is being altered in layoutSubviews and this results in the subsequent weirdness when I attempt to rotate it with the above code. I have currently worked around this problem by placing the button I wish to rotate inside another UIView and then moving that view around as desired, but I'd like a better solution that doesn't require redoing my screen layouts.
How can I adjust/alter my button's transform after a move, so that this rotation code continues to work as expected?
The problem you are facing is that a view's frame is "undefined" if it's transform isn't the identity transform.
What you should do is use the view's center property to move it around. That works even if you've changed it's transform.
You can also apply a rotation to the view's layer instead of the view (although be aware that the layer transform is a CATransform3D, a 4x4 transformation matrix instead of a 3x3, so you need different methods to manipulate it.)
I have a problem which is causing me a 'headache' and hope someone out there can help me.
I have a UIView with a child UIImageView presented on top, containing an image.
Using gestures, I can pan, scale and rotate that image. During this process, the anchor point may change to ensure the scale/rotate is point between the two fingers of the gesture. CGAffineTransforms are used to achieve this and it works well.
My problem is storing and recreating the transforms when I destroy and recreate the enclosing view controller.
When I leave that view, I store the transform and frame...
-(CGAffineTransform)imageViewTransform
{
return self.testImageView.transform;
}
-(CGRect)imageViewFrame
{
return self.testImageView.frame;
}
....and when I recreate that view I set the frame and transform of my imageView...
self.imageView.transform = mySavedTransform;
self.imageView.frame = mySavedFrame;
This works and I can move and stretch my image, then recreate next time in.
However, if I rotate my image, the recreated image frame at the correct angle but is stretched and not at original coordinates, so I don't get exactly what I saved.
Setting the frame before the transform has the same undesired effect on rotations.
My question is why can't I recreate the exact scaled, rotated and positioned UIImageView using the above technique?
Surely reproducing the frame and transform would have the desired effect?
Is this the usual method for doing this, or am I missing something?
Any help appreciated.
For those interest, this is sorted.
The problem was the anchor-point which was not being stored, as well as using the center for recreation of the image. So, to faithfully reproduce a transformation, it appears you need to store the center, transform and anchor.
HTH
I am rotating the image on slider value -
I am using this code for rotation -
editingView.transform = CGAffineTransformRotate(editingView.transform,sliderVal);
its Rotating properly but if i am trying to move or resize after rotation,The editingView is resizing with unexpected behavior and view disappears from screen.
Please suggest me what i am doing wrong.
Well whenever you rotate a view which is inside a superview, you should preserve the position of the view. If you are not rotating any view across the origin then, you should first translate the view's origin to the superview's origin and then rotate and then again translate back to the original point.
Find the coordinate if the view you want to rotate with respect to its superview, say it (x,y).
Translate the view to the origin as;
view.transform = CGAffineTransformMakeTranslation(x,y)
Rotate the view by some angle, say PI,
view.transform = CGAffineTransformMakeRotation(PI)
After rotation translate back to the original point as;
view.transform = CGAffineTransformMakeTranslation(-x,-y)
And that's it. It should work with all the different rotation.
Have a look at this question.
Since you do not show any code on how you do the move or resizing, I suspect you are not properly concatenating the transforms. Furthermore, after you did the rotation, a translation will possibly work on the rotated coordinate system, therefore leading to unexpected behaviour.
Changing transform value affecting the frame value of UIView. As Apple says:
Warning: If this property is not the identity transform, the value of
the frame property is undefined and therefore should be ignored.
Apple docs
So, if you are moving or resizing your view using frame property, try do this with bounds, center properties
I think my question can be summed up as how to store and reset the transform of a view. But then perhaps explaining my situation might help.
If I apply the transforms below to a view, one after another (like if I add this code to a switch or a button). I get exactly the result I would expect: the scale switches between: a view that is .55 times the size of the original view, and the view at it's original scale. Works to scale sub-views of someView too, just as I want. Ad infinitum. Perfect.
//tranformScale 1
someView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.55, 0.55 );
//tranformScale 2
someView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.0, 1.0 );
The trouble is I want to use this code (or similar) to scale a sub-view of self.view when an iOS device goes into landscape, to fit the sub-view to the landscape screen (and back up when in portrait). It almost works, but for some reason instead of outputting round values values for the frame of the view being scaled (as happens with a test using a button to call the transforms), progressively strange values are produced. Eventually, after about 4 rotations the sub-view flies off screen. I suspect it has to do with self.view changing shape, but then again, when I log the frame of self.view it's shape is very predictable.
By the way I am centering the view using autoresizingMask flexible margins, not using auto-layout. perhaps I should be centering the view with another type of calculation?
Thanks for reading!
I did this and it worked perfect.
self.imageview.transform = CGAffineTransformIdentity
(1) be sure not to set or get any frame data after applying transforms, it is unsupported and will yield unpredictable results;
(2) turn off your autoresizing mask flex margins and center like this:
view.center = CGPointMake(view.superview.bounds.size.width/2,
view.superview.bounds.size.height/2);
(take care to apply centering while view is at full size. i.e. BEFORE the transform if it is the resize transform, AFTER the transform if it is the identity transform)