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:
Related
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 have a subclass of a UIImageView containing some gesture recognisers that I'm using to apply transforms to itself. I'm having no issues with panning or scaling, but the rotated transform is causing problems when the device itself is rotated. Basically each time the device is rotated it will have the effect of scaling the image...
I guess it makes sense that the rotation of everything might cause problems with a rotated transform but does anyone know any ways around this kind of behaviour? Preferably something that can be implemented within the UIImageView subclass? I need other sibling views to autoresize so I can't disable "autoresize subviews" in the parent view.
Here's the code responsible for creating the rotated transform if it helps:
- (void)setUpRotation
{
UIRotationGestureRecognizer *newRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(handleRotationGesture)];
newRecognizer.delegate = self;
self.rotationRecognizer = newRecognizer;
[self addGestureRecognizer:self.rotationRecognizer];
[newRecognizer release];
self.userInteractionEnabled = YES;
}
- (void)handleRotationGesture
{
// Initial state
if(self.rotationRecognizer.state == UIGestureRecognizerStateEnded)
{
lastRotation = 0.0;
return;
}
CGFloat rotation = 0.0 - (lastRotation - self.rotationRecognizer.rotation);
CGAffineTransform currentTransform = self.transform;
self.transform = CGAffineTransformRotate(currentTransform,rotation);
lastRotation = self.rotationRecognizer.rotation;
}
This is on iOS 5.0 btw.
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.
I have a UIView that I rotate with this code:
helpView.transform = CGAffineTransformMakeRotation(degreesToRadians( rotationAngle ));
Where degreeToRadians just is a macro to convert from degrees to radians.
This works fine as long as the view is visible, eg alpha = 1. When I hide it (alpha = 0, which I animate) it does not rotate any more. I guess this is a smart way for the devices to "save" on drawing time, but is there any way I can force it to be drawn even when alpha is 0? Otherwise I will have to rotate it before I show it again.
Any good ideas?
Thanks
Edit: This is the code I use to show/hide the view.
-(void)showHelp
{
bool helpAlpha = !helpView.alpha;
CGFloat newScale;
if (helpView.alpha) {
newScale = kHelpSmall;
helpView.transform = CGAffineTransformMakeScale(kHelpBig, kHelpBig);
} else {
newScale = kHelpBig;
helpView.transform = CGAffineTransformMakeScale(kHelpSmall, kHelpSmall);
}
[UIView animateWithDuration:(kAnimationTimeShort / 2) animations:^(void) {
[helpView setAlpha:helpAlpha];
helpView.transform = CGAffineTransformMakeScale(newScale, newScale);
}];
}
As you see I also scale it for a nicer effect. Works perfect when visible, does not rotate when alpha = 0. Rotation is done in another method, where I would prefer to keep it as I also rotate some other views there.
You are resetting the transform every time you use CGAffineTransformMake*. If you do this, you will get either a rotated transform or a scaled one. I am assuming the scaled one is after the rotated one and hence you aren't able to see the view rotated. If you need both the effects to remain, you will have to use CGAffineTransformRotate. So a scale and rotate will be
helpView.transform = CGAffineTransformMakeScale(kHelpSmall, kHelpSmall);
helpView.transform = CGAffineTransformRotate(helpView.transform, degreesToRadians(rotationAngle));
The order might vary.