I am having issues with my tableView not firing the didSelectRowAtIndexPath method. I have implemented the delegates as such:
#interface ViewController : UIViewController<UITableViewDataSource, UITableViewDelegate,UIScrollViewDelegate>
And in my storyboard the tableView's data source and delegate are both pointed at the base View Controller. I have User Interactions enabled as well as Selection set to Single Selection, and it is not the TapGesture problem since my tap gestures are not bound to the view and I have checked and they do not fire.
This is the code for setting up the table:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return menuArray.count;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"menuCell"];
NSDictionary *menuItem = [menuArray objectAtIndex:indexPath.row];
cell.textLabel.text = menuItem[#"Title"];
cell.detailTextLabel.text = menuItem[#"Subtitle"];
return cell;
}
-(void)showMenu{
[UIView animateWithDuration:.25 animations:^{
[content setFrame:CGRectMake(menuTable.frame.size.width, content.frame.origin.y, content.frame.size.width, content.frame.size.height)];
}];
}
-(void)hideMenu{
[UIView animateWithDuration:.25 animations:^{
[content setFrame:CGRectMake(0, content.frame.origin.y, content.frame.size.width, content.frame.size.height)];
}];
}
-(IBAction)showMenuDown:(id)sender {
if(content.frame.origin.x == 0)
[self showMenu];
else
[self hideMenu];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//whatever
}
The table is initially out of view on the storyboard (origin.x is set to -150), then when the user clicks on a button in the navigationBar, the view slides over to reveal it, which is what might be causing the problem I think.
Is there anything wrong with my code or implementation that would be causing this to not work?
If you already see your table populated with values from your dictionary then you can rule out data source and delegate as being the problem. i.e. your storyboard connections are working.
Your code looks fine to me. the only difference I see is I usually define my table like this. Try this and see if it helps.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSLog(#"Inside cellForRowAtIndexPath");
static NSString *CellIdentifier = #"Cell";
// Try to retrieve from the table view a now-unused cell with the given identifier.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// If no cell is available, create a new one using the given identifier.
if (cell == nil)
{
// Use the default cell style.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
//Your code here
// ....
return cell;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"menuCell"];
This will return nil in case there was never a cell created.
so checking if cell is nil is mandatory and if so, you need to create a cell.
static NSString *CellIdentifier = #"menuCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil) {
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
as you are using storyboard you can alternatively use
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
for prototype cells. Make sure you use the same identifier in the storyboard and that you registered your the cell's class
- (void) viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"menuCell"];
}
Related
I'm new in CoreData and using MagicalRecord to rule with it. My problem is that I have the UITableView with an NSArray as dataSource populated with objects which fetched from CoreData db, and everything seems fine until I scroll the table for some times.
Here is my code:
Method for fetching data (MyDatabase.m):
+(NSArray *)getEntities{
...
return [MyEntity MR_findAllSortedBy:#"name" ascending:YES withPredicate:predicate];
}
Here is how I fetch and set data to UITableView in my ViewController:
- (void)viewDidLoad {
[super viewDidLoad];
myEntitiesArray = [MyDatabase getEntities];
if(myEntitiesArray.count != 0)
[myTableView setTableData:myEntitiesArray];
}
Here is setTableData method implementation in MyTableView.m:
- (void)setTableData:(NSArray *)array {
if (array && [array count] > 0) {
_tableData = array;
[self reloadData];
}
}
And here is how I set up my cells in MyTableView.m:
- (void)tableView:(UITableView *)tableView willDisplayCell:(SSCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.nameLabel.text = [(MyEntity *)_tableData[indexPath.row] name];
}
I tried to put an NSLog(#"name is %#",[(MyEntity *)_tableData[indexPath.row] name]) into willDisplayCell and found that when cells become empty, NSLog prints out the messages "name is (null)". I know this question is possibly solved by many people and many times before I faced this problem. Hope someone will help me to solve it too :)
UPDATE: cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"ssCell";
SSCell *cell = (SSCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if( !cell ) {
[self registerNib:[UINib nibWithNibName:#"SSCell" bundle:nil] forCellReuseIdentifier:cellIdentifier];
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
[cell setSelectedBackgroundView:selectedBackgroundView];
}
cell.nameLabel.text = [(MyEntity *)_tableData[indexPath.row] name];
return cell;
}
I also call this method inside MyTableView.m init method:
[self registerNib:[UINib nibWithNibName:#"SSCell" bundle:nil] forCellReuseIdentifier:#"ssCell"];
You have to use cellForRowAtIndexPath. In this method the cells are allocated.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
/*
* This is an important bit, it asks the table view if it has any available cells
* already created which it is not using (if they are offscreen), so that it can
* reuse them (saving the time of alloc/init/load from xib a new cell ).
* The identifier is there to differentiate between different types of cells
* (you can display different types of cells in the same table view)
*/
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
/*
* If the cell is nil it means no cell was available for reuse and that we should
* create a new one.
*/
if (cell == nil) {
/*
* Actually create a new cell (with an identifier so that it can be dequeued).
*/
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MyIdentifier"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
/*
* Now that we have a cell we can configure it to display the data corresponding to
* this row/section
*/
cell.nameLabel.text = [(MyEntity *)_tableData[indexPath.row] name];
return cell;
}
You should be initializing the cell by calling init. Instead you are doing the following:
if( !cell ) {
[self registerNib:[UINib nibWithNibName:#"SSCell" bundle:nil] forCellReuseIdentifier:cellIdentifier];
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
[cell setSelectedBackgroundView:selectedBackgroundView];
The second call attempts to again reuse an existing cell when there isn't one available. That would probably return nil again.
be very careful of the "feature" of objective C, where calling a method of a nil object does nothing. Instead of crashing with null.pointer.exception like Java, it probably floats over [cell setSelectedBackgroundView:selectedBackgroundView] and a whole bunch of other lines without a problem.
I have the following code to create my cells in a UITableView.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"futureAppointments";
FutureAppointmentsViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (nil == cell) {
[tableView registerNib:[UINib nibWithNibName:#"FutureAppointmentsViewCell" bundle:nil] forCellReuseIdentifier:CellIdentifier];
cell = [[FutureAppointmentsViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
NSUInteger position = indexPath.row;
cell.appointmentDescription.text = [NSString stringWithFormat:#"%lu. %#. %#", (unsigned long)position, #"Steve", #"10/03/"];
return cell;
}
The problems is, the first cell of the tableView is missing. It should start with 0. Steve but instead starts with 1. Steve. Also there are only 4 elements in the list instead of 5.
When I place a break point in the code, the first cell is nil.
Does anyone know what might be happening?
Put this line of code:
[tableView registerNib:[UINib nibWithNibName:#"FutureAppointmentsViewCell"
bundle:nil]
forCellReuseIdentifier:CellIdentifier];
In viewDidLoad. It doesn't belong in cellForRowAtIndexPath. Once you do this then you no longer need to check whether the cell is nil.
Change cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"futureAppointments";
FutureAppointmentsViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSUInteger position = indexPath.row;
cell.appointmentDescription.text =
[NSString stringWithFormat:#"%lu. %#. %#",
(unsigned long)position, #"Steve", #"10/03/"];
return cell;
}
I think I had similar problem when I was using UITableView inside UIView and initialized it in init method.
I was not able to find good explanation for that behavior, but I've found tricky solution for that - I was reloading UITableView instance from UIViewController in viewDidAppear method.
I would also like to know, why UITableView is not drawing all UITableViewCell.
I'm adding subviews with UITableViewCell default type. In this, assume that, I've only 2 rows. At first, when it comes to tableView:cellForRowAtIndexPath: I'll generate a dynamic identifier for each cell based on indexPath.row. So it goes well and create a new cell inside if(cell == nil) condition. I'm adding subviews inside this condition only.
Next, out side that condition, I'm simply updating content of subview by fetching them out of the cell's contentView. This works great.
Problem is here : I'm expanding my cell (on which user tapped). When I do this, it'll go into tableView:cellForRowAtIndexPath: and I'm able to see the same identifier generated before. However, its going inside if(cell == nil) condition.
For a note, I'm checking for that condition like this,
if(!cell) and not if(cell == nil).
Can you guess what is the issue? However in my deep investigation, I found that, it'll only goes inside if(cell == nil) once I expand particular row and not next time for the same row.
Here's the sample :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//number of count
return [array count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
//dynamic height will be provided based on content.
return [self heightForRow:indexPath];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellIdentifier = [NSString stringWithFormat:#"cellIdentifier_%ld", (long)indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[self setupCell:cell forRow:[indexPath row] inTable:tableView];
}
[self updateCell:cell forRow:[indexPath row] inTable:tableView];
return cell;
}
- (void) setupCell:(UITableViewCell *)cell forRow:(NSInteger)row inTable:(UITableView *)tableView {
//setting up view here
}
- (void) updateCell:(UITableViewCell *)cell forRow:(NSInteger)row inTable:(UITableView *)tableView {
//updating view here
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.expandedIndexPath = ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) ? nil : indexPath;
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
[tableView scrollToRowAtIndexPath:self.expandedIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
Hope my issue is clear to you. Please comment if you don't get something or found anything missing here (and I'll update it here) or answer if you know what's the solution to this.
Thanks!
At the outset I would like to tell that I have researched and tried to follow stackoverflow links such as UISearchDisplayController and custom cell but still the problem persists
I have Search Bar and Search Display controller integrated into my UITableView. The search functionality works fine but the search results cell have the default white cell design and not my custom cell design which is used for the UITableView. Below is my code to make the Search Result's Cell design adapt my custom design.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.searchDisplayController.searchResultsTableView registerClass:[ItemCellTableViewCell class] forCellReuseIdentifier:#"Cell"];
if(!self.document){
[self initDocument];
}
self.filteredItemCatalogArray = [[NSMutableArray alloc]init];
self.itemCatalogTableView.dataSource = self;
self.itemCatalogTableView.delegate = self;
[self.itemCatalogTableView reloadData];
self.itemCatalogTableView.backgroundColor = [UIColor clearColor];
self.itemCatalogTableView.opaque = NO;
self.itemCatalogTableView.bounces = YES;
self.itemCatalogTableView.backgroundView = nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
if(tableView == self.searchDisplayController.searchResultsTableView)
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//cell fields customization
cell.backgroundColor = [UIColor clearColor];
cell.opaque = NO;
return cell;
}
else
{
ItemCellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//cell fields customization
cell.backgroundColor = [UIColor clearColor];
cell.opaque = NO;
return cell;
}
}
What am I missing here ?
EDITED :
In the if block for search results I changed tableview to self.tableview and it gets the correct custom cell. But it takes the incorrect height which is smaller and so overlaps the rows for search results
In the if block for search results I changed tableview to self.tableview and it get the correct custom cell. But it takes the incorrect height which is smaller and so overlaps the rows for search results
to rectify the height issue I added the following line in viewdidload
self.searchDisplayController.searchResultsTableView.rowHeight =
self.tableView.rowHeight ;
1) If your cell are using a xib file you should add to viewDidLoad (or other method that will be called before tableView delegate)
[yourTableView registerNib:[UINib nibWithtName:#"your_nibName" bunde:yourBunde] forCellReuseIdentifier:#"yourIdentifier"]
also you should use registred identifier for custom cell :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
static NSString *CustomCellIdentifier = #"yourIdentifier";
if(tableView == self.searchDisplayController.searchResultsTableView)
{
// UITableViewCell customization
return cell;
}
else
{
ItemCellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CustomCellIdentifier forIndexPath:indexPath];
if (!cell)
cell = [[ItemCellTableViewCell alloc] initWithStyle:yourPreferedStyle reuseIdentifier:CustomCellIdentifier] // or other custom initialization
//put cell fields customization here
return cell;
}
}
If your custom cell is designed using a NIB try this
[self.searchDisplayController.searchResultsTableView registerNib:[UINib nibWithNibName:#"YourCellNibName" bundle:Nil] forCellWithReuseIdentifier:#"Cell"];
You have to get a reference of your tableView and then in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomTableCell";
MyCell *cell = (MyCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
...
}
I am trying to call selectRowAtIndexPath on a UITableView that is a subview to the UIViewController I am calling it from.
I have set it up so that when you select a cell it goes grey and thats fine, however I am loading different data sets in and out of the UITableView and when ever a selection is made I am sending that selected NSIndexPath back to the UIViewController. Then when the view is next loaded with the correct data set for the NSIndexPath I call this method from my UIViewController.
if (codeIndexPath != nil) {
[filterViewController.tableView selectRowAtIndexPath:codeIndexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
}
Then in the UITableView class my cellForRowAtIndexPath and didSelectRowAtIndexPath look like this.
- (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];
}
// Configure the cell...
NSString *projectDescriptionString = [currentFilterMutableArray objectAtIndex:indexPath.row];
cell.textLabel.text = projectDescriptionString;
if (indexPath == currentlySelectedIndex) {
cell.highlighted = YES;
} else if (indexPath == currentlySelectedIndex) {
cell.highlighted = NO;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
// send this data back in the delegate so you can use it to show where the tick is again if you need too.
currentlySelectedIndex = indexPath;
[[self delegate] updateInstallTableWithFilter:[currentFilterMutableArray objectAtIndex:indexPath.row] FilterType:filterType InstallIndex:indexPath];
}
When It loads on the screen the correct cell will highlight for a second then go back to white.
Update
New if statment inside cellForRowAtIndexPath
if ([indexPath isEqual:currentlySelectedIndex]) {
cell.highlighted = YES;
} else if (![indexPath isEqual:currentlySelectedIndex]) {
cell.highlighted = NO;
}
I am still receiving the same error.
UITableViewController has a property called clearsSelectionOnViewWillAppear. From the doc:
When the table view is about to appear the first time it’s loaded, the
table-view controller reloads the table view’s data. It also clears
its selection (with or without animation, depending on the request)
every time the table view is displayed. The UITableViewController
class implements this in the superclass method viewWillAppear:. You
can disable this behavior by changing the value in the
clearsSelectionOnViewWillAppear property.
So in that table view controller subclass, in viewDidLoad...
- (void)viewDidLoad {
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
}