Adding gesture recognizer to UIImage, not UIImageView - ios

I have a UIImageView whose 'View Mode' is set to 'Aspect Fit' in Interface Builder.I want to add gesture recognizer to only my image inside of the UIImageView.To clarify what i say:
I have a UIImageView whose bounds.size.width = 100; and bounds.size.height = 100; and I have a UIImage whose size is 100x50. So, when I add my UIImage to my UIImageView there exists some spaces of 25 from top and 25 from bottom. But I don't want to recognize user's tap when he/she taps on these spaces. I just want to know when user tapped on UIImage.
How can I do that?

As UIImage extends from NSObject you can not add UITapGestureRecognizer to it, you have to add gesture to the UIImageView itself. Then in any of the UIGestureRecognizer delegate methods:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;
or
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
get touch's location in UIImageView. Then by using CGPoint location = [_gesture locationInView:yourImageView] get the touched point, now get the image's frame using this link. Now check if the point is in Image's frame or not by using:
bool CGRectContainsPoint ( CGRect rect, CGPoint point );
This might help you.

You have to add the gesture recognizer to the imageView. What you can do it use the gesture recognize delegate methods:
- gestureRecognizerShouldBegin:
//OR
- gestureRecognizer:shouldReceiveTouch:
You can then figure out where the user tapped in the imageView and compute if the touch landed inside the image in the imageView or outside the image.
You can do this using using the following:
CGPoint tapPoint = [gestureRecognizer locationInView:imageView];
// use tap point to compute if it lands inside the image.

Related

How to make Clickable Image area with zoom in & out in iOS

I have an image as a background and I want to make certain parts of this image clickable with zooming in and out , Is there any way to do something like that ??
Don't create a new view for your gesture recognizer. The recognizer implements a locationInView: method. Set it up for the view that contains the sensitive region. On the handleGesture, hit-test the region you care about like this:
0) Do all this on the view that contains the region you care about. Don't add a special view just for the gesture recognizer.
1) Setup mySensitiveRect
#property (assign, nonatomic) CGRect mySensitiveRect;
#synthesize mySensitiveRect=_mySensitiveRect;
self.mySensitiveRect = CGRectMake(0.0, 240.0, 320.0, 240.0);
2) Create your gestureRecognizer:
gr = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
[self.view addGestureRecognizer:gr];
// if not using ARC, you should [gr release];
// mySensitiveRect coords are in the coordinate system of self.view
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer {
CGPoint p = [gestureRecognizer locationInView:self.view];
if (CGRectContainsPoint(mySensitiveRect, p)) {
//Add your zooming code here
} else {
NSLog(#"got a tap, but not where i need it");
}
}}
The sensitive rect should be initialized in myView's coordinate system, the same view to which you attach the recognizer.
Apple has a demo app called PhotoScroller that implements a zoomable, scrollable set of images (in a page view controller, but you don't need that.) That would be a good starting point for what you need.
Their sample apps used to be built into the Xcode docs. Since Xcode 6 I haven't seen them linked in the docs any more.
You can download PhotoScroller from Apple's online iOS Developer Library. (link)

iOS detect drag outside left edge of screen

In iPad when you put your finger outside top or bottom edge of screen and then drag it on screen a menu is revealed. How can I implement that?
There is specifically a Gesture Recogniser class for this, introduced in iOS 7. It's the UIScreenEdgePanGestureRecognizer. The documentation for it is here. Check it out.
To test this in the simulator, just start the drag from near the edge (~15 points).
Also, you will have to create a gestureRecognizer for each edge. You can't OR edges together, so UIRectEdgeAll won't work.
There is a simple example here. Hope this helps!
Well you can do something like this, this example is the case where you want you pan gesture to work only when the user swipes 20px inside from the right hand side of the screen
First of all add the gesture to your window
- (void)addGestures {
if (!_panGesture) {
_panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGesture:)];
[_panGesture setDelegate:self];
[self.view addGestureRecognizer:_panGesture];
}
}
After adding the check whether the touch you recieved is a pan gesture and then perform your action accordingly
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
CGPoint point = [touch locationInView:self.view];
if (gestureRecognizer == _panGesture) {
return [self slideMenuForGestureRecognizer:gestureRecognizer withTouchPoint:point];
}
return YES;
}
Here is how you can check whether your touch is contained in the region where you want it to be
-(BOOL)isPointContainedWithinBezelRect:(CGPoint)point {
CGRect leftBezelRect;
CGRect tempRect;
//this will be the width between CGRectMaxXEdge and the screen offset, thus identifying teh region
CGFloat bezelWidth =20.0;
CGRectDivide(self.view.bounds, &leftBezelRect, &tempRect, bezelWidth, CGRectMaxXEdge);
return CGRectContainsPoint(leftBezelRect, point);
}

UIButton position while UIImage zoom on UIScrollView

