I have created a UITableView with a custom UITableViewCell. My cell includes one UIImageView on the left and UITextView on the right.
Inside UITableViewController, I set both image and text in tableview cellForRowAtIndexPath.
Everything shows fine but now I need to implement didSelectRowAtIndex and I need to distinguish if UIImageView or UITextView of the cell has been clicked.
Let's say, image clicking is representing delete action and the rest of the cell editing action.
Rather than adding the gesture recognisers to each individual cell, you can add one to the table view and determine which cell was selected from the point of the users touch, and then determine if the user touched the image or the cell.
First make sure your controller adopts the UIGestureRecognizerDelegate protocol.
#interface MyTableViewController() <UIGestureRecognizerDelegate>
#end
Then add the UIGestureRecognizer to the UITableView when the view loads.
- (void)viewDidLoad
{
[super viewDidLoad];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
singleTap.delegate = self;
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self.tableView addGestureRecognizer:singleTap];
}
This delegate method determines if the handleTap: method should be executed. If it can find an indexPath from the users touch, then it returns YES otherwise it returns NO.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
UITableView *tableView = (UITableView *)gestureRecognizer.view;
CGPoint p = [gestureRecognizer locationInView:gestureRecognizer.view];
if ([tableView indexPathForRowAtPoint:p]) {
return YES;
}
return NO;
}
Once we have determined if the user has clicked in a cell, the handleTap: method is called, which then decides if the user touched the image, or any other part of the cell.
- (void)handleTap:(UITapGestureRecognizer *)tap
{
if (UIGestureRecognizerStateEnded == tap.state) {
UITableView *tableView = (UITableView *)tap.view;
CGPoint p = [tap locationInView:tap.view];
NSIndexPath* indexPath = [tableView indexPathForRowAtPoint:p];
[tableView deselectRowAtIndexPath:indexPath animated:NO];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
CGPoint pointInCell = [tap locationInView:cell];
if (CGRectContainsPoint(cell.imageView.frame, pointInCell)) {
// user tapped image
} else {
// user tapped cell
}
}
}
You could subclass UITableViewCell and override touchesEnded.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
UIView *hitView = [self hitTest:location withEvent:event];
if (hitView == myImageView) ...;
if (hitView == myTextView) ...;
}
You need to keep some reference to your UIImageView and UITextView (they should probably be properties of your cell).
You can of course override touchesBegan instead of touchesEnded, depends on what functionality you want to achieve.
a very abstract and general answer is to do the following
For each UIImage and UILabel you add set their tag to be the indexPath.row
//When creating the label and image add a recognizer to them
label.tag = indexPath.row;
imageView.tag = indexPath.row;
Then add a UITapGestureRecognizer on each image and label, like so
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleTap:)];
[label addGestureRecognizer:recognizer];
[imageView addGestureRecognizer:recognizer];
}
- (void) handleTap:(UITapGestureRecognizer*)recognizer
{
UIView *view = recognizer.view;
int row = view.tag;
if ([view isKindOfClass:[UILabel class]]) {
//Row is row
//and the label is pressed
}
if ([view isKindOfClass:[UIImageView class]]) {
//Row is row
//and the imageview is pressed
}
}
Make the image a UIButton. When the button's action is triggered, you know the user tapped the image (cell will NOT be selected). If cell is selected, you know the user tapped somewhere else on the row (including text view or label).
Also, set the button's tag property to, e.g. the cell's row index so you can know which row's image was tapped.
you have two option is to implement :-
1--add UITapGestureRecognizer in your "uitableviewcell" and make it point to image view
and pass "indexpath" as parameter to selector and make the delegate pass it to tableviewcell
UILabel *label = =[UILabel alloc]init];
label.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture =
[[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(labelTap)] autorelease];
[label addGestureRecognizer:tapGesture];
-(void)labelTap{
[delegate performselector#selector(labeltapped:)withobject:indexpath];
}
2- Second way is to check the sender of DidSelectRowAtIndexPath of type [imageview or label];
but i prefer the first way
Related
In my iPhone UITable view application
I need to add Two Buttons when user swipes on table view Cell / Row.
One for Compose mail button one for Send sms button to the contacts.
I implemented following code to add Send Mail (Blue Cloud Button) only for now
//Add a left swipe gesture recognizer in view Did load
UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(swipeRow:)];
[recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)];
[contactsTable addGestureRecognizer:recognizer];
[recognizer release];
- (void)swipeRow:(UISwipeGestureRecognizer *)gestureRecognizer
{
//Get location of the swipe
CGPoint location = [gestureRecognizer locationInView:contactsTable];
NSIndexPath *indexPath = [contactsTable indexPathForRowAtPoint:location];
UITableViewCell *cell = [contactsTable cellForRowAtIndexPath:indexPath];
UIButton *MailButton = [UIButton buttonWithType:UIButtonTypeCustom];
[MailButton setBackgroundImage:[UIImage imageNamed:#"sendData.png"] forState:UIControlStateNormal];
MailButton.frame=CGRectMake(150, 0, 40.0, 30.0);
[MailButton addTarget:self action:#selector(SendMail) forControlEvents:UIControlEventTouchUpInside];
//Add mail button if index path is valid
if(indexPath)
{
[cell addSubview:MailButton];
}
}
It execute fine and add button to the selected cell/row when we swipe on the cell.
But when we swipe another cell the buttons on the previous cell are does not hide / removed repeated as follows
I want to not showing (remove) buttons on the other cells while taping swipe on the other cell Only one cloud button At a time ..
Why not you can keep the button in XIb and add it to respective cell? It is a single object for the entire class, so there will be no duplicate of buttons in previous cells. Please try.
What you need is one ivar for storing the lastIndexPath where you added the cloud button.
And on every time to add new button you have to remove the button from the perviously added indexPath and assign the new indexPath to the ivar.
Your code may be look like this (not tested).
in .h file on property like
#property (strong, nonatomic) NSIndexPath *lastIndexPath;
and in .m file synthesize it.
Now the code will be look like this
UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(swipeRow:)];
[recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)];
[contactsTable addGestureRecognizer:recognizer];
[recognizer release];
- (void)swipeRow:(UISwipeGestureRecognizer *)gestureRecognizer
{
//Get location of the swipe
CGPoint location = [gestureRecognizer locationInView:contactsTable];
NSIndexPath *indexPath = [contactsTable indexPathForRowAtPoint:location];
//New Code
UITableViewCell *cell = [contactsTable cellForRowAtIndexPath:indexPath];
if(lastIndexPath != nil)
UITableViewCell *last = [contactsTable cellForRowAtIndexPath:lastIndexPath];
//End
UIButton *MailButton = [UIButton buttonWithType:UIButtonTypeCustom];
[MailButton setBackgroundImage:[UIImage imageNamed:#"sendData.png"] forState:UIControlStateNormal];
MailButton.frame=CGRectMake(150, 0, 40.0, 30.0);
[MailButton addTarget:self action:#selector(SendMail) forControlEvents:UIControlEventTouchUpInside];
MailButton.tag = 1111;// New code
//Add mail button if index path is valid
if(indexPath)
{
//New Code
UIButton *mb = (UIButton *) [cell viewWithTag:1111];
[mb removeFromSuperview];
//End
[cell addSubview:MailButton];
lastIndexPath = indexPath;// New Code
}
}
I have this in my table header view section:
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(sectionHeaderTapped:)];
I want to pass the section number in the method sectionHeaderTapped so i can recognize which section got tapped.
My method implementation looks like this:
-(void)sectionHeaderTapped:(NSInteger)sectionValue {
NSLog(#"the section header is tapped ");
}
How can I achieve this?
The method sectionHeaderTapped should have one of the following signatures:
- (void)sectionHeaderTapped:(UITapGestureRecognizer *)sender;
- (void)sectionHeaderTapped;
You have to figure out the cell that was tapped using the coordinates of the tap.
-(void)sectionHeaderTapped:(UITapGestureRecognizer *)gestureRecognizer
{
CGPoint tapLocation = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *tapIndexPath = [self.tableView indexPathForRowAtPoint:tapLocation];
UITableViewCell* tappedCell = [self.tableView cellForRowAtIndexPath:tapIndexPath];
}
You can probably get the section header using that method. But it may be easier to attach a different gesture recognizer to each section header.
- (UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section
{
// ...
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(sectionHeaderTapped:)];
[headerView addGestureRecognizer:tapGesture];
return headerView;
}
And then
-(void)sectionHeaderTapped:(UITapGestureRecognizer *)gestureRecognizer
{
UIView *headerView = gestureRecognizer.view;
// ...
}
An alternate : You can add UIButton on the tableHeaderView and get click of button.
I got this solution on this site: Click an UIImage and open an UIImageView in Objective-c
Add UITapGestureRecognizer to your UIImageView:
UITapGestureRecognizer *tapRecognizer;
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(yourSelector)];
[thumbnail addGestureRecognizer:tapRecognizer];
[tapRecognizer release];
thumbnail.userInteractionEnabled = YES; // very important for UIImageView
This is working very fine for single ImageView, but I am adding more than one (about to 20) to my scrollView then How can I differentiate which ImageView will tapped or selected by user. I tried to set my own #selector(imageClicked), but it only returns tag for last imageView.
I am adding addGestureRecognizer in a loop, as I load 20 static images dynamically in an imageView.
This might help
for(int i=0;i<20;i++)
{
UIImageView *img=[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"yourimage.png"]];
[img setTag:i];
img.frame= //set frame accordingly;
img.userInteractionEnabled = YES;
UITapGestureRecognizer *tap =
[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[img addGestureRecognizer:tap];
[tap release];
[scrollView addSubView:img];
}
- (void)handleTap:(UITapGestureRecognizer *)recognizer {
UIImageView *imageView = (UIImageView *)recognizer.view;
switch([imageView tag])
{
case 1:
//do your work
break;
.
.
.
.
case n:
}
}
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
for (UIImageView *thumbnail in imageArray) {
[thumbnail addGestureRecognizer:tapRecognizer];
}
[tapRecognizer release];
You can get the view from the property "view" of UIGestureRecognizer.
In your selector, for example:
- (void)handleTap:(UITapGestureRecognizer *)recognizer {
UIImageView *imageView = (UIImageView *)recognizer.view;
// Now do something with your view
}
You cannot add a single tap recognizer to multiple views. Create a new one for every view you want to add a tap recognizer to. Since you are using a tableview just do that in the tableView:cellForRowAtIndexPath: method:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// usual implementation
static NSString *cellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeue ...];
if (!cell) {
cell = [[UITableViewCell alloc] init....];
// add new gesture recognizer here.
}
// setup cell: set the image (just an example)
cell.imageView.image = [images objectAtIndex:indexPath.row];
return cell;
}
Instead of using tags like mentioned in the other answers, and just getting the imageview try to work with the underlying model. When handling the tap, find the indexPath to know what model object to access:
- (void)handleTap:(UITapGestureRecognizer *)recognizer {
UIImageView *imageView = (UIImageView *)recognizer.view;
// assumes the image view is direct subview of the cell
// change to match your cell structure
UITableViewCell *cell = (UITableViewCell *) [imageView superview];
// get the index path for the cell clicked
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
// TODO: Use index path to get full image to display
}
This way you know the exact row of the image clicked, so you can access your model to get access to the full image to display.
You need to add tag to all images - thumbnail.tag = 100 for example.Then modify your selector to youSelector:(UITapGestureRecognizer *)sender;
In selector add switch
- (void) yourSelector:(UITapGestureRecognizer *)sender {
UIImageView *imageView = (UIImageView *)sender.view;
switch(imageView.tag) {
case 100: {
//This code will be handled if tag == 100;
}
}
}
Please try to subclass the ImageView & add the gesture recognizer to the subclass.
Now for each image create the ImageView object and add the image to that object.
Set some unique property so that you can identify which object click like the name of the image.
i have a main view controller with two subview in it,one is note-view and other one is share-view . When i click the cell of the tableview that is in the main-view controller ,it popups note-view,there is button in the note-view ,if the user tap the button it pop sup a another subview ,the share-view .But i need to close the share view when the user tap the note-view.Is that possible to implement a touch event in a subview ?
I have done this coding but result is error
in viewdidload
NotesView *label =[[NotesView alloc]init];
UITapGestureRecognizer *tapGesture =
[[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(NoteTap)] autorelease];
[label addGestureRecognizer:tapGesture];
[tapGesture release];
then
-(void)NoteTap
{
UIView *langview = (UIView *)[self.view viewWithTag:120];
//ViewWithTag Number should be same as used while allocating
[langview removeFromSuperview];
}
Inside touchesBegan: of the view controller, you can check to see if a touch event takes places within the frame of the subview like so.
CGPoint touchPoint = [touch locationInView:self];
if (CGRectContainsPoint(label.frame, touchPoint)) {
UIView *langview = (UIView *)[self.view viewWithTag:120];
[langview removeFromSuperview];
}
I have a scrollView onto which some imageViews are added.
I want that when scrollView scrolls then also the imageViews should get touch events.
But it is not happening in my case.
Only one of the two things happen at a time.
Either the scrollView scrolls but the imageViews do not get touch events
or the imageViews get touch events and the scrollView does not get scrolled.
Can't both of the things happen simultaneously.
Please suggest.
You can use "Tap Gesture Recognizer" to get the events on your images while it's being added to the scroll view.
EDIT
You can refer to this code:
-(void)yourGestureRecognizer
{
UITapGestureRecognizer *yourTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[yourImageView addGestureRecognizer:yourGestureRecognizer];
[yourGestureRecognizer release];
}
You can handle tap here:
-(void)handleTap:(UIGestureRecognizer *)sender{
UIImageView *imageView = (UIImageView *)sender.view;
switch (imageView.tag)
{
case 1:
m_pYourInstance=[[ViewController alloc]initWithNibName:#"ViewController" bundle:nil];
[self.navigationController pushViewController:XXXXX animated:YES];
break;
}
Hope this helps !!
I know this was posted a while ago, but...
By default, UIImageView objects have their property userInteractionEnabled set to NO. This could be why touches aren't registering. This has tripped me up on more than one occasion and it's one of those little things that only experience will help.
Set it to YES and see if that fixes it.
Use UIGestureRecognizer. Add the recognizer to your image views.
And if you are targeting some old iOS version, have a look at this link.
yes you can do both. you need to customize your uiimageview by sub classing it and have to add Tap Gesture Recognizer like :
in .h file
#interface CustomView : UIImageView
{
// your variables
}
in .m file
//when you create an instants of your image view UITapGestureRecognizer will added in all your instants.
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
// add Gesture for tapping
UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(tap:)];
tap.numberOfTapsRequired = 1;
[self addGestureRecognizer:tap];
}
return self;
}
then add delegate methods in .m file too
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch *touch = [touches anyObject];
}
now in your custom cell, add your custom image view with Tap Gesture Recognizer
hope it will help you...
Use UIButtons with background images set on them. Here's the loop you use to layout UIScrollView subviews:
for (int i = 0; i < [images count]; i++) {
CGRect aFrame = ...;
UIButton *button = [[[UIButton alloc] initWithFrame:aFrame] autorelease];
[button setImage:[images objectAtIndex:i] forState:UIControlStateNormal];
button.tag = i;
[button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[scrollView addSubview:button];
}
And here you process the touch event:
- (void)buttonClicked:(id)sender {
UIButton *button = (UIButton *)sender;
[self performActionWithImageAtIndex:[images objectAtIndex:button.tag]];
}
That will omit the need to create additional gesture recognizers and will lower the amount of boilerplate work.
-- edit
The other reason your UIImageViews are not detecting the touches is because you create them in code and forget to set userInteractionEnabled to YES.