I have a mapview in my app which needs customized calloutView for every map annotations.
Therefore, I have an XIB file for this customized calloutView.
Here is my code for the map view controller
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
CustomCalloutView *calloutView = (CustomCalloutView *)[[[NSBundle mainBundle] loadNibNamed:#"CustomCalloutView" owner:self options:nil] objectAtIndex:0];
[calloutView.layer setCornerRadius:10];
CGRect calloutViewFrame = calloutView.frame;
calloutViewFrame.origin = CGPointMake(-calloutViewFrame.size.width/2 + 15, -calloutViewFrame.size.height);
calloutView.frame = calloutViewFrame;
// some other code such as load images for calloutView
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap)];
singleTap.numberOfTapsRequired = 1;
[calloutView addGestureRecognizer:singleTap];
[view addSubview:calloutView];
}
- (void)handleSingleTap{
NSLog(#"it works");
}
However, the handleSingleTap: has never been called. Instead, every tap on the calloutView will only simply dismiss the calloutView. I also tried to add a button on the calloutView, but tap on it will also cause the calloutView dismissing, rather than calling the button action.
Can anyone help?
Update:
I've tried to change the code
[view addSubview:calloutView];
to
[self.view addSubview:calloutView];
which add the customized calloutView into the main container view rather than the mapView.
Then, it works fine with the tap gesture. Therefore, I think the problem should be caused by the mapView, it seems that mapView passes all the touch event on calloutView to itself. Anyone have ideas regarding this?
OK. I worked out the solution finally. We need to have a customized class for the MKAnnotationView to solve this. Here is the code in the .m file
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView* hitView = [super hitTest:point withEvent:event];
if (hitView != nil)
{
[self.superview bringSubviewToFront:self];
}
return hitView;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event
{
CGRect rect = self.bounds;
BOOL isInside = CGRectContainsPoint(rect, point);
if(!isInside)
{
for (UIView *view in self.subviews)
{
isInside = CGRectContainsPoint(view.frame, point);
if(isInside)
break;
}
}
return isInside;
}
Hopefully this can be helpful for others.
You need to modify your tapgesture implemenation & the method called.Have a look & check whether it work or not.
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap)];
singleTap.numberOfTapsRequired = 1;
[calloutView addGestureRecognizer:singleTap];
[view addSubview:calloutView];
- (void)handleSingleTap:(UIGestureRecognizer *)recognizer {
NSLog(#"it works");
}
Related
my image is not click table
-(void)makeBlockAction{
blocksArr = [NSMutableArray new];
}
and my function for event is
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch* myTouch = [[touches allObjects] objectAtIndex:0];
NSLog(#"test2");
if ( [ blocksArr containsObject: myTouch.view ])
{
myTouch.view.alpha = 0;
NSLog(#"test");
}
}
There are better ways to go about this, try adding a UITapGestureRecognizer to the UIImageView. That way you won't need to use the touchesEnded function and check all the touches.
Also, make sure you have set userInteractionEnabled on your UIImageView to YES and that your UIImageView is not underneath any other subviews that may be preventing the tap being detected.
EXAMPLE
- (void)viewDidLoad
{
[super viewDidLoad];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(imageViewTapped:)];
[self.myImageView addGestureRecognizer:tapGestureRecognizer];
self.myImageView.userInteractionEnabled = YES;
[self.view bringSubviewToFront: self.myImageView];
}
- (void)imageViewTapped:(UITapGestureRecognizer *)tapGestureRecognizer
{
//Do what you need to do.
}
From my side I will try bellow code, but it is not working.
- (void) handleTouch:(UITapGestureRecognizer *) gesture
{
CGPoint touchPoint = [gesture locationInView:self.view];
NSArray *viewsAtPoint = [self viewsAtPoint:touchPoint];
for(TheifView * aView in viewsAtPoint)
{
[aView removeFromSuperview];
}
}
- (void) registerGesture
{
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTouch:)];
tapGesture.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tapGesture];
}
Gesture target and selector can be removed by following code
[gesture removeTarget:self action:#selector(handleTouch:)];
Put tag to ThiefView...say 111. and then u can identify it and remove from self.view. If this is not the answer you expected then explain with clear description..
//For Removing Gestures from View..
for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers) {
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[self.view removeGestureRecognizer:recognizer];
}
}
//For Removing ThiefView from View
for (UIView *subview in self.view.subviews) {
if (subview.tag==111) {
[subview removeFromSuperview];
}
}
To remove it fully
- (void) handleTouch:(UITapGestureRecognizer *) gesture
{
[self.view removeGestureRecognizer:gesture];
}
To remove its target
- (void) handleTouch:(UITapGestureRecognizer *) gesture
{
[gesture removeTarget:self action:#selector(handleTouch:)];
}
To disable the gesture
- (void) handleTouch:(UITapGestureRecognizer *) gesture
{
gesture.enabled=NO;
}
I want to hide the keyboard by touching the view. Everybody recommends to use this method, saying there's no need to link or anything else, but is not working.
The problem is that my method is not called.Is there anything else that should be done?
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[[self view] endEditing:YES];
}
I had trouble with this so use a method that loops through all views seeing if they are textviews and firstResponders. Not sure of the structure of a UITextView but you might need to check for that too although it may be covered.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UIView *txt in self.view.subviews){
if ([txt isKindOfClass:[UITextField class]] && [txt isFirstResponder]) {
[txt resignFirstResponder];
}
}
}
The best approach is to create a "lock view" which is a UIView that takes over the whole screen once the textField becomesFirstResponder. Make sure it's on top of all views (well, besides the textview, of course).
- (void)loadLockView {
CGRect bounds = [UIScreen mainScreen].bounds;
_lockView = [[UIView alloc] initWithFrame:bounds];
_lockView.backgroundColor = [UIColor clearColor];
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(lockViewTapped:)];
[_lockView addGestureRecognizer:tgr];
[self.view addSubview:_lockView];
}
- (void)lockViewTapped:(UITapGestureRecognizer *)tgr {
[_lockView removeFromSuperView];
[_textField resignFirstResponder];
}
Use UITapGestureRecognizer For Dismiss keyboard.
Write this code on your viewdidload().
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissKeyboard)];[self.view addGestureRecognizer:tap];
and in dismissKeyboard method put this code.
-(void)dismissKeyboard
{
[TextFieldName resignFirstResponder];
}
I have created a custom annotation and a call out for my map view. I need to navigate to another view when the user clicks on call out view or he clicks to the button that added as sub view to the callout view. But both gesture recognizer and add target is not working for me in this case. The setSelected: method was invoked and the view get hidden when tap occurs in call out view.
#interface VBPunchCardAnnotation : MKAnnotationView{
UIView *calloutView;
}
- (id)initWithAnnotation:(id )annotation reuseIdentifier:(NSString *)reuseIdentifier deal:(id)punchdeal
{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
calloutView = [[UIView alloc] init];
calloutView.hidden = YES;
infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
[calloutView addSubview:infoButton];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(annotationTapped:)];
singleTap.numberOfTapsRequired = 1;
singleTap.delegate = self;
[calloutView addGestureRecognizer:singleTap];
[infoButton addTarget:self action:#selector(annotationTapped:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:calloutView];
return self;
}
-(void)setSelected:(BOOL)selected animated:(BOOL)animated
{
// show/hide callout and swap pin image
calloutView.hidden = !selected;
self.image = !selected ? normalPin : selectedPin;
// dispatch an event to alert app a pin has been selected
if(selected) [[NSNotificationCenter defaultCenter] postNotificationName:#"punchCardAnnotation" object:self];
}
-(void)annotationTapped:(id)sender{
[self.delegate punchCardAnnotationClickedForDeal:self.punchDeal];
}
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView* hitView = [super hitTest:point withEvent:event];
if ([hitView isKindOfClass:[UIButton class]]) {
}
}
Finally I got the answer. It;s here
Followed this tutorial. Really great solution.
https://github.com/nfarina/calloutview
Happy coding!!
Something strange I encountered today while attaching same gesture recogniser to multiple image views. It gets attached to only the last one, in other words, it can be attached to only one view!
I had to create multiple gesture recognisers to meet my requirements.
Following is what I have done. Am I doing correct? Is that's the only way to attach recognisers to the multiple imageviews?
Please note that I don't want to use UITableView or UIVIew and put all imageviews in it and attach gesture recogniser to only UITableView or UIVIew. I have all image scattered and I have to detect which image is being dragged. Thanks.
[imgView1 setUserInteractionEnabled:YES];
[imgView1 setMultipleTouchEnabled:YES];
[imgView2 setUserInteractionEnabled:YES];
[imgView2 setMultipleTouchEnabled:YES];
[imgView3 setUserInteractionEnabled:YES];
[imgView3 setMultipleTouchEnabled:YES];
[imgView4 setUserInteractionEnabled:YES];
[imgView4 setMultipleTouchEnabled:YES];
[imgView5 setUserInteractionEnabled:YES];
[imgView5 setMultipleTouchEnabled:YES];
[imgView6 setUserInteractionEnabled:YES];
[imgView6 setMultipleTouchEnabled:YES];
//Attach gesture recognizer to each imagviews
gestureRecognizer1 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
gestureRecognizer1.delegate = self;
gestureRecognizer2 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
gestureRecognizer2.delegate = self;
gestureRecognizer3 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
gestureRecognizer3.delegate = self;
gestureRecognizer4 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
gestureRecognizer4.delegate = self;
gestureRecognizer5 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
gestureRecognizer5.delegate = self;
gestureRecognizer6 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
gestureRecognizer6.delegate = self;
[imgView1 addGestureRecognizer:gestureRecognizer1];
[imgView2 addGestureRecognizer:gestureRecognizer2];
[imgView3 addGestureRecognizer:gestureRecognizer3];
[imgView4 addGestureRecognizer:gestureRecognizer4];
[imgView5 addGestureRecognizer:gestureRecognizer5];
[imgView6 addGestureRecognizer:gestureRecognizer6];
Yes, one view per gesture recognizer. So if you want only one recognizer, put it on the superview, e.g.:
UILongPressGestureRecognizer *gestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(gestureHandler:)];
[self.view addGestureRecognizer:gestureRecognizer];
And then, in your handler, you can:
- (void)handleLongPress:(UILongPressGestureRecognizer *)sender
{
CGPoint location = [sender locationInView:self.view];
if (sender.state == UIGestureRecognizerStateBegan)
{
for (UIView *view in self.view.subviews)
{
if ([view isKindOfClass:[UIImageView class]] && CGRectContainsPoint(view.frame, location))
{
UIImageView *image = (UIImageView *) view;
// ok, now you know which image you received your long press for
// do whatever you wanted on it at this point
return;
}
}
}
}
By the way, if you do that, you don't need to worry about enabling user interaction on the images, either.
Finally, you don't need to worry about specifying your gesture recognizer's delegate unless you're going to conform to UIGestureRecognizerDelegate, which this isn't. Also note that I'm using a local var for my recognizer because there's no reason to hang onto it.
Update:
While the above code works fine, perhaps even better would be a custom long press gesture recognizer that would fail if the long press didn't take place over an image (this way it's more likely to play well in case you have other gesture recognizers taking place in your view). So:
#import <UIKit/UIGestureRecognizerSubclass.h>
#interface ImageLongPressGestureRecognizer : UILongPressGestureRecognizer
#property (nonatomic, weak) UIImageView *imageview;
#end
#implementation ImageLongPressGestureRecognizer
#synthesize imageview = _imageview;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.imageview = nil;
[super touchesBegan:touches withEvent:event];
CGPoint location = [self locationInView:self.view];
for (UIView *view in self.view.subviews)
{
if ([view isKindOfClass:[UIImageView class]] && CGRectContainsPoint(view.frame, location))
{
self.imageview = (UIImageView *)view;
return;
}
}
self.state = UIGestureRecognizerStateFailed;
}
#end
then create your gesture recognizer accordingly, using this new subclass:
ImageLongPressGestureRecognizer *gestureRecognizer = [[ImageLongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPress:)];
[self.view addGestureRecognizer:gestureRecognizer];
and then, as a nice little benefit of this subclassing, your main gesture recognizer is simplified, namely:
- (void)handleLongPress:(ImageLongPressGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateBegan)
{
// you can now do whatever you want with sender.imageview, e.g. this makes it blink for you:
[UIView animateWithDuration:0.5
animations:^{
sender.imageview.alpha = 0.0;
} completion:^(BOOL finished){
[UIView animateWithDuration:0.5
animations:^{
sender.imageview.alpha = 1.0;
}
completion:nil];
}];
}
}
You can't attach a gesture recognizer to more than one object (as you discovered). One solution to what you are doing might be to subclass UIImageView and have setup code in that class so each view creates its recognizer, etc.
I guess, first of all, you should make an array of views and array of recognizers (mutable array, if needed) and then populate it. It will help you to use cycles to avoid code duplication.
As for multiple view with one recognizer - no, it's not possible, answered here.