I am currently writing an app, using storyboards, that contains two UITableViews within the same window.
When running the application, the first UITableView will populate with a number of "Registration Numbers".
When the user taps on a cell, then the second UITableView is supposed to populate with detail about the selected cell.
When tapping a number in the first table I have a method that drives the:
[mySecondTableView reloadData]
Which it does. I am under the impression that when invoking the reloadData command, it should then call both:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
and
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
The first fires, but the second won't.
I have both the data source and delegate wired to self.
I am not sure what I am missing.
Are both methods meant to fire?
When things like this happen to me, it's usually because I'm calling -reloadData outside of the main thread.
After reloadData, try this:
NSIndexSet * sections = [NSIndexSet indexSetWithIndex:0];
[self.tableView reloadSections:sections withRowAnimation:UITableViewRowAnimationNone];
I found if the data change is subtle, the table view seems to optimize away the need to refresh.
Pointed answers doesn't solve my table view problem. It was still calling
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
however cell drawing method wasn't fired:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
My problem was that(because of my mistake of course) table view height was 0. So as table displays only cells that are visible, it never tries to display a cell. When I changed height value drawing cell method started to fire again.
Botom line double check your table frame and ensure you have non-zero width and height values!
I just incurred this same issue. I used a performSelectorOnMainThread to call a method which then calls the reloadData. This was necessary since I was trying to update outside the main thread as suggested by hatfinch.
Make sure you call performSelectorOnMainThread with waitUntilDone:NO, otherwise you may still have the same issue
Found another cause of cellForRowAtIndexPath not being called.
If numberOfRowsInSection returns 0, cellForRowAtIndexPath won't be called.
This happened in my case because of an NSArray being de-allocated during a view controller change and its consequent count (since it was nil) returning 0.
After banging my head against a wall for days this solved my problem:
Set the All Exceptions breakpoint and see if you are getting an out of bounds exception in your datasource. Without setting the breakpoint there is no crash and the tableview will simply fail to reload.
Full answer here
I was debugging a similar issue for hours before I realized my .reloadData() call was being executed by a callback of a previous instance of the view controller.
So my breakpoint was hitting on the .reloadData() call of my old instance of that view controller, but the new instance that was actually shown wasn't reloading because it wasn't the one executing the callback that called .reloadData().
Related
I understand the concepts of cell re-usability for Xcode 5.0 table views. However, I have one very weird observation which I don't understand and wish anyone of you could enlighten me here. Thanks.
I have implemented a table view with a search bar utility (just on top of the table view). Under each custom cell (prototype cell), whenever a user clicks on it, it will be marked with a checkmark (UITableViewCellAccessoryCheckmark). The number of cells are more than 10.
Observation:
- Without using any search, marking and unmarking a cell is working as intended. Cells are updated instantly along with their checkmarks.
- When doing a search, from the results given, marking and unmarking a cell is also working as intended.
[Problem] Here comes the weird issue: when cancelling a search, an already marked cell (marked during search) does not refresh itself in the tableview unless scrolling up or down is performed!
And hence, I wrote [tableview reload] at the end of tableview:didSelectRowAtIndexPath: method. Obviously, it doesn't refresh the tableview for me. Without further changing any other code, merely modifying [tableview reload] to [self.tableview reload] under the same method works!
Why is the only addition of "self." able to make the table cells refreshed instantly? I have always thought the first argument, tableView, from the method (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath is as equal to self.tableview. Obviously, my interpretation in this case is wrong.
Thank you. I'm sorry for my lengthy post.
My guess is that this UISearchBar comes from a UISearchDisplayController. Is that correct?
If true, that is a common misconception, but an easy one to understand.
When filtering your UITableView entries and showing results, UISearchDisplayController actually overlays the view with its own tableView, UISearchResultsTableView.
Thus, this overlaid tableView also gets to call data source and delegate methods on your implementation, and this is when the tableView argument from tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath stops being equivalent to self.tableView.
This means that calling [tableView reloadData] during filtering actually asks UISearchResultsTableView to reload its contents, not self.tableView, a property of your viewController.
I'm trying to implement infinite scroll like the Facebook app has.
It appears that Facebook is somehow inserting rows or reloading the table view without stopping the current scroll.
Does anyone know how they're achieving this?
When I call insert rows or reload data during a scroll the scroll is stopped dead. Any help would be appreciated.
Not much code to provide just typical UITableView functions.
[self.tableView insertRowsAtIndexPaths:self.paths withRowAnimation:UITableViewRowAnimationNone];
or
[self.tableView reloadData];
self.paths being the paths recently added via the next page request.
I've also added the following method
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath;
Which is working correctly now with reloadData being called. Unfortunately even while this speeds up the addition of cells it still locks the current scroll.
Okay so, as far as the stopping of the currently active scroll is concerned the problem came down to a single line of code. And the solution was just as small.
My TableView has a refresh control on it. In the method in which I receive the next set of data for the TableView I would call the following.
[refreshControl endRefreshing];
My assumption was if it wasn't refreshing that endRefreshing would simply do nothing.
Well... Apparently that's not the case. When you call endRefreshing on a refresh control that isn't refreshing it'll stop the tableview scroll. My fix is as follows.
if(refreshControl.isRefreshing){
[refreshControl endRefreshing];
}
Now the scroll continues after the next load albeit with some delay. That delay of course is easier to debug.
That's just the about of overhead involved in calculating the height of the cells being added which is called on all cells when reload is called unless you have
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath;
So now that I have everything sorted out it's time to create an efficient way to calculate a reasonably close estimate height.
I have a UITableView that I modify (delete one row and insert one row) when the user clicks on any of the rows. Memory deallocation works great in iOS6 and earlier, but iOS7 will not release the tableview. Here is the basics of my code. I have reduced the code hugely, but the issue is still there.
- (void)tableView:(UITableView *)intableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[intableView beginUpdates];
[intableView endUpdates];
}
The begin/end updates cause the memory to get held somewhere. It's the same if I insert or delete rows (along with the background data array) inside this function. I subclassed the UITableView and cells with just adding:
-(void)dealloc
{
NSLog(#"deallocated %#",[self class]);
}
So that I can see when they get deallocated. Are we not supposed to update the tableview on selection anymore? Is it in a different thread where something gets held? (I have tried dispatch_async into different threads)
I could post more code, but my current code looks just like what I posted and it is still holding on to the tableview somehow and not deallocating it.
When is cellForRowAtIndexPath called when the cells of a TableViewController are added dynamicall? Is it after ViewDidLoad? We never trigger anything programatically to tell the app that we have done all the initial work we need to do and now you can start adding the cells with the corresponding details, discousure etc...
I am terribly confused and therefore I have no clue about the flow of execution of controls in a TableViewController. Please help me !
The delegate calls for the tableView will only be called if the table exists, for the table to exist the view must exist as it is part of the view. The viewDidLoad is called right after creating the view. So in short: viewDidLoad is called BEFORE tableView:cellForRowAtIndexPath:.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath is a delegate method of a UITableView. It is called every time a cell is becoming visible(appearing on the screen due to scrolling for example).
And at the beginning it is called after viewDidLoad as many times as necessary(depending how many cells should be present).
Hope it helps
it is called after viewDidLoad, and yes it is called for every cell dynamically. you can put breakpoints and check the flow. hope it helps. happy coding :)
The tableview knows that when it's unarchived it needs to load its data.
As a general tip, if you want to know when a method is executed you can set a breakpoint. Xcode lets you choose whether to pause or continue when a breakpoint is met, so you can trigger a set of actions when different methods are encountered to find out what order thet occur in.
I've read every post here on SO regarding UITableView and reloadData, and nothing I've found has worked for me.
In the UI builder I have a UIViewController xib file with a UIView in it, and on that UIView, a UITableView. My UIViewController subclass is references as the File Owner for the xib. My subclass is a UITableViewDataSource and UITableViewDelegate, and I've wired up the dataSource and delegate referencing outlets to point to the UITableView in the UI builder. I've linked the UITableView in builder to the IBOutlet defined in my subclass.
Initially when the UITableView is displayed, the underlying NSMutableArray that I'm using as the data source has no data it, so the UITableView is empty as expected.
Later on, I modify the array, adding data to it.
I then have tried the following to refresh the view of the UITableView, all to no avail:
Called [myTableView reloadData] after changing my array
Called [myTableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
Called insertRowsAtIndexPaths on the myTableView, thinking I might need to actually tell it which row to update.
None of these work. However, the data is there and everything seems to be wired up OK, as whenever I drag the UITableView up and release, so that the first row has temporarily disappeared, suddenly it does display the expected content when it comes back into view.
Any ideas?
I am sure that you right to call [tableView reloadData]
But you may want to set the [array count] in this method
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
And within
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.textLabel.text = [array objectAtIndex:indexPath];}
This should allow you show the data from the array.
Although I had everything wired up correctly, I was chasing my tail. Ultimately the problem was not with the UITable at all, and not with the underlying array being used as the basis for my UITableView, but with the dictionary I was using to generate the text for each cell!
The array was being updated correctly, then reloadData was being called. But the dictionary wasn't being loaded with the necessary data until AFTER reloadData was being called. So the UITable was being "populated" with the right number of rows, but they all appeared blank, making it seem like things weren't working at all.
Once I took the trailer trash method of NSLogging some debugging data for each call to my UITableView delegates, it became obvious that things were being called as expected, but the data was not being pulled correctly for each cell.
Whew!