So, I've added a UITableViewController and UITableViewCell into a UIViewController and while the cellForRowAtIndexPath delegate method works, didSelectRowAtIndexPath does not. Does anyone have any ideas?
EDIT 2: The delegate for the UITableView is set to the UIViewController.
EDIT 3: I found the answer to my issue on another Stack question here. Basically I had a UITap... on my [self view] that was blocking the didSelectRow.... I have no idea why the tap blocks the delegate method and I also have no idea how to get both the tap and the table working together simultaneously.
EDIT: The part that bugs me is that I've gotten this exact setup working on an earlier app. So that means I've missed a step somewhere along the way. The thing is, I've combed over all the steps and have compared previous app vs current app and I really have no idea what I missed.
I've added logging to both delegate methods and while one outputs, the other does not.
ViewController.h
#import "...TableViewCell.h"
...
UITableViewDataSource,
UITableViewDelegate
...
ViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"cellForRowAtIndexPath");
...
return Cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"didSelectRowAtIndexPath");
}
...TableViewCell.h (contents not important)
...TableViewCell.m (contents not important)
I found the answer on another StackOverflow question.
I had a UITapGestureRecognizer added to [self view] which I commented out, and then the delegate method worked.
Can anyone please tell me why this worked and also how I can get the UITapGestureRecognizer working on the same screen as the UITableView?
// Hide keyboard when user taps outside of it
UITapGestureRecognizer *tapGestureRecognizer =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(hideKeyboardOnTap)];
//[[self view] addGestureRecognizer:tapGestureRecognizer];
EDIT: Corrected typo of UITapeGestureRecognizer to UITapGestureRecognizer
have you set the delegate of the tableView?
myTableView.delegate = self;
EDIT: My bad, did not read that cell for row is being called.
You say that you have used a custom tableViewController. If you have overridden the didSelectRowAtIndexPath method, it might be important to call [super didSelectRowAtIndexPath:] in the tableViewController
EDIT 2: One more thing. I do not know the reason for this, but I faced the same issue some time back in a viewController. I resolved it by adding an empty implementation of didDeselectRowAtIndexPathin the same viewController. Try adding it to your table's delegate controller.
To answer to the question "how I can get the UITapGestureRecognizer working on the same screen as the UITableView";
You should "inform" your GestureRecognizer that other Recognizer can handle the same gesture:
You do that by implementing the method of UIGestureRecognizerDelegate
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
This is a small exemple...
#interface MyController : UIViewController<UIGestureRecognizerDelegate>
{
}
#end
-(void)viewDidLoad
{
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(actionOnTapGesture:)];
[gr setNumberOfTapsRequired:1];
[gr setDelegate:self];
[self.view addGestureRecognizer:gr];
}
-(BOOL) gestureRecognizer:(UIGestureRecognizer *) gestureRecognizer shouldRecognizeSimultaneouslyGestureRecognizer:(UIGestureRecognizer *) otherGestureRecognizer
{
return YES;
}
Check in the interface builder if the property selection is set to 'No selection'.
Change it to 'Single selection' or other option according to your needs.
This might be the reason why didSelect is not getting triggered.
Related
I am new to objective c, and i am very interested in learn new technologies.
I have a UITapGestureRecognizer on a label. I am using 2 label ,when i tap on label1 load some data Tableview and when I tap on label 2 i want to change the Tableview.
I didn't find any solution for my requirement. so anyone help me .
suggest me guy's how to change the TableView data when tap on UILabel.
You can do this like following way:
Way 1:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
..
..
..
// Here is your stuff for cell
UILabel *lbl1 = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 80, 40)];
[lbl1 setText:#"Label"];
[lbl1 setUserInteractionEnabled:YES];
[cell.contentView addSubview:lbl1];
UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapAction:)];
tap.tag = [NSIndexPath row];
[tap setNumberOfTapsRequired:1];
[lbl addGestureRecognizer:tap];
...
}
- (void)tapAction:(id)sender {
switch(((UITapGestureRecognizer *)sender).view.tag) {
case 0:
// code for first TableView
break;
case 1:
// code for second TableView
break;
....
}
}
Way 2:
in cellForRowAtIndexPath: method:
UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapAction:)];
tap.tag = [NSIndexPath row];
...
Handler method:
- (void)tapAction:(id)sender
{
switch(((UITapGestureRecognizer *)sender).tag)
{
case 0:
NSLog(#"Tap action 1");
break;
case 1:
NSLog(#"tap action 2");
break;
..... }
I assume you have setup the datasource/delegate methods for the tableview? Then change necessary things within these methods when the label is tapped and simply call
[self.tableView reloadData]
If you don't understand anything about what I'm saying then I advise you to first read some manuals about tableviews before starting asking questions on SO :)
Welcome to Stack Overflow!
This answer should help clear things up for you nicely and give you a stronger understanding of how to use table view's and buttons to do what you need.
FIRST
To Alladinian's comment, I highly recommend switching over to the UIButton object instead of using UILabel objects. This way your elements can still display your text (as you were with labels) but you are able to make use of the built-in target capabilities of the button class.
- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
SECOND
Regarding your question, again, simply make use of an already built-in method that is designed for this very situation:
- (void)reloadData;
This method is available by using / setting UITableViewDelegate and the UITableViewDataSource. Like such: here is an example of a generic UIViewController class that conforms (can use properly) the convenient UITableView delegates.
Header File (SomeClassController.h)
// SomeClassController.h
#import <UIKit/UIKit.h>
#interface SomeClassController : UIViewController <UITableViewDelegate, UITableViewDataSource>
// do anything here. properties, methods, etc :)
#property (nonatomic, ...) UITableView *tableView;
#end
Implementation File (SomeClassController.m)
// ...
- (void)viewDidLoad {
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
Now by using UIButton objects you can set a method that will handle the user touching the screen by performing a method. Like here:
- (void)viewDidLoad {
...
[someButton addTarget:self action:#selector(didTouchButton:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)didTouchButton:(__kindof UIButton *)button {
// Here you received the users touch event on the button, do what you like!
// As your question asked how to reload table data, simply add this line
// after following my other steps.
[self.tableView reloadData]; // Here the magic is done!
// This method will automatically reload the table view to display the
// most recently updated data you have given it.
}
Make sure you change your data before reloading, otherwise it may not change both visually or content-wise.
Happy coding!
I am working on a app using TableView now i am facing an issue listed below.
Inside my TableView there is UITextView on it, that MUST be selectable, but not editable (because I need to use and proceed links).
My issue is:
when I tap on a link as everybody does, it doesn't work. I need to hold it a bit longer to make it work. I thought that it is because of "Selectable" property brings in a Double Tap Gesture recognizer, so my textView checks if there is a second tap, but I don't know how to find and remove only double tap recognizer.
What should I do?
Thank you.
Have you considered replacing the TextView with a UIWebView, and just do a loadHTMLString function?
This way when you tap on a link, it will open instantly? You can even have a UIWebView delegate and do what you want when the link is pressed(Custom UIWebView instead of auto opening in safari etc)
You've to handle tap event.. Through this code
tapGesture.numberOfTapsRequired = 1
OR
To do this, you will need to embed one in your UITableViewCell. But there's no need to create a custom cell. Here is the basic idea of what you will want to do:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UITextView *comment = [[UITextView alloc] initWithFrame:CGRectMake(cell.frame.origin.x, cell.frame.origin.y, cell.frame.size.width, tableView.rowHeight)];
comment.editable = NO;
comment.delegate = self;
[cell.contentView addSubview:comment];
[comment release];
}
return cell;
}
You will, of course, need to set your rowHeight if you don't want the standard 44pt height that comes with the cell. And if you want actual cells, you'll need to add your own logic so that only the cell you want is a textView, but this is the basic idea. The rest is yours to customize to your fitting. Hope this helps
EDIT: to bypass the textView to get to your cell, there are two ways to go about this.
1) you can make a custom textView class and overwrite touchesBegan to send the message to super:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
}
this will send the touch events to its superview, which would be your tableView. Considering you didn't want to make custom UITableViewCells, I imagine you probably don't want to make a custom textView class either. Which leads me to option two.
2) when creating the textView, remove comment.editable = NO;. We need to keep it editable, but will fix that in a delegate method.
In your code, you will want to insert a textView delegate method and we'll do all our work from there:
EDIT: changing this code to use with a UITableViewController
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
// this method is called every time you touch in the textView, provided it's editable;
NSIndexPath *indexPath = [self.tableView indexPathForCell:textView.superview.superview];
// i know that looks a bit obscure, but calling superview the first time finds the contentView of your cell;
// calling it the second time returns the cell it's held in, which we can retrieve an index path from;
// this is the edited part;
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
// this programmatically selects the cell you've called behind the textView;
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
// this selects the cell under the textView;
return NO; // specifies you don't want to edit the textView;
}
If that's not what you wanted, just let me know and we'll get you sorted out
Finding and Removing Double Tap Gesture recognizer
Objective C
- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]])
{
[(UITapGestureRecognizer *)gestureRecognizer setNumberOfTapsRequired:1];
gestureRecognizer.enabled = NO;
}
}
Swift
func addGestureRecognizer(gestureRecognizer: UIGestureRecognizer)
{
if gestureRecognizer.isKindOfClass(UITapGestureRecognizer)
{
(gestureRecognizer as! UITapGestureRecognizer).numberOfTapsRequired = 1
gestureRecognizer.enabled = false
}
}
I have a view controller with a UITableView. I have both the datasource and delegate set to the view controller. The method cellForRowAtIndexPath: is being called and behaving as expected however didSelectRowAtIndexPath: is not being called. Any ideas whats going on here?
I have this in my .h:
#interface DetailViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
And in my viewDidLoad in the .m I have:
self.tableView.dataSource=self;
self.tableView.delegate=self;
It doesn't seem to make sense that the cellForRowAtIndexPath would be called but not didSelectRowAtIndexPath.This is what the method looks like for now:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"did select row at index path");
}
I have encountered some possible reasons:
Make sure the the editing property of tableView is set to NO. The didSelectRowAtIndex: method isn't called when the editing property of table is set to YES. By default this property is set to NO.
self.tableView.editing = NO;
Make sure the allowsSelection property is set to YES. By default it is.
Try to call.[super viewDidLoad] in the viewDidLoad method or setting the delegate and the dataSource properties in the init initializer.
Set the delegate and the dataSource properties in the loadView method and assign the tableView to the view property of the UIViewController. Here's an example:
- (void)loadView
{
UITableView *tableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain];
tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
tableView.delegate = self;
tableView.dataSource = self;
[tableView reloadData];
self.view = tableView;
}
(this is, actually, what I usually do) Try to subclass DetailViewController from UITableViewController which inherits from UIViewController too. UITableViewController comforms automatically to UITableViewDelegate and UITableViewDataSource.
Gesture recognizer on a view controller's view prevented touches, solved by setting gesture recognizer's cancelsTouchesInView to false.
In my case, I used UITapGestureRecognizer and it worked like a magic for me.
cell.tag=indexPath.row;
UITapGestureRecognizer* tap=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(tapOnCell:)];
[cell addGestureRecognizer:tap];
Here is my tapOnCell method implementation
-(void)tapOnCell:(UITapGestureRecognizer*)tap
{
ADPointCashTransaction* aTransaction=[transactionArray objectAtIndex:tap.view.tag];
// Work with aTransaction Object
}
Putting the answer in the comments of seto nugroho with my work around the problem. If you have a gesture recognizer in the tableView, then the didSelect row will not be called. What you can do is increase the number of taps for the gesture recognizer to two.
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
gestureRecognizer.numberOfTapsRequired = 2
view.addGestureRecognizer(gestureRecognizer)`
I'll add one more: make sure you don't have any other views overlapping the table or cells at any point, even hidden ones. If you do, disable 'User interaction enabled' in storyboard unless you explicitly need to catch gestures through the view.
In my case, I was assigning the dataSource and calling reloadData() all in a background thread. So, make sure you are doing this on the main thread.
I have created an app using a storyboard. In other screens, I have used a UITableViewController directly and the selection is working as expected.
In this case, I have a UITableView that is one of several controls within a UIViewController.
My custom ViewController.h file has a definition similar to the below:
#interface MyViewController : UIViewController <UITableViewDelegate,UITableViewDataSource>
#property (weak,nonatomic) IBOutlet UITableView *myTableView;
#end
Then within viewDidLoad I am doing this:
_myTableView.delegate = self;
_myTableView.dataSource = self;
Having done this, my numberOfSectionsInTableView, numberOfRowsInSection and cellForRowAtIndexPath methods are all being called and my table looks as I want it to.
The problem I have is that the rows do not select and the didSelectRowAtIndexPath method is not getting called.
I have checked that Selection is set to Single Selection in the storyboard view and I have also tried to set _myTableView.allowsSelection=YES; in viewDidLoad but this doesn't seem to make any difference.
I know that this is probably something to do with the fact that my table is within a normal view controller, but I can't figure out the magic step I've missed to make the selection work.
For now I have added a workaround. In cellForRowAtIndexPath I have attached a UITapGestureRecognizer to each view in the cell:
for(UIView *view in cell.contentView.subviews){
[view setUserInteractionEnabled:YES];
UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapRow:)];
[tap setNumberOfTapsRequired:1];
[view addGestureRecognizer:tap];
view.tag = row; // So that I can identify in the handler which row has been tapped
}
Then in the handler:
-(void)tapRow:(id)sender{
UITapGestureRecognizer *gesture = (UITapGestureRecognizer*)sender;
UIView *myView = (UIView*)gesture.view;
int row = myView.tag;
// Handle tap of row here
}
This achieves what I need, but I would still like to figure out what I've done wrong with the row selection!
Make sure there is not another view on top of the table view (e.g. a transparent view that would be stealing touch events). Also ensure that userInteraction is enabled for the tableview and all of its parent views.
We type sometime wrong delegate call didDeselectRowAtIndexPath instead of didSeselectRowAtIndexPath. Is it your case?
I see that you can update your content in the tableview so the connection should be OK. I think about mistakes with function names.
Just to test: Replace your didSelectRowAtIndexPath function with this (copy, paste).
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Selected %d",indexPath.row);
}
Just incase anyone still has this problem. I just realised -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath method won't work for any view or subview with a tap gesture recogniser set to it. So remove the tap gesture recogniser from that view of it's parent view. if you still want to handle touch events, then use "touchesBegan" function. ` - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
}`
I had the exactly the same problem with my current project for two minutes yesterday when I realised that I didn't link my delegate and dataSource of the table view. You made it programmatically, so it should work. For me was a delegation problem, you can check your Attributes Inspector to make sure that everything is ok.
In cellForRowAtIndexPath method add this line
cell.userInteractionEnabled = NO;
I added the SWRevealViewController to my app, along with the hamburger stack to access my menu. My app has a UITableView for the main application view. I want to allow users to delete items from the list using the swipe to delete function.
I enabled the swipe to delete and added a method call to handle this. I noticed that the method never gets called. Not sure how to get this to work.
Any help would be greatly appreciated.
I was asked for a code example. I have not made any changes to the SWRevealViewController standard source code. In my code for the TableView, I have added the following:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
Normally, the code above should show a delete button when you do a right to left swipe. However, this is not happening. My guess is that the SWRevealViewController is eating the pan gesture recognizer.
Just found a solution after reading a thread of a guy asking a similar question to the maker of this class. Whatever class you use to add the gesture recognizers, make it the delegate of the SWRevealController then paste in this method.
#pragma mark - SWRevealViewControllerDelegate
- (BOOL)revealControllerPanGestureShouldBegin:(SWRevealViewController *)revealController
{
float velocity = [revealController.panGestureRecognizer velocityInView:self.view].x;
if (velocity < 0 && self.revealViewController.frontViewPosition == FrontViewPositionLeft)
return NO;
else
return YES;
}
I have done like this < UIGestureRecognizerDelegate> in .h file in which you are implementing tableview cell delete functionality and in .m file viewDidLoad
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
self.revealViewController.panGestureRecognizer.delegate = self;
After that simply use this method
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([NSStringFromClass([touch.view class]) isEqualToString:#"UITableViewCellContentView"])
return NO;
else
return YES;
}