I have a scenario where I need to implement an Offline Map concept for which I am using the image of map on a UIScrollView that zooms on PinchGesture, which works fine.
Problem
I have a UIButton on map. While zooming, the button does not track its position with respect to UIImageView which is being scaled.I am able to reframe the button without affecting its size. But the position is wrong.
TLDR,
I need to reproduce the mapView with annotation kinda concept on UIScrollView with UIImage on it. Can any one help?
Thanks in advance :)
I have found the answer for this. I initially stored the button value in a CGRect initialButtonFrame. Then I updated the button frame (only origins, not the size of the button size as I wanted the button not to zoom like the image ie; I button should not zoom) using the scrollview delegate
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
[self manageImageOnScrollView];//here i managed the image's coordinates and zoom
[self manageButtonCoordinatesWithRespectToImageWithScale:scale];
}
-(void)manageButtonCoordinatesWithRespectToImageWithScale:(float)scaleFactor
{
//initialButtonFrame is frame of button
self.button.frame = CGRectMake((initialButtonFrame.origin.x * scaleFactor),
(initialButtonFrame.origin.y * scaleFactor),
initialButtonFrame.size.width,
initialButtonFrame.size.height);
[self.scrollView addSubview:self.button];// I removed the button from superview while zooming and later added with updated button coordinates which I got here
}
If you know your current offset and zoom of your map, you should be able to compute the position of your button:
//Assuming your map image has its origin at 0, 0
CGPoint mapOffsetX, mapOffsetY; // these would come from your map as you calculated it.
CGPoint mapZoomFactor; // 1.0 means not zoomed, 3.0 means zooming in 3x, etc
CGPoint buttonAnchorPosition; //the position of your button on your map at 1.0 zoom
CGFloat buttonX = buttonAnchorPosition.x * mapZoomFactor + mapOffsetX;
CGFloat buttonY = buttonAnchorPosition.y * mapZoomFactor + mapOffsetY;
CGPoint buttonPosition = CGPointMake(buttonX, buttonY);
button.position = buttonPosition;
Try that, good luck

Panning a UIImage with a Gesture Recognizer on a placeholder UIView

I need to crop an image to match a specific dimension. I have three layers in my view starting from the bottom:
I have the raw image in a UIImage. This image comes from the camera. (called cameraImage)
I have a UIView holding this image. When user clicks "crop", the UIView's bounds are used to crop the raw image inside it.
Above all of this I have a guide image which shows the user the dimensions they need to pan, rotate, and pinch their image to fit into.
I want to add the pan gesture to the top guide image and have it control the raw image at the bottom. So the guide image never moves but it is listening for the pan gesture.
I can't figure out how to reset the recognizer without it making my raw image jump back to zero. Maybe someone could help?
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:recognizer.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x+translation.x, recognizer.view.center.y+ translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:recognizer.view];
}
The above code works great in my gesture is attached to the bottom image. The problem is when the user goes outside the view's bounds, the image stops panning and is basically stuck. You can't touch it anymore so it sits there. So I thought if i attached the gesture to the top it would solve this problem.
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:recognizer.view];
cameraImage.center = CGPointMake(recognizer.view.center.x+translation.x, recognizer.view.center.y+ translation.y);
}
This almost works. I set the cameraImage's center and removed the third line which resets the recognizer. If I don't remove it, the cameraImage jumps back into the same position every time I try and pan. It almost works because when you click the image again it doesn't start from the pixel you touched. It moves the image back to the original position and then lets you pan.
First option:
when the recognizer enter the UIGestureRecognizerStateEndedstate
if(recofnizer.state == UIGestureRecognizerStateEnded )
{
...
}
You store the translation at that point in time in an instance varibale (#property) of your class.
And then you always add the saved translation to the new translation.
In code this would look like this:
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:recognizer.view];
CGPoint updatedTranslation = CGPointMake(translation.x+self.savedTranslation.x,translation.y+self.savedTranslation.y);
cameraImage.center = CGPointMake(recognizer.view.center.x+updatedTranslation.x, recognizer.view.center.y+ updatedTranslation.y);
if(recofnizer.state == UIGestureRecognizerStateEnded )
{
self.savedTranslation = updatedTranslation;
}
}
Dont forget to add #property (nonatomic, assign) CGPoint savedTranslation; to your interface.
Also make sure the savedTranslation variable is initialized in the init method of your class to self.savedTranslation = CGPointMake(0,0);
Second option:
You should think about doing all that what you want in an scrollview with the imageview as the viewForZooming of the scrollview. This allows very smooth interaction, like users are used to!
Above this scrollview you can then put your mask/guide, but make sure to disable the userInteraction of the mask/guide view to make your user touch the scrollview below!

Multiple Gesture Responders for a Single View

I have an image that I would like to set up to respond to several different gesture responders. So for example, if one part of the picture is touched I would like one selector to be called, and another selector for a different part of the picture.
I looked at the UIGestureRecognizer and UITapGestureRecognizer classes, but I couldn't find a way to specify the image zones to be associated with them. Is this at all possible in iOS? And if so what classes should I look into using?
The easiest solution is to lay invisible views over the image and put the gesture recognizers on them.
If that's not feasible you'll have to look at the locationInView in the gesture recognizer's tap handler and figure out what you want to do based on where the user tapped.
Use the locationInView: property to determine where your tap occurred and then conditionally invoke a method. You can do this by setting up some CGRects that correspond to your hit areas. Then use the CGRectContainsPoint() function to determine if the tap landed in one of the hit areas.
Your tap gesture recognizer action may look something like this:
- (void)tapGestureRecognized:(UIGestureRecognizer *)recognizer
{
// Specify some CGRects that will be hit areas
CGRect firstHitArea = CGRectMake(10.0f, 10.0f, 44.0f, 44.0f);
CGRect secondHitArea = CGRectMake(64.0f, 10.0f, 44.0f, 44.0f)
// Get the location of the touch in the view's coordinate space
CGPoint touchLocation = [recognizer locationInView:recognizer.view];
if (CGRectContainsPoint(firstHitArea, touchLocation))
{
[self firstMethod];
}
else if (CGRectContainsPoint(secondHitArea, touchLocation))
{
[self secondMethod];
}
}

Resources