I have an image view that I want to pinch to rescale without keeping the aspect ratio. In order to do this, I thought it might be feasible to either:
Use two pinch gesture recognisers, one that stretches horizontally, one that does so vertically.
Use one pinch recogniser but apply the two transforms one after the other.
Here's my pinch handling function:
- (void) pinch:(UIPinchGestureRecognizer *)recognizer {
static CGRect initialBounds;
if (recognizer.state == UIGestureRecognizerStateBegan)
{
initialBounds = imageView.bounds;
}
CGFloat factor = [(UIPinchGestureRecognizer *)recognizer scale];
//scale horizontally
CGAffineTransform zt = CGAffineTransformScale(CGAffineTransformIdentity,
factor-(1.0-factor), 1.0);
imageView.bounds = CGRectApplyAffineTransform(initialBounds, zt);
//now scale vertically
zt = CGAffineTransformScale(CGAffineTransformIdentity, 1.0, factor);
imageView.bounds = CGRectApplyAffineTransform(initialBounds, zt);
return;
}
For some reason, the transform is only being done vertically (last one). I tried changing the first parameter of the second CGRectApplyAffineTransform to imageView.bounds, but it still didn't work.
Can anyone please tell me where I am going wrong?
Also, when using two pinch gesture recognisers, the same thing happens - only one of them actually gets recognised.
Thanks!
Your second one is starting with a CGAffineTransformIdentity. Instead, pass in the zt.
Related
Im using the UIGestureRecognizer to transform a view and its workin perfectly, but now I want to use it to transform my view width and height independently. The only way that comes to my mind to solve this is getting the two finger positions and make an if clause to recognize if the user is trying to increase width or height, but for this I need to get each finger position involved in the Pinch Gesture. But I cant find any method to do this I was wondering if this is posible or if there is another alternative for achieving this.
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer {
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, 1);//To transform height insted of width just swap positions of the second and third parameter.
NSLog(#"%f",recognizer.scale);
recognizer.scale = 1;
}
Got the answer if somebody needs to do this, there is a method called location of touch. With this method you can get each touch x and y position. But call it when the Gesture recognizer state began because it crashes if you do it in the state changed. Save this values in some variables and you are good to go. Hope it helps someone who is interested.
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer {
if(recognizer.state == UIGestureRecognizerStateBegan){
NSLog(#"pos : 0%f, %f",[recognizer locationOfTouch:0 inView:self.view].x,[recognizer locationOfTouch:0 inView:self.view].y);
NSLog(#"pos 1: %f, %f",[recognizer locationOfTouch:1 inView:self.view].x,[recognizer locationOfTouch:1 inView:self.view].y);
}
if(recognizer.state == UIGestureRecognizerStateChanged){
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, 1);
//NSLog(#"%f",recognizer.scale);
recognizer.scale = 1;
}
if(recognizer.state == UIGestureRecognizerStateEnded){
}
I understand the various ways of detecting drag gestures just fine (currently I'm using a UIPanGestureRecognizer), but my understanding of transformations is limited, so I'm not sure if/how this is possible. Essentially, what I want to have is a scaling transformation applied to a UIView at the same pace (for lack of a better word) as the user performs a drag gesture elsewhere on the screen. In other words, as the user drags up, I want the size of my transforming UIView to increase proportionally to the position of that gesture, and if the user then starts dragging down, it should start to shrink proportionally.
Hopefully that makes sense. As a dummy example, just imagine a slider that you can adjust to change the size of a UIView in real time. Is there a good way to make those sorts of incremental and constant size updates with CGAffineTransform?
In your pan gesture handler, you simply grab translationInView or locationInView, calculate a scale from that, and then update the transform accordingly. For example:
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
static CGAffineTransform originalTransform;
if (gesture.state == UIGestureRecognizerStateBegan)
{
originalTransform = self.viewToScale.transform;
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
CGPoint translation = [gesture translationInView:gesture.view];
CGFloat scale = 1.0 - translation.y / 160.0;
self.viewToScale.transform = CGAffineTransformScale(originalTransform, scale, scale);
}
}
You can play around with the scale calculation depending upon precisely what you want to do, but hopefully you get the idea.
Personally, I'd rather use the pinch gesture recognizer for resizing (it's a UI that users have been trained on, it gives you the scale factor right out of the box, etc.), but whatever works for you. If you did a pinch gesture recognizer, it might look like:
- (void)handlePinch:(UIPinchGestureRecognizer *)gesture
{
static CGAffineTransform originalTransform;
if (gesture.state == UIGestureRecognizerStateBegan)
{
originalTransform = self.viewToScale.transform;
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
self.viewToScale.transform = CGAffineTransformScale(originalTransform, gesture.scale, gesture.scale);
}
}
I found the best approach is to use locationInView so that you can equate a location offset in pixels with a scale. For example, if the circle is placed in the centre of the view:
func dragDot(recognizer: UIPanGestureRecognizer) {
let locationX = recognizer.location(in: self.view).x
// Expand and contract the circle
let locationXOffset = locationX - self.view.center.x
// We need scale to be 1 when locationXOffset = circle radius
let scale: CGFloat = locationXOffset / (self.widthOfCircle / 2)
self.ring.transform = CGAffineTransform(scaleX: scale, y: scale)
}
If the circle isn't in the centre of the view then replace self.view.center.x with the initial location of the circle.
This method will work across all devices and screen resolutions and will avoid the requirement to calibrate a constant
I am using PinchGestureRecognizer and RotationGestureRecognizer both working fine. The code is as follows:
- (IBAction)pinchDetected:(UIPinchGestureRecognizer *)recognizer {
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
recognizer.scale = 1;
}
-(IBAction)rotationDetected:(UIRotationGestureRecognizer *)recognizer
{
recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);
recognizer.rotation = 0;
}
with this code I am able to pinch as well as rotate my view. but on "RESET" button click I want to set my view's frame as it was before pinching or rotating.
for that I am using
[viewTwo setFrame:CGRectMake(80.0f, 65.0f, 160.0f, 101.0f)];
but my frame does not set.
so How can I set my view's frame again as it was before pinching and zooming?
You are not changing the frame with your gesture recognisers.
You need to assign the transform back to the identity.
recognizer.view.transform = CGAffineTransformIdentity;
Frame and transform are applied to a view in two completely separate ways (where frame is the smallest rectangle that fits a view, and transform is a representation of the underlying 2-D matrix of the view). If you wish to return to the size the view was previously, assign recognizer.view.transform to CGAffineTransformIdentity.
I'm using a pinch gesture to let users increase/decrease the size of an image. I temporarily manipulate that CGAffineTransform to let the user play with the scale, then just undo it and set the frame size so that image scales with quality (CGAffineTransformScale does nothing for image quality and will only stretch out the image).
-(void)handlePinch:(UIPinchGestureRecognizer *)recognizer{
if(recognizer.state == UIGestureRecognizerStateBegan){
self.alpha = 0.7;
startingTransform = self.transform;
}
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
recognizer.scale = 1;
if (recognizer.state == UIGestureRecognizerStateEnded){
self.alpha = 1.0;
CGRect endFrame = self.frame;
self.transform = startingTransform;
self.frame = endFrame;
//self.transform = CGAffineTransformRotate(self.transform, acos(startingTransform.c)); //reapply rotation?
}
}
The issue: The problem with my code is that it does not account for rotation. Some of my images start out rotated (with CGAffineTransformRotate being previously applied to them) and what happens when the pinch gesture finishes is that the imageview warps out into really thin rectangles, devoid of any rotation or scale they were before.
I thought I could carry over the original rotation and apply it after the frame was set, but I don't think that works. Any help is appreciated. Thanks
Note that the UIView documentation says that as soon as a UIViews transform is not its identity transform, the frame property becomes undefined and should not be used, so do not rely on CGRect endFrame = ... to work.
Otherwise, if you want the quality to change, just set the transform to identity, scale the frame and then reapply the transform to keep the rotation. That should work fine.
add imageview as subview of scrollview, and then specify the delegate of scrolview which asks for viewtobezoomed as the imageview. it will easi.y solve it.
http://developer.apple.com/library/ios/documentation/uikit/reference/uiscrollviewdelegate_protocol/Reference/UIScrollViewDelegate.html#//apple_ref/occ/intfm/UIScrollViewDelegate/viewForZoomingInScrollView:
I have a custom UIImageView, I can drag it around screen by making a translation with (xDif and yDif is the amount fingers moved):
CGAffineTransform translate = CGAffineTransformMakeTranslation(xDif, yDif);
[self setTransform: CGAffineTransformConcat([self transform], translate)];
Let's say I moved the ImageView for 50px in both x and y directions. I then try to rotate the ImageView (via gesture recognizer) with:
CGAffineTransform transform = CGAffineTransformMakeRotation([recognizer rotation]);
myImageView.transform = transform;
What happens is the ImageView suddenly moves to where the ImageView was originally located (before the translation - not from the moved position + 50px in both directions).
(It seems that no matter how I translate the view, the self.center of the ImageView subclass stays the same - where it was originally laid in IB).
Another problem is, if I rotate the ImageView by 30 deg, and then try to rotate it a bit more, it will again start from the original position (angle = 0) and go from there, why wouldn't it start from the angle 30 deg and not 0.
You are overwriting the earlier transform. To add to the current transform, you should do this –
myImageView.transform = CGAffineTransformRotate(myImageView.transform, recognizer.rotation);
Since you're changing the transform property in a serial order, you should use CGAffineTransformRotate, CGAffineTransformTranslate and CGAffineTransformScale instead so that you add to the original transform and not create a new one.