I've got a UITableView set up and working, and now I have a little issue. I need to reload the tableView's data whenever the view loads, and one thing which I can't get working is attempting to reload the data, when there was no data in the table view to begin with.
Basically the list is of variable length, and the contents are loaded from a file, what I want to know is, is there I way I can force the table to reload and not have it ignore the datasource methods, as is what happens whenever [tableView reloadData] is called.
[tableView reloadData] relies entirely on the data source methods! So, the only way you would see your data source methods beind ignored would be that you have not set the data source (and perhaps delegate) of your table view to be the object you want to be the data source. You can set these through Interface Builder, or programatically, e.g.:
tableView.dataSource = self;
tableView.delegate = self;
whenever the view loads
Where do you call reloadData? Do you call it in -viewDidLoad method? If yes it is probably incorrect, because -viewDidLoad is called only once - when view of the corresponding UIViewController is created (on the first usage). Maybe you should take a look at -viewWillAppear which is called(assuming correct usage of UIViewController) whenever view is going to be displayed.
The other possible reason is that if you do use -viewWillAppear (or -viewDidAppear) it is not triggered at all. This can happen if you use a custom UIViewController hierarchy. In that case you must call it with your own hands (there are exceptions - UINavigationController for example does this for you, but simple [someView addSubview:myController.view] doesn't).
Also please check if delegates are set correctly and tableView is not equal to nil (as you know messages to nil are just ignored).
datasource methods aren't ignored when u call [tableView reloadData];
Did u set the IBOUTLET and the sources right?
Related
I declare an array at the beginning of my UITableViewController:
class ArchiveTableViewController: UITableViewController {
var dataSource: [[Book]]!
(…)
And then I have a function getDataSource() that updates the array from a database.
My first thought was to call it at viewWillAppear, but it seems that the table view loads before that, so it ends up not matching the array.
I could call the function from every single table view method, but that seems a little stupid. So where is the best place to do it? It must get called every time the view appears, so viewDidLoad won't work, but it must get called before the tableview methods, so viewWillAppear won't work either. It's like I need something in between. Or is there a better way to do what I want?
Thanks in advance,
Daniel
Edit: I should have added, that dataSource array is made up of other arrays, each representing a section in the table view. I get the number of sections and the number of rows in a section from the array too, so it must stay the same throughout the tableview methods or the app will crash, it might try to populate a row that shouldn't exist anymore, for instance.
Edit 4: Ah, ok, I got it! I'm sorry for wasting everyone's time looking for complicated answers. I just had to call getDataSource() from both viewDidLoad and viewWillAppear, and call tableView.reloadData() only in viewWillAppear.
I was trying all combinations of where to put things and with reloadData inside getDataSource() it would sometimes get called repeatedly forever until it crashed… or on other attempts I would call getDataSource() from viewWillAppear and not from viewDidLoad, and then it would crash the first time… anyway, I missed the most obvious combination and now I can't understand how I didn't see it before. Thank you all so much for your time.
Set it in override func loadView().
EDIT: As a general practice this is how you should go it:
Show loading overlay on screen while table data is being fetched.
Once data is available:
2.1. remove the loading overlay.
2.2. update data source model.
2.3. reload your table view so latest model could be picked.
Are you are showing other view controller and then going back to the one with tableView and that's why you want to update the tableView every time the view appears?
If so - it should just work the way you initially tried, if you put the getDataSource in viewWillAppear() and it calls tableView.reloadData() in the end (as you have it here in the source sample) then it will effectively show just what you have prepared for it (all the tableview methods will be called again after reloadData() even if they were already called before).
So I would advise debugging why it is not working when you call it from viewWillAppear(). Please add some screenshots or other data of how it is not correct with this method.
As a sidenote, if getDataSource() takes significant time, it may delay showing your view controller if you put it in viewWillAppear. In such case you'll need to do the way #Abhinav laid out for you (but you can start it from viewWillAppear).
Update: You're right, depending on how you've written numberOfSectionsInTableView() and tableView(_, numberOfRowsInSection) you may need to call getDataSource() also from viewDidLoad() or else you may crash because you're not handling empty datasets.
In that case you don't need tableView.reloadData() because table view methods will be called anyway, so as you noted, it's good to separate it from your getDataSource().
Regarding getDataSource() called repeatedly until crash - it probably happened when you added getDataSource() in one of UITableViewDataSource methods (like numberOfSectionsInTableView()) because reloadData() inside getDataSource() would trigger this method again.
Every function needs to access your var dataSource. So that's where the call to getDataSource must go: You create an optional variable that contains either dataSource or is nil, and then you give dataSource a getter which returns that optional variable unwrapped if not nil, and calls getDataSource otherwise.
tableView:numberOfRowsInSection is sent to the delegate of a UITableView to find out how many rows it needs to have in a given section.
My question is, when and how often is this method called?
The method is called very first time the tableview is getting loaded and if you are more interested in the delegates then put a breakpoint and check when and where which delegate is called and how many times.
Below are the instances when that function will get called,
For the first time when table is loaded
the time you reload the table data
the time you add/update/delete your row or sections dynamically.
The method - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section is a protocol method of the UITableViewDataSource - protocol. It will be called
the very first time your table view is loaded based on that you have set the dataSource properly, e.g.
self.yourTableView.dataSource = self;
If you are interested in updating your table again at a later time you can call
[self.yourTableView reloadData];
in order to reload the entire table. If you are only interested in reloading a part of your table you can do something similar to
NSIndexSet *reloadSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfSectionsInTableView:self.yourTableView])];
[self.yourTableView reloadSections:reloadSet withRowAnimation:UITableViewRowAnimationAutomatic];
Hope it helps!
My question is, when and how often is this method called?
Short Answer : When your UITableView needs to update something.
Long Answer : Delegates Methods generally called themselves however it may be called multiple times when your UITableView needs to update something. By default, it's called very first time the tableview is getting loaded or updated (reloaded).
It depends on how often user will scroll UITable view to section and how many sections there are. This value, which is returned by this function and is casched. Method will need be revoked if you will update content of table view (filtering results, or updating data via reloadData).
Best thing for you will be to add logging to this function and check this yourself.
In my application I used two tableViews. For the first table all delegate and datasource methods are called. When I add the second table and call the method
[self.tableView reloadData];
cellForRowAtIndex method is not called. In the second table, the number of sections is 1 and the number of rows is 5. I set the delegate and datasource for tableView programatically.
It can be due to any of the below resons :
You have not connected the outlet to second table you are reloading.
You have not assign delegate as yourtable.delegate= self;
So if there is any of the missing case fix it.
When NSAssert(self.tableView.dataSource, #"No datasource"); is crashing, you haven't set the dataSource of the tableView correctly.
The NSAssert will crash your app if the condition inside the parenthesis of the assert validates to false (or 0, or in this case nil).
Wherever you create your tableView, make sure that you set the dataSource (and probably delegate too) correctly.
Regarding setting the dataSource correctly, don't make the mistake to set the dataSource to a newly created instance of your viewController. Most likely you want to use self.tableView.dataSource = self;.
I've been stuck on this for all day.
I have a Nib file with a custom table cell wich I use to load into a
tableView instance variable of a view controller.
In the view controller I have a Search Bar and a Table View IBOutlets and a NSMutableArray to store my data as instance variables, among other stuff.
I have an object wich does asynchronous calls to an external API using Restkit so I define my view controller as it's delegate to receive the data when the request is finishe, save it on the NSMutableArray and then trigger a reloadData.
Because I'm making asynchronous calls the method shouldReloadTableForSearchString is in charge of sending the request and then return NO.
If I return YES instead of NO, making a call to reloadData after, my cells get loaded into the tableView one step back of my calls, that means that if I type the first letter nothing happens but on the second letter I get the results from the first letter. That behavior it's ok because the instance variable are setted after shouldReloadTableForSearchString returning YES. That is expected, Ok.
The problem that I'm facing is that if I use only reloadData, with shouldReloadTableForSearchString returning NO, the cells are not being assigned to the tableView. I checkhed and cellForRowAtIndexPath is being called and every cell is created with the data but it does not show.
Maybe I have problems with the datasource property, but I'm not sure.
I don't get the difference. Any Ideas?
I'm using a UITableView which loads data from a server. I need to use async ASIHTTPRequest, which is in a separate .m.
How could I force to reload the table data when the requestFinished:request is called?
Thanks!
You can just send the data that needs to be loaded into the TableView by passing it between files. and then finally call a different method of inside the table view that implements the command [self.tableview relaodData];
Update:
What i see from your comments is that the you have a tableView CONtroller or someform of controller that loads the data by calling another class that holds the ASIHTTPrequest/ ansynchrinous requsts. now You have also told me that the TableView Also hold a UIViewController in each cell which i Dont think is a good idea you could go with a custom cell method have a look at this 4 part tutorial... now and the data that needs to be loaded into the TableView is loaded by TableViewController by some method wither it be each cell or something... so after you are able to get the data just inside the tableViewController just Call the [self.tablebview reloadData] inside the TableViewController which is the parent of the TableView there is no need to call in the other methods.
Finally solved with observers although when the app is completely closed won't work. I'll think in something.
Thanks