In my app(Using Storyboards FYI) I have a viewController which contains a tableView. My viewController hits an API and returns an array of items that my tableView should populate with. The problem is, after successfully retrieving the array, the reloadData method does not trigger a call to cellForRowAtIndexPath. I've set the delegate and datasource properly, and still have no luck. Here is my code:
in my viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView = [[UITableView alloc]init];
self.tableView.dataSource = self;
self.tableView.delegate = self;
[[CCHTTPClient sharedClient] getItemsWithSuccess:^(NSArray *items) {
self.items = items;
[self.tableView reloadData];
} failure:^(NSError *error) {
}];
}
and also in my numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.items count];
}
I've also found that if i did an API call on the previous VC and set the array of the viewController's items before the viewController is pushed, the table populates properly. really unsure about what's going on here. Any help would be gratefully appreciated !
self.tableView should be an outlet if your controller and table view are made in a storyboard. You're creating a different table view in viewDidLoad with alloc init, so you're calling reloadData on that one, not the one you have on screen. Remove that alloc init line, and connect the outlet in IB.
I would suspect this is caused by calling reloadData from a secondary thread. Try this:
[[AGHTTPClient sharedClient] getItemsWithSuccess:^(NSArray *items) {
self.items = items;
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
} failure:nil];
Related
I wanna reload some data in my FAQViewcontroller. The tableview is placed in a nib file (view) which is loaded with the Viewcontroller, called this way:
FAQViewController *vc = [[FAQViewController alloc] initWithNibName:#"FAQViewController" bundle:nil];
[self presentViewController:vc animated:YES completion:nil];
In viewDidLoad of FAQViewcontroller, the tableview is displaying the right way (I see the separator lines and when I'm changing the backgroundColor, it is working well). But there's no information to display yet, so the cells are empty (as expected).
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.tableView registerNib:[UINib nibWithNibName:#"FAQCell" bundle:nil] forCellReuseIdentifier:#"FAQCell"];
if (self.faqData == nil || [_faqData count] == 0) {
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
[[DataModel sharedInstance] getFrequentlyAskedQuestions:^(NSError *error) {
}];
});
}
}
When the network call is done, this function in the FAQViewcontroller is called:
- (void)setFaqData:(NSArray *)faqData {
_faqData = faqData;
NSLog(#"%#", _faqData);
dispatch_async(dispatch_get_main_queue(), ^() {
[self.tableView reloadData];
});
}
But reloadData is doing nothing. The numberofrowsinsection isn't called.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _faqData.count;
}
I don't know what to do about it. Never had this before (normally I code in Swift, maybe I forget something in Objective-C)?
Does anyone know what I'm doing wrong?
Thank you very much!
Could you try making tableView as a strong reference?
something like:
#property (nonatomic, strong) IBOutlet UITableView *tableView
I'm creating the UITableView datasource array using this code:
-(void) viewWillAppear:(BOOL)animated
{
// IBOutlet of tableview is 'editMsgTableView'
editMsgTableView.dataSource=nil;
editMsgTableView.delegate=nil;
menuMessageArray = [[NSMutableArray alloc] init];
editMainMenuMsgArray = [[NSMutableArray alloc] init];
menuMessageArray = [DBManager fetchmenu:0 noOfRows:32];
for(int i=0; i< [menuMessageArray count]; i++)
{
NSMutableDictionary *menuMsgListDic = [[NSMutableDictionary alloc] init];
menuMsgListDic = [menuMessageArray objectAtIndex:i];
[editMainMenuMsgArray addObject:menuMsgListDic];
}
editMsgTableView.dataSource=self;
editMsgTableView.delegate=self;
[editMsgTableView reloadData];
}
But it works for the first time. But whenever I do some tableView editing stuff or comes from another view controller,after that if viewWillAppearis called then reloadData is not working. I also tried:
dispatch_sync(dispatch_get_main_queue(), ^{
[editMsgTableView reloadData];
});
but not working. Please help me out.
When editing begins call [tableView startUpading]; and when editing is done, call [tableView stopUpdating]; then [tableView reloadData];.
Try ,
dispatch_async(dispatch_get_main_queue(), ^{
[editMsgTableView reloadData];
});
are you giving to that viewController the tableViewDelegates correctly? :
#interface theNameOfTheViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> in the .h file?
i suggest not to do this:
editMsgTableView.dataSource=nil;
editMsgTableView.delegate=nil;
editMsgTableView.dataSource=self;
editMsgTableView.delegate=self;
just this:
[self.editMsgTableView reloadData];
be sure that all the delegate methods are called when you use reloadData (put a breakpoint in every delegate method that should be used to reload the Data of the table when you make a change or the view Appears), remember that reloadData method, calls all delegate methods (if you´re giving to the view controller correctly the delegate as I said before) of the tableViewController. If nothing of this helps you, tell me.
I am assuming that you are navigating the view from one view to another view, then you should call [tableView reloadData] on ViewDidLoad.
This is because of the view life cycle - when you are coming back from one view and your tableView data source is updated, the view life cycle will be like this:
viewDidLoad
tableViewDelegateMethods
viewDidAppear
So when you update tableView's data source object and call reloadData during viewDidLoad will resolve the issue.
I struggle with tableview datasource and delegate methods. I've a tableview if user navigate to that table view controller I'm calling web service method which is block based service after the request finished successfully reloaded tableview sections but tableView:cellForRowAtIndexPath:indexPath called twice. Here's my code.
viewDidLoad
[[NetworkManager sharedInstance].webService getValues:self.currentId completion:^(NSArray *result, BOOL handleError, NSError *error) {
self.data = result[0];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 4)] withRowAnimation:UITableViewRowAnimationAutomatic];
});
}
but cellForRowAtIndexPath self.data value is null in the first time.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"%#", self.data); // first time it print null
}
So is there any ideas about this? Thank you so much!
Did you initialise the data array in viewDidLoad? If you didn't it will return null.
if you want to avoid two calls to the tableview try this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if(!data)
return 0;
return yourNumberOfSections;
}
The tableview calls cellForRowAtIndexPath when it needs to render a cell on screen. If the tableview appears before it has any data, self.data will be empty.
In viewDidLoad set [[self.data = NSMutableArray alloc] init] (for example) and in the datasource/delegate methods of UIITableView it should correctly return numberOfRows etc as zero until your web service populates your data.
It sounds like -tableView:cellForRowAtIndexPath: is being called simply because the view is loaded and the table is trying to populate itself before you've received any data from your web service. At that point, you won't have set self.data (whatever that is) to any useful value, so you get null instead. When your web service returns some data, the completion routine causes the relevant sections to be reloaded, and the table draws the data.
I have to initialize my UITableView instance within viewDidLoad, such as:
tableView1 = UITableView(frame: frame)
However, when I tried to access the view controller that includes the tableView1, the UITableViewDataSource's tableView: numberOfRowsInSection: is called before the viewDidLoad method, and within the numberOfRowsInSection:, I want to refer to the tableView1 like if tableView == tableView1.
However, then how can I initialize the tableView1 before calling viewDidLoad? If I tried to initialize it within viewDidLoad but still refer to it from within numberOfRowsInSection:, then it causes the error: fatal error: unexpectedly found nil while unwrapping an Optional value because it is not initialized yet and hence its value is nil.
I think if I use storyboard and #IBOutlet, I don't have the problem. But now I use them all from my code, so I'm not sure how I can cope with the issue.
UPDATE
If this cannot be done as posted by #coolcracker, then how can I port this Objective-C code?
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo;
if (tableView == _tableView1) {
sectionInfo = _frc1.sections[section];
} else if (tableView == _tableView2) {
sectionInfo = _frc2.sections[section];
}
return sectionInfo.numberOfObjects;
}
Don’t use Storyboard at all, just declare and initialise your tableview as follows
#interface ApplicationsPageGroups : UIViewController
{
UITableView* tableView1;
}
- (void)viewDidLoad
{
[super viewDidLoad];
tableView1 = [[UITableView alloc] init];
tableView1.frame = CGRectMake(10, 95, [UIScreen mainScreen].bounds.size.width-20, [UIScreen mainScreen].bounds.size.height-230);
tableView1.delegate = self;
tableView1.dataSource = self;
tableView1.tableFooterView = [[UIView alloc] init] ;
[self.view addSubview:tableView1];
[self getTableData];
}
-(void) getTableData
{
// after you get some array of data to fill the tableview
[tableView1 reloadData];
}
Now override all the UITableView delegate and dataSource methods.
simply add
[tableView1 reloadData];
which call your DataSource methods call again
You could override the view controller initWithFrame or initWithNibOrNil method and initialize the tableViews there.
Also, have you tried initializing the table views BEFORE calling the [super viewDidLoad] method? I have not checked, but it may be possible that the superclass controller is calling the reload method before your initializers, depending on your class hierarchy.
I am downloading a string with the contents of a file online and separating these components by string into an array.
Once the download is finished I am calling a method to update my tableview to display these values.
- (void)updateTableView {
tableView.delegate = self;
tableView.dataSource = self;
}
Basically in this method I set the delegate and dataSource so the table view gets updated using this method...
- (UITableViewCell *)tableView:(UITableView *)tView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell.textLabel.text = [arrayDownloaded objectAtIndex:indexPath.row];
return cell;
}
But the issue is is that the table view doesn't get updated until I "touch" the view.
Like all the data is loaded and the method has been called but not until I tap on anywhere on the screen does the tableview get updated?
Why is this and how can I make it update as soon as I set the delegate and dataSource?
-Henry
Just reload the tabledata after finishing the downloading.
- (void)updateTableView
{
tableView.delegate = self;
tableView.dataSource = self;
[tableView reloadData];
}
I figured the solution. Rather than calling [self updateTableView]; to call the following method...
- (void)updateTableView {
[tableView reloadData];
}
I needed to do [self performSelectorOnMainThread:#selector(updateTableView) withObject:nil waitUntilDoneNO];
Some people are calling this an iOS bug since if you are not on the main thread numberOfRowsInSection gets called but cellForRowAtIndexPath doesn't.
Just a strange thing that I saw happening in my app. Hopefully this helps future developers.
-Henry
If you aren't already doing this somewhere, you need to call [tableView reloadData] after you've applied your changes.