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.
Related
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 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'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'm trying to create a knob-like behavior in one of my views with a UIRotationGestureRecognizer. This works, and positions the view as expected. However, every single time a gesture is performed, the rotation of the recognizer resets, so the knob starts at 0 every time.
How can I remember the last rotation of the UIRotationGestureRecognizer to let the user adjust the knob UIView without resetting it every single time?
I'm trying to make the recognizer start calculating rotation changes from the view's last known rotation:
knob starts at 0, recognizer is at 0
recognizer is rotated to 45 degrees
recognizer stops rotating
the knob is left at 45 degrees //this is already happening with the provided code snippet
next touch:
//this is what's is happening
recognizer starts at 0, rotates the knob back to 0
//I want it to be:
recognizer starts at 45, rotates the knob as in the example above.
- (IBAction)rotateView:(id)sender {
if([sender isKindOfClass:[UIRotationGestureRecognizer class]])
{
UIRotationGestureRecognizer* recognizer = sender;
CGAffineTransform transform = CGAffineTransformMakeRotation([recognizer rotation]);
rotatingView.transform = transform;
}
}
You should be able to get the current rotation of the rotatingView from it's transform property. Store this value into a savedRotation variable when the gesture begins. Make sure to assign a delegate to handle the gestureRecognizerShouldBegin callback.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)recognizer
{
savedRotation = atan2(rotatingView.transform.b, rotatingView.transform.a);
return YES;
}
- (void)rotateView:(UIRotationGestureRecognizer*)recognizer
{
rotatingView.transform = CGAffineTransformMakeRotation(recognizer.rotation+savedRotation);
}
Transform the transform:
rotatingView.transform = CGAffineTransformRotate(rotatingView.transform, [recognizer rotation]);
You have to make sure that you reset the rotation after the transform. Otherwise they will stack on top of each other and you get the "interesting" behavior.
rotatingView.transform = CGAffineTransformScale(rotatingView.transform, recognizer.scale, recognizer.scale);
[recognizer setRotation:0]; // this line
You should also do this for any translation or scaling transformations that you may do when handling gestures. The methods there are:
[recognizer setScale:1];
[recognizer setTranslation:CGPointZero inView:recognizer.view.superview];
I have a CALayer that I want to animate across the screen. I have created two methods: one slide open the layer and one to slide close. These both work by assigning a property to the layer's transform property.
Now I want to use a CAKeyFrameAnimation to slide open the layer. I got this working so the layer slides open, but now I can't slide the layer close using my old method. I am trying to figure out why this is. Any help would be great.
Code for my CALayer:
- (id)init
{
if( self = [super init] )
{
bIsOpen = NO;
closeTransform = self.transform;
openTransform = CATransform3DMakeTranslation(-235.0, 0.0, 0.0);
}
return self;
}
- (void)closeMenu
{
if( bIsOpen )
{
self.transform = closeTransform;
bIsOpen = !bIsOpen;
}
}
- (void)openMenu
{
if( !bIsOpen )
{
CAKeyframeAnimation *closeAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform"];
closeAnimation.duration = 1.0;
closeAnimation.removedOnCompletion = NO;
closeAnimation.fillMode = kCAFillModeForwards;
closeAnimation.values = [NSArray arrayWithObjects:[NSValue valueWithCATransform3D:closeTransform],[NSValue valueWithCATransform3D:openTransform],nil];
closeAnimation.timingFunctions = [NSArray arrayWithObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
[self addAnimation:closeAnimation forKey:#"transform"];
bIsOpen = !bIsOpen;
}
}
I figured this out. My problem was that the animation animates the object as if it modifying the property that is changed ( in this case the transform ), but the value isn't actually changed. I believe this has to do with the Presentation Layer vs. the Model Layer. So when the animation was done for opening the layer, the layer was positioned on the screen correctly, but its transform has not actually changed. So to fix this I used the (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag delegate method to set the transform to the opened state once the animation was complete.