UIImageView height and width changes after rotation - ios

Hey guys I've been trying to use two UISliders to manipulate one UIImageView. One slider to scale the image and one to rotate it. However, when I switch from one slider to the other it resets the previous rotation. (So if I scale the image to say 125x125 when its normally 100x100 and then go to use the other slider to rotate the image, it rotates it but changes its size back to 100x100 and vice versa). So anyways, Ive tried using CGAffineTransformConcat to combine the two transformations, Ive tried setting the frame of the ImageView after every transformation, I've tried deleting and readding the imageView to _myArray to "save" it, and I briefly tried using anchorpoints but all with no luck. So my question is, what am I doing wrong??? I feel like my code should work and each action shouldn't reset my UIImageView but I have no idea why it won't.
- (IBAction)scaleImage:(UISlider *)sender {
NSLog(#"ScaleImage Called");
UIImageView *selectedImage = [_myArray objectAtIndex:0];
CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformIdentity, sender.value, sender.value);
selectedImage.transform = transform;
_scaleValue = sender.value;
//_width = selectedImage.frame.size.width
//_height = selectedImage.frame.size.height
_sizeLabel.text = [NSString stringWithFormat:#"%d", _scaleValue];
}
- (IBAction)rotateImage:(UISlider *)sender {
UIImageView *selectedImage = [_myArray objectAtIndex:0];
//selectedImage.frame = CGRectMake(selectedImage.frame.origin.x, selectedImage.frame.origin.y, _width, _height);
selectedImage.transform = CGAffineTransformMakeRotation(sender.value * 2*M_PI / sender.maximumValue);
_rotationLabel.text = [NSString stringWithFormat:#"%d", _rotationValue];
_rotationValue = sender.value;
}
Does anyone know why each method resets the others previous transformation.

If you look at your code, you will notice that you are explicitly setting the transform to the new value across the Identity transform.
CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformIdentity, sender.value, sender.value);
What you will want to do is apply the two together... you could save a rotation transform and a scale transform as properties and the IBActions would update those and then set the transform as a concatenation of the two.
So, the scale would look like:
self.scaleValue = sender.value;
And the rotation would look like:
self.rotateValue = sender.value * 2*M_PI / sender.maximumValue;
And you would have another method called transform
-(void)transform
{
CGAffineTransform fullTransform = CGAffineTransformMakeRotation(self.rotateValue);
CGAffineTransformScale(fullTransform, self.scaleValue, self.scaleValue);
UIImageView *selectedImage = [_myArray objectAtIndex:0];
selectedImage.transform = fullTransform;
}
You would then call that transform method in the IBAction calls at the end instead of trying to set the transforms separately

Related

Set transform of view as another view

I want to set a transform of a view as another view. For example I have UIImageView and I want to have the same transform in another UIImageView like in the first one.
I try to make it by using CGAffineTransformMakeRotation, but I don't know how to get the relative angle of the first UIImageView, to make the rotation. Maybe it can be done in different way?
I made a picture to explain the effect which I want to get:
Best regards,
EDIT: I want to set second one in the same position like the first one. Not in the same place, but with the same angle for example to the border of screen.
It's simply just need to assign the transform of one view to other.
eg.
self.imageView1.transform = CGAffineTransformMakeRotation( 45.0/180*M_PI );
self.imageView2.transform = self.imageView1.transform;
imageView2 will same transform as ImageView1.
okay, u can get the transform of your second image view (20) marked image use that transform to set 4th image view (in your pic) for example,
CGAffineTransform currentTransform = self.imageView_2.transform; //say image view rotated transform
self.imageView_4.transform = currentTransform; //last image set its transform to any view that u want to rotate
Edit:2
i am not get what u want exactly, if u want to place it somewhere, first set the frame and after that apply transform, like
self.imageView_3.frame = self.imageView_1.frame; //set the place where u want to place,
CGAffineTransform currentTransform = self.imageView_2.transform;
self.imageView_3.transform = currentTransform;
edit 3
if u want to rotate back to normal then use like below
self.imageView_3.frame = self.imageView_1.frame; //set the place where u want to place,
CGAffineTransform currentTransform = self.imageView_2.transform;
self.imageView_3.transform = CGAffineTransformIdentity;//currentTransform; //brings back to original
//set the position where u want to place
Here using property transform and method CGAffineTransformMakeRotation of imageView can do that.
e.g.
self.imageView.transform = CGAffineTransformMakeRotation( rotation degree in decimal /180*M_PI);
use animation block to animate accordingly.
[UIView animateWithDuration:1.0 animations:^{
self.imageView.transform = CGAffineTransformMakeRotation( 180.0/180*M_PI);
} completion:^(BOOL finished){
}];
Edit:
In order to rotate imageView same as a another one you can do this by assign transform property of rotated imageView see code below.
self.imageView2.transform = self.imageView1.transform;

How do we rotate 2 UIView planes simultaneously in 3D space

I'm trying to create a "page flip effect" using UIView instead of CALayer due to a project limitation. This requires flipping 1 UIView 180 degrees and essentially "sticking it" to the back of the other UIView. You then rotate the two UIViews simultaneously by rotating the superview in 3D space.
I'm trying to port AFKPageFlipper's "initFlip" method to use UIView instead of UIImage.
Below is a snippet of my attempt to port it. The initial page flip works, but the "front layer" in the code doesn't seem to show up. As if I"m not able to see the backend of the page. When I'm flipping the page, the animation is initially correct (back layer is fine), but then the other side of the page (front layer), I see the inverted view of the first page (backLayer).
Any help would be awesome!
flipAnimationLayer = [[UIView alloc] init];
flipAnimationLayer.layer.anchorPoint = CGPointMake(1.0, 0.5);
flipAnimationLayer.layer.frame = rect;
[self addSubview:flipAnimationLayer];
UIView *backLayer;
UIView *frontLayer;
if (flipDirection == AFKPageFlipperDirectionRight)
{
backLayer = currentViewSnap2;
backLayer.layer.contentsGravity = kCAGravityLeft;
frontLayer = nextViewSnap2;
frontLayer.layer.contentsGravity = kCAGravityRight;
}else
{
backLayer = nextViewSnap2;
backLayer.layer.contentsGravity = kCAGravityLeft;
frontLayer= currentViewSnap2;
frontLayer.layer.contentsGravity = kCAGravityRight;
}
backLayer.frame = flipAnimationLayer.bounds;
backLayer.layer.doubleSided = NO;
backLayer.clipsToBounds = YES;
[flipAnimationLayer addSubview:backLayer];
frontLayer.frame = flipAnimationLayer.bounds;
frontLayer.layer.doubleSided = NO;
frontLayer.clipsToBounds = YES;
frontLayer.layer.transform = CATransform3DMakeRotation(M_PI, 0, 1.0, 0);
[flipAnimationLayer addSubview:frontLayer];
if (flipDirection == AFKPageFlipperDirectionRight)
{
CATransform3D transform = CATransform3DMakeRotation(0.0, 0.0, 1.0, 0.0);
transform.m34 = 1.0f / 2500.0f;
flipAnimationLayer.layer.transform = transform;
currentAngle = startFlipAngle = 0;
endFlipAngle = -M_PI;
} else
{
CATransform3D transform = CATransform3DMakeRotation(-M_PI / 1.1, 0.0, 1.0, 0.0);
transform.m34 = 1.0f / 2500.0f;
flipAnimationLayer.layer.transform = transform;
currentAngle = startFlipAngle = -M_PI;
endFlipAngle = 0;
}
Your code is rotating layers, not views. That's fine.
I would not expect the code you posted to animate, since a layer's backing view doesn't do implicit animation, You could make it animate by using a CABasicAnimation. Or, you could create layers for your front and back views and attach them as sublayers of your view's layers. If you do that than manipulating the transform on the layers will use implicit animations.
What I've done to create my own font-to-back flip as you describe is to fake it.
I animate in 2 steps: First from zero degrees (flat) to 90 degrees (where the layers become invisible.) At that moment I hide the first layer and make the second layer visible, rotated 90 degrees the other way, and then rotate the other layer back to zero. This creates the same visual effect as showing the back face of the rotation.
If you use implicit layer animation to do this you'll need to put the changes to the transform inside a CATransaction block and set the animation timing to linear, or use ease-in for the first half and ease-out for the second half. That's because animations default to ease-in,ease-out timing, and the first animation to 90 degrees will slow down at the end, and then the second 90 degree animation will ease in.

UIImageView image crop based on UIView mask

So i have an canvas (UIView) and a UIImageView, the canvas acts as a mask over the imageview
i am using UIGestureRecognizers to zoom and rotate the UIImageView which is under the canvas.
i want to convert the final image (show in the canvas to a UIImage, one solution is to convert the canvas to an image like below
UIGraphicsBeginImageContext(self.canvas.bounds.size);
[self.canvas.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *newCombinedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
now this works fine but the problem with this solution is the image is cropped to the dimensions of the canvas so the resolution is very low.
another option i explored was to use some custom UIImage categories to rotate and scale.
[[[self.photoImage image] imageRotatedByDegrees:rotaton_angle]
imageAtRect:CGRectMake(x,y width,height)]
i need to provide rotation angle (the rotation angle provided by UIGesture Delegate is not in Degrees or Radians, then there is x,y,width,height, i imagine these needs to be calculated based on some scale, (i do get scale value from UIGesture delegate but they do not seem to be correct for this function)
there are a number of solutions here, that guides you to crop and image given a rect. but in my case the rect is not the same scale as the image also there is rotation involved.
any help will be appreciated.
i have managed to solve this, here is my solution, it most definitely isn't the cleanest, but it works.
i needed to handle 3 things, pan, zoom and rotate.
firstly i used the the UIGestureRecognizer Delegate to get cumulative values incrementing at UIGestureRecognizerStateEnded for all 3.
then for rotation i just used the UIImage Category discussed here
self.imagetoEdit = [self.imagetoEdit imageRotatedByRadians:total_rotation];
for zooming (scale) i used GPUImage (i am using this throughout the app)
GPUImageTransformFilter *scaleFilter = [[GPUImageTransformFilter alloc] init];
[scaleFilter setAffineTransform:CGAffineTransformMakeScale(total_scale, total_scale)];
[scaleFilter prepareForImageCapture];
self.imagetoEdit = [scaleFilter imageByFilteringImage:self.imagetoEdit];
for panning, im doing this. (Not the cleanest code :S ) also using the above mention UIImage+Categories.
CGFloat x_ = (translation_point.x/canvas.frame.size.width)*self.imagetoEdit.size.width;
CGFloat y_ = (translation_point.y/canvas.frame.size.height)*self.imagetoEdit.size.height;
CGFloat xx = 0;
CGFloat yy = 0;
CGFloat ww = self.imagetoEdit.size.width-x_;
CGFloat hh = self.imagetoEdit.size.height-y_;
if (translation_point.x < 0) {
xx = x_*-1;
ww = self.imagetoEdit.size.width + xx;
}
if (translation_point.y < 0) {
yy = y_*-1;
hh = self.imagetoEdit.size.height + yy;
}
CGRect cgrect = CGRectMake(xx,yy, ww, hh);
self.imagetoEdit = [self.imagetoEdit imageAtRect:cgrect];
everything seem to work.
This might be helpful...
Resizing a UIImage the right way
It might need some updating for ARC etc... though I think there are people who have done it and have posted it on Github.

Determine if UIImageView has been rotated

I am rotating a UIImageView in this way:
-(IBAction)rotateImageView:(id)sender{
photoView.transform = CGAffineTransformRotate(photoView.transform, M_PI);
}
As you can see the image can either be up or down. Is there the possibility to detect if the image is up or down, in order to do something like this:
if(image is up)....
If you just want to tell if the image has been rotated using an affine transform, as your question implies, you can do this:
if (CGAffineTransformIsIdentity(photoView.transform)) {
// not rotated
...
} else {
// rotated
...
}
If you want to check for a particular rotation, do this:
CGAffineTransform t = CGAffineTransformMakeRotation(M_PI);
if (CGAffineTransformEqualToTransform(photoView.transform, t)) {
// rotated by M_PI
...
}
Note that the above two solutions only work if you are not applying OTHER affine transforms to the view at the same time as the rotation transforms. If you are also applying other transforms, perhaps you are better off just tracking the rotation state in a variable.
Swift 4
if transform.isIdentity { }
You can check the angle of the photoView, if it is 0.00 that means its up and if its 3 or 3.13 or any close value to it then it shows that its down. (you can log the exact value of down).
CGFloat angle = [(NSNumber *)[photoView valueForKeyPath:#"layer.transform.rotation.z"] floatValue];
NSLog(#"%f", angle);

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