I understand that UITableView will call -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method to get each of the cells for the table view. Say I have my data source is fetched over the internet and I have to account for latency. What will be the best way of "stopping" this method from being called? Should it block on a boolean flag in the application? Should I just call cellForRowAtIndexPath again from within my application?
I am uncertain as to when the function gets called, ie, how often the UITableView "refreshes" itself. Any explanations will be helpful! Thanks!
If you don't have data, or you don't have the data for additional cells, then cellForRowAtIndex: will not be called as long as you don't tell the UTableView that you have rowCounts or new rowCounts. That value is being set in numberOfRowsInSection:.
In other words, don't report any new cells in numberOfRowsInSection:, until you actually have that data in hand, and then cellForRowAtIndexPath: won't be called prematurely.
When you do get the additional row data, then call reloadData to get the UITableView to ask for the number of rows and then call cellForRowAtIndex:.
If you've set UITableView datasource and delegate from IB then it will at least go for numberOfRowsInSection method when you push to the view, however if you're showing data from an NSArray, it'll return count ZERO if array is still empty, so table won't go for other methods to call.
In practice, I'm pulling data from web service to feed the table, so I am not setting up the datasource and delegate from IB instead once I get the data and status OK response I'd set the tableview.datasource = self and tableview.delegate = self and then call reloadData method to update table. This ensures that it won't go for numberOfRowsInSection method as you don't need to call it up without having the data.
What you should do is call reloadData on your table view when your data is done being fetched. That will force the table view to call it's delegate methods again for display. Those methods are called whenever the table view needs to re-display, so either when it comes into view, scrolling occurs, or you manually call reloadData.
When you invoke [tableView reloadData], the framework automatically invokes all the data source methods again for the table view cells that are visible currently (not for all the cells of the tableview). So the best approach would be to invoke reloadData every time you get data from the internet.
Related
The UITableView object I have in my storyboard theoretically should have its delegate set, but it does not. I dragged the UITableView object from storyboard into the header and added it as an IBOutlet property and synthesized it. However, I checked and only the data source method is being called. So something seems to be wrong with the way I'm implementing the main delegate protocol. As you can see in the images below, I seem to be doing everything standardly? But the delegate is not being set still! Thoughts?
I think the reason your solution is not working is because you are using a TableView inside of a UiViewController instead of a UiTableViewController. I had this same issue a while back. Here is what I did. Create an IBOutlet to the header file and synthesize it in the implementation file (I believe you have already completed this step). Go back to the storyboard. control + click on your table view and drag the connector to the view controller (the yellow circle with the white box). Select datasource. Repeat this step again and instead of selecting datasource select delegate. In the menu on the right-hand side you should be able to see your outlets if it is set up correctly. This should fix your delegate problems.
See the screenshot from my example below:
Try changing your numberOfRows method (for better debugging).
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int rows = [allParties count];
NSLog(#"number of rows: %d", rows);
return rows;
}
If you're returning 0, it won't work.
Also in the future, please paste your code instead of taking screen shots.
You have put a log in both numberOfRowsInSection: which is being called but not the one in cellForRowAtIndexPath: which shows that no cells are being created for your table. You simply are returning 0 as number of rows for the table. Check for that.
And the delegate method didSelectRowAtIndexPath: will be called when you select a row, but for that, you have to have a row first in your table.
I have been practicing with table views and I know how to create them but I would like to have a better understanding about delegate and source when creating table views.
Can someone explain the need for a delegate and a source when creating table views?
Why do you need them?
What is happening when you connect delegate and source to File’s Owner or ViewController and why they need to be connected?
I guess I need a general explanation about delegates and source and what happens when you connect them to File’s Owner or ViewController?
The delegate and data sources allow the tableview to conform to the MVC design pattern, which is a recurring design pattern in Cocoa and Cocoa Touch.
The TableView itself provides the [V]iew part and the delegate provides the [C]ontroller part while the data source provides the [M]odel part.
When you connect the delegate and datasource in the NIB file you are creating this connection visually; you can just as easily do it programmatically.
Delegate:-
A delegate is an object that acts on behalf of, or in coordination with, another object when that object encounters an event in a program. The delegating object is often a responder object—that is, an object inheriting from NSResponder in AppKit or UIResponder in UIKit—that is responding to a user event. The delegate is an object that is delegated control of the user interface for that event, or is at least asked to interpret the event in an application-specific manner.
Data Source:-
A data source is like a delegate except that, instead of being delegated control of the user interface, it is delegated control of data. A data source is an outlet held by NSView and UIView objects such as table views and outline views that require a source from which to populate their rows of visible data. The data source for a view is usually the same object that acts as its delegate, but it can be any object. As with the delegate, the data source must implement one or more methods of an informal protocol to supply the view with the data it needs and, in more advanced implementations, to handle data that users directly edit in such views.
For Detail info goto
http://developer.apple.com/library/ios/#documentation/general/conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html
You dont need to make any connections if you are happy to write the following code:
tableview.delegate=self;
tableview.dataSource=self;
The UITableViewDataSource protocol is adopted by an object that mediates the application’s data model for a UITableView object. The data source provides the table-view object with the information it needs to construct and modify a table view.
Example:
Whereas a data source type object gives data to another object. For example again, the UITableViewDataSource protocol has methods such as cellForRowAtIndexPath and numberOfRowsInSection dictating what should be displayed in the table
The UITableViewDelegate of a UITableView object must adopt the UITableViewDelegate protocol. Optional methods of the protocol allow the delegate to manage selections, configure section headings and footers, help to delete and reorder cells, and perform other actions.
Example :
A delegate type object responds to actions that another object takes. For example, the UITableViewDelegate protocol has methods such as didSelectRowAtIndexPath for performing actions upon a user selecting a particular row in a table.
If your programming language doesn't support multiple inheritance, you must use delegate method. When you implement delegate method, you can use object functions such as super class. Example :
// define tableview row count
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
// define tableview height
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
}
// define specific tableview cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = MyCell();
return cell;
}
So I implement UITableViewDataSource protocol using an NSFetchedResultsController.
I then modify the contents of the Core Data base and the NSFetchedResultsController then update the tableView..
Is there anyway to know when the tableView has done reloading the data?
We have a complex data model that is caching against a REST implementation, and it's difficult to determine if we need to get more data to fill up the screen (because the screen might be using complex filters against the raw data loaded). Also, the UITableViewCell objects are NOT guaranteed to be the same height.
The easy answer was to simply dump the data to core data, and use an NSFetchedResultsController to serve the data to the UITableView.
Here is how it works:
We added a tableview.tableFooterView that displays our "data loading" message.
Loading up a batch of data from the REST API and then using it update our Core Data objects.
This triggers the controllerDidChangeContent method to trigger, which then triggers a tableView reloadData (eg:)
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView reloadData];
}
We then basically then "Check" to see if the Footer is still visible 250ms later by calling a method like:
[self performSelector:#selector(checkIfFooterViewIsVisible) withObject:nil afterDelay:0.25];
- (void)checkIfFooterViewIsVisible
{
BOOL viewVisible = CGRectIntersectsRect(self.tableView.bounds,self.tableView.tableFooterView.frame);
if (viewVisible)
{
[self getMoreData];
}
We also check to see if the Footer is in view everytime we scroll.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[self checkIfFooterViewIsVisible];
}
If we load the "last batch" of data, we actually just remove the tableViewFooter view object from the table (so it can't ever be visible).
So the answer is cool, because we can figure out if we need to pull more data, only if the user "needs" it. Either because there wasn't much visible data in the first batch, or because they have scrolled down and want more data. Checking the current placement of the tableFooterView lets us know if we have "painted" enough data on the screen.
The PROBLEM is - can I get rid of the:
[self performSelector:#selector(checkIfFooterViewIsVisible) withObject:nil afterDelay:0.25];
If we set the delay too fast, then UITableView doesn't have time to update the screen (and change the footer's position). If we update too slowly, then it feels like the app "stutters" as it loads data, and can load data slower than it could. But different iOS devices (and different network coverage) are going to mean slightly different timings, so the "let the UITableView update and check a bit later" works most of the time, but I feel like this could work smoother.
Is there anyway (maybe by overloading UITableView) that we can determine that the UITableView is "done" loading UITableViewCell objects? (at least until the next time the scroll moves?). Adding in this weird delay works well, but it would work cleaner if we knew definitively that the [tableview reloadDate] operation has completed.
I have read several articles about UITableView, including the official doc and some on SO. But my situation seems to be different.
I want to update the Table each time the view loaded. And I must fetch the data using HTTP request.
What I got now is:
When enter the table view, I should use a non-synchronous HTTP request to update the data. Because I don't want the main thread to wait. One place to do that, is in the tableView:cellForRowAtIndexPath: method. So I return 0 for no data exist at the beginning.
When I get the HTTP respond, I update rows on main thread using beginUpdates endUpdates insertRowsAtIndexPaths:withRowAnimation:
And I must update the "Data Source" at the same time, but how to do that?
Or should I make a daemon thread and update my data every once in a while? So that the data will be ready when TableView is loaded.
You would do it like this:
Have a boolean or some variable where you can reliably detect whether you have all the data.
In viewWillAppear, reset everything. Start loading your data.
If you don't have the data yet, you only display one section with one cell (a placeholder cell that reads "Loading..." and shows a spinner, for instance).
Once the data is completely loaded, you set the bool or whatever.
Call [self.tableView reloadData];
In all of your UITableViewDataSource methods you would need to check whether you've got the data already or not. If not, you return the placeholder data.
[yourtablename reloadData]; will help you relaod the data in the tableview, You can call this once you get the response from your server
I'm not sure there's a "best method" for what you're trying to accomplish here. I would suggest trying the method you have, and seeing if it provides an adequate user experience (whatever that means to you) and if it doesn't, try something else. I would definitely suggest having some sort of "loading" indicator while the table is empty and waiting for http response.
In terms of your question about the "data source", the data source of a UITableView is simply an object that implements the UITableViewDataSource protocol which you can read about here. Often times, you will have XCode set up a UITableViewController object which will act as both delegate and data source to your table view. How you actually store your data is up to you. The data source protocol simply provides the methods by which a table view will "ask" for the data it needs to load.
I have a tab bar application which loads a UITableView when one of it's buttons are selected. It seems to load the view controller however it doesn't seem to be populating the data. I have tried setting the cell.text = #"cell" (while setting the number of rows > 0) and an NSLog in the CellForRowAtIndexPath proves that in fact the function is not being called. The same NSLog output to the console in the viewDidLoad function also generates no output so it seems as though its not getting called.
Any suggestions?
Try and call [tableView reloadData] from your -(void)viewWillAppear:(BOOL)animated; method. This should be called each time the user navigates to that tab and should set off the delegate callbacks that aren't currently being called. If they still aren't called make sure you have set the table delegate to self.
EDIT: Actually make sure you have set your table view's delegate and datasource to self;