I've got a view on screen with a UIImageView inside it. The user can pan, scale, and rotate.
I have a rotation gesture setup, and everything is fine with rotation except for when rotating the image moves off the screen/out of the view.
Any idea what would cause this to behavior? The expected behavior I'm looking for is when you rotate the image it does not move at all (other than rotating).
I've tried setting an anchor point of the layer of the parent view, but it doesn't seem to be helping any.
EDIT: I've put some logging on the rotation gesture and what's happening is the image views center (X) is what is getting off hence it going off the screen.
Why would this coordinate change upon rotation? The Y is staying the same. I've even tried turning the other gestures off and it's definitely something whacky with the rotation gesture.
Code Below:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CALayer *l = [self.viewCase layer];
[l setMasksToBounds:YES];
[l setCornerRadius:30.0];
self.imgUserPhoto.userInteractionEnabled = YES;
[self.imgUserPhoto setClipsToBounds:NO];
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panDetected:)];
panRecognizer.maximumNumberOfTouches = 1;
[self.view addGestureRecognizer:panRecognizer];
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(pinchDetected:)];
[self.view addGestureRecognizer:pinchRecognizer];
UIRotationGestureRecognizer *rotationRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotationDetected:)];
[self.view addGestureRecognizer:rotationRecognizer];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapDetected:)];
tapRecognizer.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:tapRecognizer];
panRecognizer.delegate = self;
pinchRecognizer.delegate = self;
rotationRecognizer.delegate = self;
}
- (void)panDetected:(UIPanGestureRecognizer *)panRecognizer
{
CGPoint translation = [panRecognizer translationInView:self.view];
CGPoint imageViewPosition = self.imageView.center;
imageViewPosition.x += translation.x;
imageViewPosition.y += translation.y;
self.imageView.center = imageViewPosition;
[panRecognizer setTranslation:CGPointZero inView:self.view];
}
- (void)pinchDetected:(UIPinchGestureRecognizer *)pinchRecognizer
{
CGFloat scale = pinchRecognizer.scale;
self.imageView.transform = CGAffineTransformScale(self.imageView.transform, scale, scale);
pinchRecognizer.scale = 1.0;
}
- (void)rotationDetected:(UIRotationGestureRecognizer *)rotationRecognizer
{
CGFloat angle = rotationRecognizer.rotation;
self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, angle);
rotationRecognizer.rotation = 0.0;
}
- (void)tapDetected:(UITapGestureRecognizer *)tapRecognizer
{
[UIView animateWithDuration:0.25 animations:^{
self.imageView.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
self.imageView.transform = CGAffineTransformIdentity;
}];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Maybe because the pan gesture is also getting called : try setting the maximumNumberOfTouches to 1 on your UIPanGestureRecognizer
Related
I am trying to pinch an image via UIPinchGestureRecognizer but , the problem is my code does not work properly and actually it could not zoom my image
-(void) pinching: (UIPinchGestureRecognizer *) sender {
CGAffineTransform myTransformation =
CGAffineTransformMakeScale(sender.scale, sender.scale);
sender.view.transform = myTransformation;
}
- (void)viewDidLoad
{
UIPinchGestureRecognizer *pinch =
[[UIPinchGestureRecognizer alloc]
initWithTarget:self
action:#selector(pinching:)];
pinch.delegate = self;
[imageBG addGestureRecognizer:pinch];
[imageBG setUserInteractionEnabled:YES];
[imageBG setMultipleTouchEnabled:YES];
}
Try using a UIScrollView.
To do this, create it
self.scrollView = [[UIScrollView alloc] initWithFrame:...];
self.scrollView.delegate = self;
self.scrollView.maximumZoomScale = 2.0; // adjust as you need
self.scrollView.minimumZoomScale = 0.5; // adjust as you need
[self.scrollView addSubview:self.imageView];
and add the delegate method:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.imageView;
}
just take one variable in .h file like bellow..
CGFloat lastScale;
and use this type of code...
in viewWillAppear: method just add it..
- (void)viewWillAppear:(BOOL)animated
{
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(scale:)];
[pinchRecognizer setDelegate:self];
[yourImageView addGestureRecognizer:pinchRecognizer];
}
-(void)scale:(id)sender {
if([(UIPinchGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded) {
lastScale = 1.0;
return;
}
CGFloat scale = 1.0 - (lastScale - [(UIPinchGestureRecognizer*)sender scale]);
CGAffineTransform currentTransform = [(UIPinchGestureRecognizer*)sender view].transform;
CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale);
[[(UIPinchGestureRecognizer*)sender view] setTransform:newTransform];
yourImageView.transform = newTransform
lastScale = [(UIPinchGestureRecognizer*)sender scale];
}
try this code also..
You need to add a scrollView to enable zooming. Add your imageView as a subView of the scroll view. Set the delegate for UIScrollViewDelegate to self. Implement the delegate methods.
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
//return the imageview here
return self.imageView;
}
Scale your imageview here:
- (CGRect)zoomRectForScrollView:(UIScrollView *)scrollView withScale:(float)scale withCenter:(CGPoint)center
You can refer this example from Apple .
I have some problem with image dragging. So I use this code:
-(void)panDetected:(UIPanGestureRecognizer *)panRecognizer {
CGPoint translation = [panRecognizer translationInView:self.view];
CGPoint imageViewPosition = player1.center;
imageViewPosition.x +=translation.x;
imageViewPosition.y +=translation.y;
player1.center = imageViewPosition;
[panRecognizer setTranslation:CGPointZero inView:self.view];
[self SaveColorRealTime];
}
-(void)pinchDetected:(UIPinchGestureRecognizer *)pinchRecognizer {
CGFloat scale = pinchRecognizer.scale;
player1.transform = CGAffineTransformScale(player1.transform, scale, scale);
pinchRecognizer.scale = 1.0;
}
-(void)rotationDetected:(UIRotationGestureRecognizer *)rotationRecognizer {
CGFloat angle = rotationRecognizer.rotation;
player1.transform = CGAffineTransformRotate(player1.transform, angle);
rotationRecognizer.rotation = 0.0;
}
-(void)tapDetected:(UITapGestureRecognizer *)tapRecognizer {
[UIView animateWithDuration:0.25 animations:^{
player1.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
player1.transform = CGAffineTransformIdentity;
}];
}
- (void)viewDidLoad
{
[super viewDidLoad];
player1.userInteractionEnabled = YES;
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panDetected:)];
[player1 addGestureRecognizer:panRecognizer];
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(pinchDetected:)];
[player1 addGestureRecognizer:pinchRecognizer];
UIRotationGestureRecognizer *rotationRecognizer = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:#selector(rotationDetected:)];
[player1 addGestureRecognizer:rotationRecognizer];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapDetected:)];
tapRecognizer.numberOfTapsRequired = 2;
[player1 addGestureRecognizer:tapRecognizer];
It works, user can drag image on view, but when user go to other view, position of image is refresh.
Have you any ideas how can I save image position?
Thanks
I have an imageView, to which, I have added UIPinchGestureRecognizer and UIRotationGestureRecognizer.
in pinch gesture, I transform and scale the View and in rotation gesture I apply rotation transform to it.
The problem is when I rotate the imageView and then start zooming. Zooming always begins from the normal state.
So What i want is when I rotate it to say 30 degree clockwise and then zoom it. it should zoom while remaining that 30 degree on the clockwise direction.
Here is the code:
- (void)viewDidLoad{
[super viewDidLoad];
//setting up the image view
mTotalRotation = 0.0;
self.imageView.image = self.photo;
self.imageView.userInteractionEnabled = YES;
UIRotationGestureRecognizer *twoFingersRotate =
[[[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(twoFingersRotate:)] autorelease];
[self.imageView addGestureRecognizer:twoFingersRotate];
UIPinchGestureRecognizer *pinchGesture = [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(pinchZoom:)] autorelease];
[self.imageView addGestureRecognizer:pinchGesture];
// Do any additional setup after loading the view from its nib.
}
// Rotation gesture handler
- (void)twoFingersRotate:(UIRotationGestureRecognizer *)recognizer
{
if ([recognizer state] == UIGestureRecognizerStateEnded) {
mTotalRotation += recognizer.rotation;
return;
}
self.imageView.transform = CGAffineTransformMakeRotation(mTotalRotation + recognizer.rotation);
}
// Pinch Gesture
-(void)pinchZoom:(UIPinchGestureRecognizer*)recognizer{
self.imageView.transform = CGAffineTransformMakeScale(recognizer.scale, recognizer.scale) ;
}
Change the line:
self.imageView.transform = CGAffineTransformMakeRotation(mTotalRotation + recognizer.rotation);
with:
self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, recognizer.rotation);
And the line:
self.imageView.transform = CGAffineTransformMakeScale(recognizer.scale, recognizer.scale);
with:
self.imageView.transform = CGAffineTransformScale(self.imageView.transform, recognizer.scale, recognizer.scale);
Edit
To limit the scale, you can do the following:
CGAffineTransform transform = self.imageView.transform;
float newScale = recognizer.scale * sqrt(transform.a*transform.a + transform.c*transform.c);
if (newScale > scaleLimit) {
self.imageView.transform = CGAffineTransformScale(transform, recognizer.scale, recognizer.scale);
}
Maybe you could have your scale and rotate view as a subview of a view that you zoom?
Trying to come up with a method for doing the exact same thing the reeder apps creator does in his iphone/ipad apps with pinch-to-expand photos to full screen.
I have a uiimageview in a table cell that I want to transition to a full screen view on pinch open, or maybe double tap. Would like to use a similar animation as well.
Any tips would be appreciated!
Ok I managed to put this together myself. Not really sure how to use a transition method, but I needed to duplicate the view in the same location and then blow it up.
http://screencast.com/t/MLTuGkIYh
So in my cell that contains the big image I hook up both the pinch and tap gesture recognizers.
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.imageView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
UIPinchGestureRecognizer *pinchGesture = [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handlePinchGesture:)] autorelease];
UITapGestureRecognizer *tapGesture = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)] autorelease];
tapGesture.numberOfTapsRequired = 2;
self.imageView.userInteractionEnabled = YES;
self.imageView.multipleTouchEnabled = YES;
[self.imageView addGestureRecognizer:pinchGesture];
[self.imageView addGestureRecognizer:tapGesture];
[cell.contentView addSubview:self.imageView];
and then here's the rest of the code. Basically when I recognized the gesture (and for pinching, make sure its finished) I place the duplicate view in the same exact location (gained via the convertRect stuff), and then animate its frame and background color. When returning from it, I do the inverse.
- (void)handlePinchGesture:(id)sender
{
if (((UIPinchGestureRecognizer *)sender).state == UIGestureRecognizerStateEnded) {
if(((UIPinchGestureRecognizer *)sender).view == self.imageView)
{
if (((UIPinchGestureRecognizer *)sender).scale > 1) {
[self showFloorPlanFullScreen];
}
} else {
if (((UIPinchGestureRecognizer *)sender).scale < 1) {
[self closeFloorPlanFullScreen];
}
}
}
}
- (void)handleTap:(id)sender
{
if (((UITapGestureRecognizer *)sender).view == self.imageView) {
[self showFloorPlanFullScreen];
} else {
[self closeFloorPlanFullScreen];
}
}
- (void)showFloorPlanFullScreen
{
CGRect newRect = [self.imageView convertRect:self.imageView.bounds toView:[self.splitViewController.view superview]];
UIImage *image = self.imageView.image;
self.fullScreenImageView = [[[UIImageView alloc] initWithImage:image] autorelease];
UIPinchGestureRecognizer *pinchGesture = [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handlePinchGesture:)] autorelease];
UITapGestureRecognizer *tapGesture = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)] autorelease];
tapGesture.numberOfTapsRequired = 2;
self.fullScreenImageView.userInteractionEnabled = YES;
self.fullScreenImageView.multipleTouchEnabled = YES;
[self.fullScreenImageView addGestureRecognizer:pinchGesture];
[self.fullScreenImageView addGestureRecognizer:tapGesture];
self.fullScreenImageView.contentMode = UIViewContentModeScaleAspectFit;
self.fullScreenImageView.frame = newRect;
self.fullScreenImageView.backgroundColor = [UIColor clearColor];
[[self.splitViewController.view superview] addSubview:self.fullScreenImageView];
CGRect splitViewRect = self.splitViewController.view.frame;
[UIView animateWithDuration:0.5 animations:^{
self.fullScreenImageView.backgroundColor = [UIColor blackColor];
self.fullScreenImageView.frame = splitViewRect;
}];
}
- (void)closeFloorPlanFullScreen
{
CGRect newRect = [self.imageView convertRect:self.imageView.bounds toView:[self.splitViewController.view superview]];
[UIView animateWithDuration:0.5
animations:^{
self.fullScreenImageView.backgroundColor = [UIColor clearColor];
self.fullScreenImageView.frame = newRect;
}
completion:^(BOOL finished) {
[self.fullScreenImageView removeFromSuperview];
self.fullScreenImageView = nil;
}];
}
If you want the picture to actual resize while zooming, I would recommend adding the duplicate view as soon as the pinching starts (and as long as its scaling > 1) and then apply the transformation:
CGAffineTransform myTransformation = CGAffineTransformMakeScale(((UIPinchGestureRecognizer *)sender).scale, ((UIPinchGestureRecognizer *)sender).scale);
self.fullScreenImageView.transform = myTransformation;
As soon as the pinching hits a end state, I would then fade in the black and adjust the frame. I decided not to go with this method as I think just recognizing the pinch out or double tap is good enough.
You have to embed your UIImageView in an UIControl and link an IBAction to UIControl events.
Use a UIPinchGestureRecognizer on the image view to recognize the pinch and the UIView transition methods to blow the image view up to full size.
So, I've managed to create a UIScrollView with zooming method programmatically, but I'm kind of stuck how to solve an issue I'm having with zooming:
When I zoom in or out the point at where it expands/retracts does not happen where I do the pinch gesture, it happens on a corner.
After zooming in or out, it will leave extra space outside the bounds, and I cannot scroll the image more than half the width & height of the image.
Other than this, I'm so close to getting it working 100%. I've tried playing around with achorpoints, but it looks like the scroll view and image view does not respond to this.
Here is the important stuff in the code listing:
UIScrollView *mapScrollView;
UIImageView *mapImageView;
CGFloat lastScale = 0;
NSMutableArray *map_List;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
mainMenuAppDelegate *del = (mainMenuAppDelegate *)[[UIApplication sharedApplication] delegate];
map_List = [[NSMutableArray alloc] init];
[map_List addObject:#"Pacific_Map_8bit.png"];
[map_List addObject:#"Atlantic_Map_8bit.png"];
CGRect mapScrollViewFrame = CGRectMake(0, 0, 1024, 768);
mapScrollView = [[UIScrollView alloc] initWithFrame:mapScrollViewFrame];
mapScrollView.contentSize = CGSizeMake(2437, 1536);
UIImage *mapImage = [UIImage imageNamed:[map_List objectAtIndex:mapNum]];
mapImageView = [[UIImageView alloc] initWithImage: mapImage];
mapScrollView.bounces = NO;
[mapImage release];
[mapScrollView addSubview:mapImageView];
[self addSubview:mapScrollView];
mapImageView.userInteractionEnabled = YES;
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(scale:)];
[mapImageView addGestureRecognizer:pinchRecognizer];
[pinchRecognizer release];
}
return self;
}
// the scale method thats triggered to zoom when pinching
-(void)scale:(id)sender {
if([(UIPinchGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded) {
lastScale = 1.0;
return;
}
CGFloat scale = 1.0 - (lastScale - [(UIPinchGestureRecognizer*)sender scale]);
NSLog(#"map scale %f", scale);
CGFloat mapWidth = mapImageView.frame.size.width;
CGFloat mapHeight = mapImageView.frame.size.height;
NSLog(#"map width %f", mapWidth);
if(scale>=1 & mapWidth<=4000 || scale<1 & mapWidth>=1234){
CGAffineTransform currentTransform = [(UIPinchGestureRecognizer*)sender view].transform;
CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale);
[[(UIPinchGestureRecognizer*)sender view] setTransform:newTransform];
lastScale = [(UIPinchGestureRecognizer*)sender scale];
}
mapScrollView.contentSize = CGSizeMake(mapWidth, mapHeight);
}
Thanks!
UIScrollView has built-in zooming support. All you need to do is set the minimumZoomScale and maximumZoomScale properties, and return a view to be used for zooming using viewForZoomingInScrollView.