I need to be able to save the frame after transformation of a UIImageView
. In the below example, the original frame is when the image is added to the superview. The user then has the ability to rotate, scale and pan the image anywhere in the gray area (superview).
I take these images and save their coordinates to an NSDictionary (which is not the problem). The problem is that if I get the frame after the rotation, the frame is completely off. I need to be able to store the new frame with transform in the dictionary, so that when the user comes back to this view and the images are loaded, the frames and saved transformations are just like they intended.
CGPoint translation = [gestureRecognizer translationInView:[object superview]];
if (CGRectContainsPoint(self.frame, CGPointMake([object center].x + translation.x, [object center].y + translation.y))) {
[object setCenter:CGPointMake([object center].x + translation.x, [object center].y + translation.y)];
[gestureRecognizer setTranslation:CGPointZero inView:[object superview]];
}
self.transformRotation = CGAffineTransformRotate([[gestureRecognizer view] transform], [gestureRecognizer rotation]);
[gestureRecognizer view].transform = self.transformRotation;
if ( [gestureRecognizer rotation] != 0 ) {
self.rotate = [gestureRecognizer rotation];
}
[gestureRecognizer setRotation:0];
self.transformScale = CGAffineTransformScale([[gestureRecognizer view] transform], [gestureRecognizer scale], [gestureRecognizer scale]);
[gestureRecognizer view].transform = self.transformScale;
if ( [gestureRecognizer scale] != 1 ) {
self.scale = [gestureRecognizer scale];
}
[gestureRecognizer setScale:1];
Using the Center point of the view keeps the image closer to the original location when it was saved, the first time it is loaded. Each time it is saved after that the position is the same, because the transform did not change during that session.
- (CGPoint)centerOnCanvas {
CGPoint originalCenter = self.center;
return originalCenter;
}
- (CGRect)frameOnCanvas {
return CGRectMake(
self.preTransformedFrame.origin.x,
self.preTransformedFrame.origin.y,
self.preTransformedFrame.size.width,
self.preTransformedFrame.size.height
);
}
- (CGRect)preTransformedFrame {
CGAffineTransform currentTransform = self.transform;
self.transform = CGAffineTransformIdentity;
CGRect originalFrame = self.bounds;
self.transform = currentTransform;
return originalFrame;
}
According to Apple document for UIView's transform property
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.
That's the reason why you can't get frame
after changing transform
.
As I understand, after rotating, scaling or panning you want to save current state to restore later. In my opinion, all you need to do it is saving transform
and center
of UIImageView
each time they are changed. You don't need frame
in this case.
For example, _transformTarget
is your UIImageView
, to save its current state you can use below method (Instead of saving in a NSDictionary
, I use NSUserDefaults
. You can change it to NSDictionary
)
- (void)saveCurrentState {
[[NSUserDefaults standardUserDefaults] setObject:NSStringFromCGAffineTransform(_transformTarget.transform) forKey:@"_transformTarget.transform"];
[[NSUserDefaults standardUserDefaults] setObject:NSStringFromCGPoint(_transformTarget.center) forKey:@"_transformTarget.center"];
}
At the end of each handling gesture method, save current state by using saveCurrentState
.
- (void)handlePanGesture:(UIPanGestureRecognizer*)gesture {
CGPoint translation = [gesture translationInView:self.view];
CGPoint newCenter = CGPointMake(_transformTarget.center.x + translation.x, _transformTarget.center.y + translation.y);
if (CGRectContainsPoint(self.view.frame, newCenter)) {
_transformTarget.center = newCenter;
[gesture setTranslation:CGPointZero inView:self.view];
[self saveCurrentState]; // Save current state when center is changed
}
}
- (void)handleRotationGesture:(UIRotationGestureRecognizer*)gesture {
_transformTarget.transform = CGAffineTransformRotate(_transformTarget.transform, gesture.rotation);
gesture.rotation = 0;
[self saveCurrentState]; // Save current state when transform is changed
}
- (void)handlePinchGesture:(UIPinchGestureRecognizer*)gesture {
_transformTarget.transform = CGAffineTransformScale(_transformTarget.transform, gesture.scale, gesture.scale);
gesture.scale = 1;
[self saveCurrentState]; // Save current state when transform is changed
}
Now, the information about UIImageView
is saved every time it's changed. At the next time user comes back, get info about center
and transform
from your dictionary and set them again.
- (void)restoreFromSavedState {
NSString *transformString = [[NSUserDefaults standardUserDefaults] objectForKey:@"_transformTarget.transform"];
CGAffineTransform transform = transformString ? CGAffineTransformFromString(transformString) : CGAffineTransformIdentity;
NSString *centerString = [[NSUserDefaults standardUserDefaults] objectForKey:@"_transformTarget.center"];
CGPoint center = centerString ? CGPointFromString(centerString) : self.view.center;
_transformTarget.center = center;
_transformTarget.transform = transform;
}
For more detail, you can take a look at my sample repo https://github.com/trungducc/stackoverflow/tree/restore-view-transform