I have placed an image (UIImageView) on the navigation bar. Now I want to detect the touch event and want to handle the event. How can I do that?
In practical terms, don't do that.
Instead add a button with Custom style (no button graphics unless you specify images) over the UIImageView. Then attach whatever methods you want called to that.
You can use that technique for many cases where you really want some area of the screen to act as a button instead of messing with the Touch stuff.
A UIImageView is derived from a UIView which is derived from UIResponder so it's ready to handle touch events. You'll want to provide the touchesBegan, touchesMoved, and touchesEnded methods and they'll get called if the user taps the image. If all you want is a tap event, it's easier to just use a custom button with the image set as the button image. But if you want finer-grain control over taps, moves, etc. this is the way to go.
You'll also want to look at a few more things:
Override canBecomeFirstResponder and return YES to indicate that the view can become the focus of touch events (the default is NO).
Set the userInteractionEnabled property to YES. The default for UIViews is YES, but for UIImageViews is NO so you have to explicitly turn it on.
If you want to respond to multi-touch events (i.e. pinch, zoom, etc) you'll want to set multipleTouchEnabled to YES.
To add a touch event to a UIImageView, use the following in your .m file:
UITapGestureRecognizer *newTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(myTapMethod)];
[myImageView setUserInteractionEnabled:YES];
[myImageView addGestureRecognizer:newTap];
-(void)myTapMethod{
// Treat image tap
}
You can also add a UIGestureRecognizer. It does not require you to add an additional element in your view hierarchy, but still provides you will all the nicely written code for handling touch events with a fairly simple interface:
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSwipe:)];
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
[imgView_ addGestureRecognizer:swipeRight];
[swipeRight release];
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSwipe:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[imgView_ addGestureRecognizer:swipeLeft];
[swipeLeft release];
I've been on different threads on the past few hours trying to find a solution for my problem, to no avail. I see that many developers share this problem, and I think people here know about this. I have multiple images inside a UIScrollView, trying to get tap events on them.
I am not getting any events from an UIImangeView, but I do get an event from a similar UILable with very similar parameters I am setting to it. Under iOS 5.1.
I have already done the following:
set setUserInteractionEnabled to YES for both `UIImageView and parent
view .
set setMultipleTouchEnabled to YES for UIImageView.
Tried subclassing UIImageView, didn't help any.
Attaching some code below, in this code I initialize both a UIImageView and UILabel, the label works fine in terms of firing events. I tried keeping out irrelevant code.
UIImageView *single_view = [[UIImageView alloc]initWithFrame:CGRectMake(200, 200, 100, 100)];
single_view.image = img;
single_view.layer.zPosition = 4;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGestureCaptured:)];
[single_view addGestureRecognizer:singleTap];
[single_view setMultipleTouchEnabled:YES];
[single_view setUserInteractionEnabled:YES];
[self.myScrollView addSubview:single_view];
self.myScrollView.userInteractionEnabled = YES;
UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
testLabel.backgroundColor = [UIColor redColor];
[self.myScrollView addSubview:testLabel];
[testLabel addGestureRecognizer:singleTap];
[testLabel setMultipleTouchEnabled:YES];
[testLabel setUserInteractionEnabled:YES];
testLabel.layer.zPosition = 4;
And the method which handles the event:
- (void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture
{
UIView *tappedView = [gesture.view hitTest:[gesture locationInView:gesture.view] withEvent:nil];
NSLog(#"Touch event on view: %#", [tappedView class]);
}
As said, the label tap is received.
Instead of making a touchable UIImageView then placing it on the navbar, you should just create a UIBarButtonItem, which you make out of a UIImageView.
First make the image view:
UIImageView *yourImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"nameOfYourImage.png"]];
Then make the barbutton item out of your image view:
UIBarButtonItem *yourBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:yourImageView];
Then add the bar button item to your navigation bar:
self.navigationItem.rightBarButtonItem = yourBarButtonItem;
Remember that this code goes into the view controller which is inside a navigation controller viewcontroller array. So basically, this "touchable image-looking bar button item" will only appear in the navigation bar when this view controller when it's being shown. When you push another view controller, this navigation bar button item will disappear.
You might want to override the touchesBegan:withEvent: method of the UIView (or subclass) that contains your UIImageView subview.
Within this method, test if any of the UITouch touches fall inside the bounds of the UIImageView instance (let's say it is called imageView).
That is, does the CGPoint element [touch locationInView] intersect with with the CGRect element [imageView bounds]? Look into the function CGRectContainsPoint to run this test.
First, you should place an UIButton and then either you can add a background image for this button, or you need to place an UIImageView over the button.
Or:
You can add the tap gesture to a UIImageView so that get the click action when tap on the UIImageView.
For those of you looking for a Swift 4 solution to this answer, you can use the following to detect a touch event on a UIImageView.
let gestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageViewTapped))
imageView.addGestureRecognizer(gestureRecognizer)
imageView.isUserInteractionEnabled = true
You will then need to define your selector as follows:
#objc func imageViewTapped() {
// Image has been tapped
}
Add gesture on that view. Add an image into that view, and then it would be detecting a gesture on the image too. You could try with the delegate method of the touch event. Then in that case it also might be detecting.
Related
I have a ViewController with full of names, basically a contact list. When you touch one of the names, you will get a detail screen with informations: name, organization, building, phone number, email, etc. It uses a navigation controller, so you can go back to the phonebook.
I have a custom map in my application (basically a picture, with several layers and with a pin, that show you where you are on the picture). I would like to achieve that when someone touch the building UILabel it goes my MapViewController with some parameters. It's a simple UILabel, but contains useful information, something like 15/A building. I have the exact x,y coordinates for the buildings.
My main problem is that I don't really know how to set up the touch event on the UILabel and go to a new view controller, with parameters.
I guess I need to make in my mapviewcontroller new initiation methods to be able to get parameters and display them.
So to summarize: how can I navigate to an other view controller with touch event and how can I initiate that view controller with parameters. It's important that I can reach my map without any buildings parameters as well from my menu.
Any help appreciated.
Alternatively you can also add a hidden button over your label. This button will handle the touch event.
Use Tap gesture for it
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(labelTapped)];
tapGestureRecognizer.numberOfTapsRequired = 1;
[myLabel addGestureRecognizer:tapGestureRecognizer];
myLabel.userInteractionEnabled = YES;
//Create a Tap Gesture and set it on your Label.
UILabel *yourLabel=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 320, 50)];
[yourLabel setTextColor:[UIColor blackColor]];
[yourLabel setTextAlignment:NSTextAlignmentCenter];
[yourLabel setText:#"Address"];
[self.view addSubview:yourLabel];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(TapGestureRecognizerLabel:)];
[tap setNumberOfTouchesRequired:1];
[yourLabel addGestureRecognizer:tap];
-(void)TapGestureRecognizerLabel:(UIGestureRecognizer *)gestureRecognizer
{
[self.navigationController pushViewController:targetControllerObj animated:YES];
}
My app shows an UIView which contains four UIButtons, one of them removes the UIView, but I also need is to remove the UIView, including all containing buttons when the user taps outside it.
This is how I remove the UIView using a button action:
-(IBAction)closeSideTasks: (id)sender
{
UIView * backgroundView = (UIView *)[self.view viewWithTag:7];
[backgroundView removeFromSuperview];
}
Any help or advice is welcome.
MORE INFORMATION:
This is the scenario: there is a table view. If the user swipes from left to right on a row, then the UIView appears on the right side of the view. The UIView *newView is removed after tapping on each of the buttons inside it and executing each button action. But may be the user does not tap on any of the buttons, and the UIView remains on the view. Then, what I want to achieve is a way that the UIView *newView gets removed if the user taps anywhere outside it, to prevent that it remains always on the view...
Check out UITapGestureRecognizer.
Example:
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] init];
tapGesture.delegate = self;
tapGesture.numberOfTapsRequired = 1;
tapGesture.numberOfTouchesRequired = 1;
[tapGesture addTarget:self action:#selector(tapChangeImage)];
[self.profileImageView addGestureRecognizer:tapGesture];
Now add the action method:
#pragma tap gestures
-(void)tapChangeImage{
// Remove view from superview!
}
Put a large button, full screen, with no graphics, no text, and a clear background. Have its action also remove the background
It may seem like duplicate, but I can't actually find any good answer to my concrete situation.
I have some button with background image (1pt-wide, streched), icon-like image inside along with the text label. It's height is 33pt, I need to make it's hittest area 44pt-high.
I saw two solutions, but neither of them works for me.
First solution is to enlarge frame and adjust image so it would have some padding. But that is not acceptable for me, because I have both background image and image inside the button.
Second solution is to subclass UIButton (which is absolutely acceptable) and override - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event. I did that, when I put some breakpoints in this method and tried to tap in desired area nothing happened (breakpoints worked when tapped inside the button frame).
Are there any other solutions? What could be possibly wrong with the second solution?
If you create the view programmatically, you can instantiate a UIView that is 44px high, add a tap gesture recognizer to it, and add your UIButton as a subview inside this view:
CGRect bigFrame = CGRectMake(0,0,100,44);
UIView *big = [[UIView alloc] initWithFrame:bigFrame];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doPress)];
[big addGestureRecognizer:tapRecognizer];
UIButton *yourButton = ...
[big addSubview:yourButton];
[self.view addSubview:big];
Then implement the selector as:
- (void)doPress {
[yourButton sendActionsForControlEvents:UIControlEventTouchUpInside];
}
In that way, the outer view taps will be interpreted as button presses for your button.
This is a strange one.
I'm quite new to Xcode. I've been trying to make a simple app that adds new buttons when you single click on the button view and removes them when you double click.
Adding buttons is OK, but removing them is unreliable. I think it has something to do with the way I've written the code because it only seems to remove the most recently added button on double click and not the actual button I've clicked on.
My abridged .m Code is below:
- (void)viewDidLoad
{
- (void)handleSingleTap:(UITapGestureRecognizer *)tapper {
//adds the buttons and gives them a unique tag
ButtonCount = ButtonCount+1;
btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(0, 0, 150, 150);
btn.userInteractionEnabled = YES;
btn.tag=PuckCount;
//attaches double tap recognizer to button
UITapGestureRecognizer *doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleDoubleTap:)];
[doubleTapGestureRecognizer setNumberOfTapsRequired:2];
[btn addGestureRecognizer:doubleTapGestureRecognizer];
//Add Tap Recognizer to pucks to create new buttons
UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleSingleTap:)];
[singleTapGestureRecognizer setNumberOfTapsRequired:1];
[singleTapGestureRecognizer requireGestureRecognizerToFail:doubleTapGestureRecognizer];
[btn addGestureRecognizer:singleTapGestureRecognizer];
UIImage *buttonImage = [UIImage imageNamed:#"puck2.png"];
[btn setImage:buttonImage forState:UIControlStateNormal];
[self.view addSubview:btn];
}
//handles what happens on a double tap - THIS IS WHERE I THINK THE PROBLEM IS.
- (void)handleDoubleTap:(UIGestureRecognizer *)doubletap{
PuckSelected = self.view.tag;
[[self.btn viewWithTag:PuckSelected] removeFromSuperview];
}
#end
If you are trying to remove the button that is being double tapped, then try this.
- (void) handleDoubleTap:(UIGestureRecognizer *) doubletap {
[doubletap.view removeFromSuperview];
}
The UIGestureRecognizer has the view attached to it. There is no need to retrieve it again. Plus your problem was that you were trying to retrive the view from self instead of doubletap
Your handleDoubleTap method doesn't make sense.
Do something like this ::
- (void)handleDoubleTap:(UIGestureRecognizer *)doubletap{
PuckSelected = doubletap.view.tag;
[[self.view viewWithTag:PuckSelected] removeFromSuperview];
}
And it will work fine
- (void)handleDoubleTap:(UIGestureRecognizer *)doubletap{
PuckSelected = self.view.tag;
[[self.btn viewWithTag:PuckSelected] removeFromSuperview];
}
According to the code above, the button that gets removed it the button that has the same tag as self.view. But you don't show how self.views tag gets set, so it's impossible for us to know why or how your code selects the button that will be removed.
Regardless, you're probably heading down the wrong road here. It looks like you might have buttons nested inside a button? Which is odd. Also, you're using gesture recognizers with UIButtons, which don't need it. They already respond to things like taps and double-taps via the target-action mechanism.
Generally speaking, controls (that is, objects that derive from UIControl) already handle touches, taps, and other simple interactions. You'd really only use gesture recognizers on UIViews that need to track swipes or pinches or the like.
There seem to be 2 problems with your code.
1. When you do a single tap you are adding a button at (0,0,150,150) so any new button that gets added will be on top of the previous button. Maybe I am seeing this because of your abridged code, but you may want to do something about that.
2. In the UITapgesturerecognizer it will give you the view(id) that was tapped. You should use that in your selector. Like:
doubletap.view.tag.
I would like to get notified when a user clicks any part of the background of my UIView; basically if the user clicks outside of any control that is on my UIView (tables, textfields, buttons, etc).
How do I go about it? I tried doing this:
if ([touch.view isKindOf:[UIView class]]) {
do something....
}
But, obviously all controls extend UIView... so, I'm a bit stuck.
You should be able to do the following:
backgroundView.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapHit:)] autorelease];
[backgroundView addGestureRecognizer:tap];
-(void) tapHit:(UITapGestureRecognizer *tap) {
NSLog( #"background tapped" );
}
One way you could do this is add a separate background view that is a child of your VC's main view. You can simply make it transparent so it doesn't interfere with your styling. Place is in the behind all the other children views. Then register your GR on that view.