UITableView in UIViewController reloadData not updating table view - uitableview

I have my MainViewController which a UIViewController class, in this view I have a table view and another view embedded in it. As you can see I added the table view delegates to the view controller.
#interface MainViewController : UIViewController <FlipsideViewControllerDelegate, UITableViewDataSource, UITableViewDelegate> {
Now to my question which has been asked about 50 times from multiple searches on the internet but still I have not been able to find a solution.
Why doesn't reloadData work...?
I am downloading info from an XML feed and I refresh the feed from the app delegate:
- (void)applicationDidBecomeActive:(UIApplication *)application
I have inserted multiple NSLogs, this is what I have found. The refreshed data from the XML feed is coming in no problem. But the table view cells are not being refreshed.
I placed a NSLog in:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
Although the data is being refreshed, when I call [tableView reloadData]; the cellForRowAtIndexPath is never called.
I am hoping someone can help me with this issue, I have been working on this issue for about 12 hours with no luck. I have exhausted the search button on multiple sites....
FYI:
-Table View is wired in Interface Builder.
-My tables are being loaded one by one via TableViewCells, following Apples example starting on page 51 of the Table View Programming guide.
Additional Code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 17;
}
Thanks for the help

Do you have any example Code ?
My first ideas:
Datasource not connected
Rows in Section give 0 back
Sections give 0 back

Everything is working now... That was a lot of time wasted on my part...
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[_mainViewController refresh];
[_mainViewController.tableView reloadData];
}
- (void)refresh {
Parser *xmlParser = [[Parser alloc] init];
[xmlParser parseRssFeed:#"http://www.somesite.com/file.xml" withDelegate:self];
[xmlParser release];
[self.tableView reloadData];
}

Related

iOS - cellForRowAtIndexPath not called on reloadData

I know that this question have been asked so many times but unfortunately non of the solutions in those questions worked for me.
Here are some of the things I tried:
making sure that numberOfSectionsInTableView is not returning 0
making sure that numberOfRowsInSection is not returning 0.
insuring that reloadData is being called on the main thread by using performSelectorOnMainThread as shown below:
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO]
setting the delegate in viewDidLoad, as shown below:
(void)viewDidLoad {
[super viewDidLoad];
// .....
self.tableView.dataSource = self;
self.tableView.delegate = self;
}
I'm creating an iOS app that keeps track of events. The app's main view is a UITableViewController (Calling it main list) that is embedded into a UINavigationController which is embedded into a UITabViewController. The user can create two objects a list or an event. When a new list is created and selected from the current UITableViewController, a new UITableViewController is pushed into the same UINavigationController.
Each list (UITableViewController) has an edit button that allows the user to delete the list.
Assuming that the user chooses to delete "list 3" that belongs to the main list, that is there are 3 view controllers pushed to the UINavigationController:
UITableViewController for the main list
UITableViewController for the selected list "list 3"
UITableViewController for the edit list view
when the delete button is tapped on, an alert is shown. If the user chooses to proceed the following code gets executed:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
// .....
//here I'm calling unwind which is going to pop the UITableViewController
//for the edit table view
[self performSegueWithIdentifier:#"unwindFromDelete" sender:nil];
//here I'm calling a method i wrote to pop the UITableViewController
//of the deleted list (in this example "list 3")
[self setNavigationControllerViewControllers];
// .....
}
- (IBAction)unwindFromEditListToList:(UIStoryboardSegue *)segue {
.....
}
- (void)setNavigationControllerViewControllers {
NSMutableArray *viewControllers = [[self.navigationController viewControllers] mutableCopy];
[viewControllers removeLastObject];
[self.navigationController setViewControllers:viewControllers];
}
After that code is executed, viewWillAppear for the main list UITableViewController is executed as shown below:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// ..... update the data
[self.tableView reloadData];
}
[self.tableView reloadData] is going to cause the following code to be executed:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// .....
return someValue; // Something greater than zero
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
// .....
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// .....
return someValue; //Something greater than zero
}
#pragma warning "Not getting called"
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// .....
}
All the methods are executed but cellForRowAtIndexPath is not. The problem is gone if setNavigationControllerViewControllers mentioned above is not called. Not calling setNavigationControllerViewControllers will result in UITableViewController for "list 3" to show which is bad since "list 3" is no longer stored in the core data I'm using to store the data.
If you have any idea what to do, please let me know.
Thanks
numberOfSectionsInTableView must return at least 1.
See this link below.
You don't even need to override it if you're not using multiple sections.
If the height of table is dynamic. Make sure the table is in a view, visible and height is
atleast 1px when reload is called.
I put an answer on this because I've been browsing for a solution for sometime and this question is the closest to the one I was asking myself...
It turns out that I was using auto-layout with visual format.
Seems that UITableView and NSLayoutContraint don't play well together. As soon as I commented the method which apply constraints, the cellForRowAtIndexPath was called and the tableview displayed...
Please put debug point on this,
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
method and log someValue value may it return 0 that's why cellForRowAtIndexPath method not called.
Thanks.
Don't know who down voted Avalerion answer, but his statement is important too.
I had same problem, delegate method cellForRowAtIndexPath was not called though the following conditions were matched:
Delegate and Datasource was set
numberOfSectionsInTableView method was returning 1
numberOfRowsInSection returned correct number and data was available.
Reason:
I didn't set height constraint for the table, so it had no height, thus app didn't call cellForRowAtIndexPath even though it called eg numberOfRowsInSection.

