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.
Related
I am trying to embed a UITableView in my View Controller (not a TableViewController).
I am unable to get any of my data to show.
Here is the relevant code:
M file:
#interface ViewController () <CBCentralManagerDelegate, CBPeripheralDelegate, UITableViewDelegate, UITableViewDataSource>
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#property (strong, nonatomic) NSMutableArray *Device_array_name;
#end
.
.
.
- (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];
}
cell.textLabel.text=self.Device_array_name[indexPath.row];
return cell;
}
I've also defined number of sections and rows, and my array does indeed have content. I'm pretty new to iOS development so I've no doubt missed something.
Thanks
Delegates & Datasources should be defined for UI Controls to get called their respective delegates.
For Instance : In your viewDidLoad function define
tableView.delegate = self
tableView.datasource = self
It will work.
Set the array allocation in your view did load.
Device_array_name=[NSMutableArray alloc]init];
Set the tableview delegates in viewdidLoad
tableview.delegate=self;
tableview.datasource=self;
After give the count of an array in Number of rows in section
return Device_array_name.count;
In your cellForRow AtIndexpath method
cell.textLabel.text=[self.Device_array_name objectAtIndex:indexPath.row];
There could be 4 reason (most probably)
1> You delegate and datasource are not set
tableView.delegate = self
tableView.datasource = self
2> Your datasource array is nil (Device_array_name)
3> Your IBOutlet is not connected with tableview
4> Your tableview frame is becoming zero due to autloayout or something else (check console at runtime)
I think you just need to initialize the delegate and dataSource for your tableView property to your ViewController.
If you embed this way, you don't have default setting for the UITableView like you have when you use UITableViewController directly. You will need to set it up by yourself. Most likely, it's because you have not wired up your delegation object. Second will be, your UITableView is invisible. That also causes the problem because the delegate methods will not be called.
i think go to storyborad and set delegate,datasource in there.if you using by storyborad.i hope will work.
Here is my list of the possible problems:
The #IBOutlet for the table view is not connected in Interface Builder. Solution: check connection in Interface Builder.
The outlet is set, but the view controller placed in the storyboard is not of your custom UIViewController subclass, but the default UIViewController. In this case, none of your code will be executed. Solution: specify the view controller's class is the identity inspector (center tab).
The dataSource property of the table view is not set to your view controller. Solution: Assign it in either Interface Builder (storyboard) or programmatically inside viewDidLoad() or later.
You are not implementing the UITableviewDataSource protocol method:
tableView(_:numberOfRowsInSection:) (i.e., the table view "thinks" it has 0 rows to display). In this case, tableView(_:cellForRowAtIndexPath:) will not be called (you can confirm this by placing a break point). Solution: Implement the method and return the count property of your model object array.
The array that serves as the data source for your table view is empty (has zero elements). Solution: Make sure the array contains your model objects.
I might have left some other common mistake out... Let me know.
Addendum: Unless you have a compelling reason not to, I would strongly suggest that you use a stock UITableViewController instead of manually adding a UITableView to a plain UIViewController. You get a lot of functionality and setup for free.
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've created a header for the first section in my UITableView. The Header is created by a view in .nib file. I've connected the button to the Views owner which is a class called HeaderSection which is a subclass of UITableViewHeaderFooterView.
Here's the code for what should happen when the button is clicked.
#import "HeaderSection.h"
#implementation HeaderSection
- (IBAction)touchButton1:(UIButton *)sender {
NSLog(#"Touch button 1 tapped!");
}
#end
And here's a screen shot of things overall.
The problem is when I tap on the button, it doesn't detect the tap, instead the cell below the button detects the tap and the code runs accordingly (brings on a new UIViewController).
How can I make it detect the top button?
Here's more code from my main ViewController.
- (void)viewDidLoad
{
[super viewDidLoad];
UINib *sectionHeaderNib = [UINib nibWithNibName:#"headerNib" bundle:nil];
NSString *SectionHeaderViewIdentifier = #"sectionHeaderIndentifier";
[self.tableView registerNib:sectionHeaderNib forHeaderFooterViewReuseIdentifier:SectionHeaderViewIdentifier];
self.iconSets = [IconSet iconSets];
self.tableView.allowsSelectionDuringEditing = YES;
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(longPressGestureRecognized:)];
[self.tableView addGestureRecognizer:longPress];
self.filteredIcons = [NSMutableArray array];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 60.0f;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
NSString *SectionHeaderViewIdentifier = #"sectionHeaderIndentifier";
HeaderSection *sectionHeaderView = [self.tableView dequeueReusableHeaderFooterViewWithIdentifier:SectionHeaderViewIdentifier];
return sectionHeaderView;
}
And here's a screen shot of the view in the .xib (I've made the underlying view dark grey to stand out).
One problem you have is that in the .xib file, the top-level object must be set, in the idendity inspector, to be the custom UIView subclass that it is, or nothing will work.
However, it looks like other things are going on. The gray from the tableview cell underneath shouldn't be peaking out on the right of the footer. Is this being added in the tableView:viewForHeaderInSection: method of the UITableViewDataSource? It can adjust the headerView's frame there to be the width of the tableView. Depending on what the button is going to do, it might also need a reference to the view controller.
If the search bar and buttons are going to stay on top always, they don't really need to be a header. On that .xib you have, just put a tableview underneath the button bar. Create a ViewController with that file as the .xib, then connect that tableView's dataSource and delegate to that View Controller. You may need to create a ViewController with built-in xib to do this automatically, in or out of storyboards.
Freestanding xibs are discouraged but I think they're perfect for this case, tableView/collectionView reusable views. But if its on top of the table, not in it, it should be part of the view controller that contains the table.
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.
I'm trying to add a swipe gesture recognizer to a tableViewCell but it doesn't work.
This is the way I create my cell:
CellIdentifier = #"EventsSentCell";
nibObjcet = [[NSBundle mainBundle] loadNibNamed:#"EventsSentCell" owner:self options:nil];
EventsSentCell *cell = [[EventsSentCell alloc] init];
cell = (EventsSentCell *)[nibObjcet objectAtIndex:0];
and this is how my cell is initiated in the .m file:
-(id)init{
self = [super init];
if (self) {
leftSwipe = [[UISwipeGestureRecognizer alloc] init];
leftSwipe.direction= UISwipeGestureRecognizerDirectionLeft;
[leftSwipe addTarget:self action:#selector(swipedLeft)];
[self addGestureRecognizer:leftSwipe];
}
return self;
}
and this is how I declared my gesture recognizer in the .h file:
#property (nonatomic,strong) IBOutlet UISwipeGestureRecognizer *leftSwipe;
But for some reason my method isn't called.
Any ideas?
Thanks
I've tried putting the below code:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
NSLog(#"%#",gestureRecognizer);
return YES;
}
and the result i'm getting after swiping left is:
<UILongPressGestureRecognizer: 0xa9d99a0; state = Possible; view = <UITableViewCellContentView 0xa9d8ce0>; target= <(action=_longPressGestureRecognized:, target=<EventsSentCell 0xa9d8bb0>)>>
Before answering the actual question, let me point out some other issues in your code.
EventsSentCell *cell = [[EventsSentCell alloc] init];
cell = (EventsSentCell *)[nibObjcet objectAtIndex:0];
First of all, there's no point in these two lines. You're allocating and initializing an instance of EventSentCell without a nib. After doing this you're overwriting cell to point to the instance initialized by loadNibNamed:. You could simplify this to EventsSentCell = (EventsSentCell *)nibObject[0];
But even after these optimizations, this still isn't the recommended way to implement cellForRowAtIndexPath:. You should use registerNib:forCellReuseIdentifier: in viewDidLoad and then use
dequeueReusableCellWithIdentifier:forIndexPath: to get a cell and leave out loading the nib yourself completely.
Next,
#property (nonatomic,strong) IBOutlet UISwipeGestureRecognizer *leftSwipe;
You declare this property as an IBOutlet but you're setting it (as far as I know) only in code, more specifically the init method. You could just leave out the IBOutlet altogether.
And this init method is probably also the cause of your problem. When instantiating a view using loadNibNamed, initWithCoder: is called instead of init. Implement your custom initialization code (adding a gesture recognizer in this case) there and it should work just fine.
Your 'init' method is not getting called, so the gesture recognizer does not get set up.
You could try initialising in awakeFromNib instead, but anyway your cell creation looks unconventional.
Assuming you are using a custom cell class with a Xib file, here is how I would do it.
Create your EventsSentCell object .m and .h files
Create a xib file "EventsSentCell.xib"
In the xib file, delete the default top-level view and replace it with a UITableViewCell (you can drag one out from the objects library). In the identity inspector change it's class to EventsSentCell
In you table viewController's viewDidLoad...
UINib* EventsSentNib = [UINib nibWithNibName:#"EventsSentCell"
bundle:nil];
[self.tableView registerNib:EventsSentNib
forCellReuseIdentifier:#"EventsSentCell"];
In your cellForRowAtIndexPath method:
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:#"EventsSentCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"EventsSentCell"];
}
In EventsSentCell.m, trigger your initialisation code from -awakeFromNib
Your initialisation code:
leftSwipe = [[UISwipeGestureRecognizer alloc] init];
leftSwipe.direction= UISwipeGestureRecognizerDirectionLeft;
[leftSwipe addTarget:self action:#selector(swipedLeft)];
[self addGestureRecognizer:leftSwipe];
will work as it is.
You get the UILongPressGestureRecognizer:... response to your gestureRecogniser delegate method because that is a built-in gesture recognizer provided by Apple that has it's delegate set to the cell. When your code is working correctly, if you also set your gesture recongnizer's delegate to the cell (leftSwipe.delegate = self), you would expect to see a simlilar log for UISwipeGestureRecognizer:...
It is also noting that the UILongPressGestureRecognizer's view is NOT the cell, but the cell's contentView. This is the superview for your cell's view hierarchy, to which you should attach all of your cell's content. Although your gesture recognizer works when you attach it to the cell, I would advise following Apple here:
[self.contentView addGestureRecognizer:leftSwipe];
Then the gesture will correctly follow the cell's view hierarchy.