UITableview doesn't call cellForRowAtIndexPath

The first time the view controller is pushed (from the previous view controller) all the delegate methods are called (inside a navigation controller).
When pressing back to return to the previous view controller , and then pushing it again (for the second time)
cellForRowAtIndexPath isn't called but numberOfRowsInSection and numberOfSectionsInTableView are called.
The reloadData is called within
-(void)viewWillAppear:(BOOL)animated
{
[self.tableView reloadData];
}
and I have tried in
-(void)viewDidAppear:(BOOL)animated
{
[self.tableView reloadData];
}
and it doesn't help.
Edit
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1; // called
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 3; // called
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// relevant code for cell - THIS METHOD IS NOT CALLED SECOND TIME
}
If you don't find any valid reason as explained above I will highlight
another mistake one can make is (as I once did).
The steps to note are
1) initialise your custom table view as first step
2) set the delegate and datasource before you add the tableView to you view.
If one has done these steps mistakenly after adding to the tableview to your self.view object, then it would be a silent way to make yourself struggle.Correct order is as under:
self.myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, myViewwidth, 120) style:UITableViewStylePlain];
self.myTableView.tag = MY_TABLEVIEW_TAG;
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
[self.view addSubview:self.myTableView];
This always happens when you define your underlying data source (array or Dictionary) as weak, the first time it gets pushed, the data is there, when deallocated, it will release and you lose control over it.
Double check the weak/strong condition and optionally set the data source before pushing again.
Please check if you have set/connected Delegate and Datasource to the File's Owner.
And check the array count of your model, if it contains value of not?
NSLog in the numberOfRowsInSection method and check it by using breakpoints and step over.

Best approach: Displaying a "nothing here, yet" screen instead of empty tableView cells

When opening a UITableViewController, I'd like to display a screen saying "nothing here, yet" (c.f. screenshot of the Dropbox App below) instead of empty rows when there are no sections/rows. This is done in multiple Apps already, but I'd like to know if there is a recommended way of achieving this.
I can imagine multiple ways of doing this, including
adding a UIView as a subView to the view of the tableViewController
switching to a UIViewController and implementing the tableView behavior on my own
setting the UIView as the backgroundView and hiding the tableView cells
Thank you for all your ideas!
The way I usually do it, is displaying a single custom cell when there's nothing to display.
There is a new (as of June 2014) library called DZNEmptyDataSet handling exactly this case. It uses the first approach (UIView as a subview) described in the question. Pretty neat realisation.
You must be having some data array through which you can define few things, in table view.. You can achieve this in your tableview only with its delegate methods..
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if([dataArr count] == 0)
return 1;
return [dataArr count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
if([dataArr count]==0){
return self.view.frame.size.height;
}
return 44.0;
}
and then
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
if([dataArr count]==0){
//Create a cell with the no data image
tableView.scrollEnabled=NO;
}else{
//Create your default cell with data..
tableView.scrollEnabled=YES;
}
return nil;
}
I am just giving you an idea, its your choice of using it..

ios6 - numberOfRowsInSection doesn't run for custom tableviewcell, delegate/datasource set

I have a custom view EMViewController with a tableview as a subview. I want EMViewController to double as the controller for the subview, so I have it set as the delegate and dataSource.
// EMViewController.h
#interface EMViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
...
// EMViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.eTableView.delegate = self;
self.eTableView.dataSource = self;
}
I'm populating the data with an asynchronous array/dictionary from Facebook, so once I have the data, I run
[self.eTableView reloadData]
For the UITableViewDataSource protocol methods, I implemented the following:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSLog(#"returns sections 0"); // This appears in my log
return 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"returning count %d", [self.eList count]); // never shows up
return [self.eList count];
}
- (EMTableViewCell *)tableView:(EMTableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// configure the cell
NSLog(#"index: %d", indexPath); // never shows up
...
}
So far, I have already tried:
making sure my storyboard is connected up
checking/rechecking the delegate/dataSource being set in viewDidLoad
completely restarting/clean-building my app just in case
creating a placeholder UIView subclass (although I'm not changing much)
creating a customUITableViewCell class with outlets for my custom layout (images, 3 labels)
testing out strong/weak references to the tableView outlet
Any advice would be appreciated. I think my problem is similar to this question, except I'm trying to add the tableview as a subview. My end goal is to populate the cells with a custom format that includes an image and three labels. Thanks!
Return 1 section instead of returning 0 sections
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}

Uitableview cell changes value when scrolling

The code below is creating a search for many strings. Initially there are 5 rows, when you reach row five, it adds another row. Instead of just directly editing the row, i load a filter controller (another view controller that as you type it completes words for you). When the user finishes finding a word he clicks it and comes back to this view controller. Now i want to fill the cell that was originally tapped with the text from the filter.
I tried asking earlier and didn't get any concrete answers.
I am running into a problem where when i scroll (after adding a new row), it starts filling in those rows with info already in the table, (as opposed to staying blank)
Please help me where i am going wrong
//global indexpath to remember which cell tapped
NSIndexPath *globalPath;
#interface SearchViewController ()
#end
#implementation SearchViewController
//Load implementation once per launch
- (void)viewDidLoad
{
[super viewDidLoad];
[self linkInputTableToDelegate];
_temporaryResultsArray =[[NSMutableArray alloc]init];
_flurryArray=[[NSMutableArray alloc]init];
_numberOfSections=6;
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:NO];
[InputTable reloadData];
textFromUserDefaults=[[[HelperMethods alloc]init]getObjectUserDefault:#"textFiltered"];
[self addTextToFlurryArrayForFlurryAndSavedLists:_textFromUserDefaults];
}
-(void)viewDidDisappear:(BOOL)animated{
}
- (IBAction)searchButtonPressed:(UIButton *)sender {
self.tabBarController.selectedIndex = 1;
}
//Makes the input table respond to delegate table view methods
-(void)linkInputTableToDelegate{
_inputTable.dataSource=self;
_inputTable.delegate=self;
}
-(void)performSearch:(NSString*)text{
//do search
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
int numberOfRows=_numberOfSections;
//Rows for iPhone 4
if ([[UIScreen mainScreen]bounds].size.height==480) {
numberOfRows=numberOfRows;
//Rows for iPhone 5
}else if ([[UIScreen mainScreen]bounds].size.height==568){
numberOfRows=numberOfRows+1;
}
return numberOfRows;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//In reality groups are created with 1 row inside, this is to allow spacing between the rows
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *kCellID = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellID];
}
//Is the cell the same as the one clicked when going to ingredient filter
BOOL cellIndexPathSameAsSelected=[self isCellIndexSameAsPreviousClicked:indexPath];
cell.textLabel.textColor=[UIColor blackColor];
if (cellIndexPathSameAsSelected && _textFromUserDefaults!=nil) {
if (![cell.textLabel.text isEqualToString:_textFromUserDefaults]) {
cell.textLabel.text=_textFromUserDefaults;
[self performTextSearch:_textFromUserDefaults];
}
}
return cell;
}
//Compares the previous clicked cell with the cell now selected
-(BOOL)isCellIndexSameAsPreviousClicked: (NSIndexPath*)cellPath{
if (cellPath.row == globalPath.row && globalPath.section==cellPath.section) {
return YES;
}
else{
return NO;
}
}
- (void)updateTableViewWithExtraRow :(NSIndexPath*)rowSelected{
NSLog(#"number of sections =%i",_numberOfSections);
if (rowSelected.section == _numberOfSections) {
_numberOfSections ++;
}
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellText = [tableView cellForRowAtIndexPath:indexPath].textLabel.text;
[[[HelperMethods alloc]init]saveObjectToUserDefaults:cellText :#"textFiltered"];
globalPath = indexPath;
[self updateTableViewWithExtraRow:indexPath];
}
-(void)addTextToFlurryArrayForFlurryAndSavedLists:(NSString*)text{
if ([_flurryArray count]==0 &&[text length]>0) {
[_flurryArray addObject:text];
}
for (int i=0;i<[_flurryArray count];i++) {
NSString *textInArray=[_flurryArray objectAtIndex:i];
if (![textInArray isEqualToString:text]) {
[_flurryArray addObject:text];
}
}
NSLog(#"Total number of saved items = %i",[_flurryArray count]);
}
// Dispose of any resources that can be recreated.
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
I have a couple of reactions looking at the code:
A couple of observations about the proper use of the UITableViewDataSource methods, specifically numberOfRowsInSection, numberOfSectionsInTableView, and cellForRowAtIndexPath:
These really should be driven by some model data structure (e.g. a NSMutableArray) and nothing else;
These methods should be stateless. They should not relying on the value of some NSString instance variable, like _textFromUserDefaults) but rather always look up the value in the NSMutableArray model structure on the basis of the value of the indexPath parameter. You simply cannot make any assumptions about when cellForRowAtIndexPath will be called. This may well account for your duplicate values.
None of these should be doing anything besides responding to the UITableView inquiry. For example, your cellForRowAtIndexPath is invoking performTextSearch. It really shouldn't do anything except return the cell.
Your cellForRowAtIndexPath currently has conditional logic and only updates the cell if certain conditions holds. Because cells are reused, you really want to make sure that you initialize the cells regardless. You can't be assured that the cell is blank when you get it, nor that the previous contents are the previous values for that indexPath. Because cells are reused, it could be for an entirely different row. This could also account for your duplicative entries.
Regarding the interaction of the master view controller and the details view controller, there are more elegant ways than passing data back and forth via NSUserDefaults. For example when you initiate the details view controller, you could just pass it the information it needs. And when it's done, it should call a method in the master view controller to update the data in the master view. To do that, the master view controller should conform to some protocol of your own creation. If you see the example I shared via chat, you can see what that might look like. Anyway, by having some delegate method in the master view controller that the detail view controller calls when it's done, that eliminates the rather fragile technique of using viewDidAppear to control the updating of the master table view.
You might want to contemplate employing "edit" (which allows you to delete, possibly also edit a particular row) and "add" buttons like the standard "master-detail" template that Xcode provides. There are a number of standard conventions here that might be better than having an array of blank cells that you can then tap on. Clearly, your user experience is entirely up to you, but you can always contemplate whether there are existing, familiar conventions that you might employ.
Rob's feedback is good. In broader terms, you can't rely on the cells in a UITableView to hold onto their data. For efficiency, it will be creating, using, and destroying cells at will, and using cellForRowAtIndexPath to figure out what they should look like. Instead of testing what's in a cell, you need to have your own set of data which describe the value of each cell, and just set the value based on the indexPath. I'd recommend storing all your cell information in an NSMutableArray which contains NSStrings or something more complicated if necessary. It will be easy to set default values when you add cells to the array. Then cellForRowAtIndexPath can just access the array rather than attempting its own logic based on current cells.

Resources