3

I am calling a method in my TableViewController class from another class.

To call the method of displaying the tableview, I do this:

TableViewController *tableVC = [[TableViewController alloc]init];
[tableVC setTableViewContent];

then in TableViewController.h

@interface TableViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>
{
NSMutableArray *nameArray;
}

-(void)setTableViewContent;
@property (nonatomic, strong) IBOutlet UITableView *tableView;

@end

TableViewController.m

@implementation TableViewController
@synthesize tableView;


- (void)viewDidLoad
{ 
 nameArray = [[NSMutableArray alloc]init];
[super viewDidLoad];

}

-(void)setTableViewContent{


AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

for(int i=0;i< [appDelegate.businessArray count];i++)
{

    NSDictionary *businessDict = [[appDelegate.businessArray objectAtIndex:i] valueForKey:@"location"];


    nameArray = [appDelegate.businessArray valueForKey:@"name"];

}
NSLog(@"%@", nameArray);


  NSLog(@"tableview: %@", tableView);

// here tableview returns null
[tableView reloadData];


 }


- (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 [nameArray count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"updating tableview...");
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Configure the cell...
         cell.textLabel.text = [nameArray objectAtIndex:indexPath.row];

    return cell;
}

For some reason when I try to log the tableview, it returns null, so the ReloadData doesn't work. The delegate and datasource is connected properly in IB, and there is a referencing outlet for tableView.

Any idea what is going on here? Thanks in advance

Spenciefy
  • 892
  • 11
  • 33

3 Answers3

3

If you added the table view controller to a container view, then you can get a reference to that controller in prepareForSegue. For a controller in a container view, prepareForSegue will be called right before the parent controller's viewDidLoad, so you don't need to do anything to invoke it. In my example below, I've called the segue "TableEmbed" -- you need to give the segue that identifier in IB.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if([segue.identifier isEqualToString:@"TableEmbed"]) {
        TableViewController *tableVC = (TableViewController *)segue.destinationViewController;
        [tableVC setTableViewContent];
    }
}

Be aware that prepareForSegue:sender: is called before either controller's viewDidLoad is called, so you should move the initialization of your array to setTableViewContent, and your reloadTable should go into viewDidLoad.

BTW, it's not clear to me why you want to call setTableContent from your other class anyway. Why not move all the code in that method to the viewDidLoad method of the table view controller?

rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • Basically I am connecting to a server to pull data first, so when the app first loads, there is nothing to populate the tableview with. Because of this, I have to get all the data from the server and then populate the tableview, presumably on the empty tableview with reloadData. That is why I need to call setTableContent, after I finish pulling data from server. – Spenciefy Jul 17 '13 at 20:05
  • And because prepareForSegue:sender is called before the viewdidloads, there is no data to populate the tableview with as the server pull has not finished yet (hasnt even started). Right now I can populate the tableview in viewdidload directly in the tableViewController class with a locally stored array, but I need to first pull an array from the server and THEN populate the tableview.. which in this case I dont think prepareforsegue will work. correct me if I am wrong. – Spenciefy Jul 17 '13 at 20:08
  • @Spenciefy, then you should create a property for your TableViewController, and set its value in prepareForSegue like I did (it would just change to self.tableVC = *tableVC = (TableViewController *)segue.destinationViewController). Then call setTableContent on that property when you finish your download, rather than in prepareForSegue. – rdelmar Jul 17 '13 at 20:33
  • Sorry, I didn't quite catch that. I put `self.tableVC = *tableVC = (TableViewController *)segue.destinationViewController` in.. the prepareForSegue method? Where is self.tableVC referring to? the tableVC created from `TableViewController *tableVC = (TableViewController *)segue.destinationViewController;`? – Spenciefy Jul 17 '13 at 20:52
  • @Spenciefy, yes, you put that in prepareForSegue. tableVC is a property you should create in your other class (@property (strong,nonatomic) TableViewController *tableVC). You need a property so you can use it in the method where you do the download (rather than just a local variable like I had in my answer). – rdelmar Jul 17 '13 at 20:58
  • So the other class that I was using was an NSObject class, where you cannot use prepareforSegue, so I moved the code to the getInfo class and then added the prepareForSegue, and now everything seems to work. thanks for the help. – Spenciefy Jul 17 '13 at 21:31
0

This is happening because you are calling a method on tableView before it actually exists. Simply initializing that class doesn't draw the table itself, so using reloadData before the table has actually been created doesn't really make any sense.

What you want to do in this situation is create your nameArray in whatever class is calling setTableViewContent, and then pass it in either via a custom init method, or by setting tableVC.nameArray before loading that table view controller.

What I would do is make custom init method like - (id)initWithArray:(NSMutableArray *)nameArr

Which should look something like this:

if (self = [super init]) {
    nameArray = [nameArr copy];
}
return self;

Then where you have TableViewController *tableVC = [[TableViewController alloc]init]; put TableViewController *tableVC = [[TableViewController alloc]initWithArray:theNameArray]; where theNameArray is the content in setTableViewContent (which you are now generating in the same class that calls the table view instead of in the table view itself).

Make sense?

Stakenborg
  • 2,890
  • 2
  • 24
  • 30
  • I understand your logic, but when I implement that into the tableView it doesn't quite work out. I call the init with `TableViewController *tableVC = [[TableViewController alloc]initWithArray:nameArray];` and then in the tableviewcontroller I put `- (id)initWithArray:(NSMutableArray *)nameArr{ if (self = [super init]) { nameArray = [nameArr copy]; } return self; }` and nameArray returns fine, but the tableview is not being made. – Spenciefy Jul 17 '13 at 04:19
  • Also, the tableView does exist before I call it. An empty tableView is the first screen in the app, I can populate it manually. – Spenciefy Jul 17 '13 at 04:24
  • What happens if you run a [tableView reloadData] at the bottom of viewDidLoad? Possible something strange is going on there. Also, since you're setting nameArray in your init now, make sure to remove `nameArray = [[NSMutableArray alloc]init];` from viewDidLoad! – Stakenborg Jul 17 '13 at 04:25
  • Oops. I removed the alloc init and added the reloadData at viewDidLoad. Still nothing being called to set tableView – Spenciefy Jul 17 '13 at 04:29
  • Hmm, it's late here so I have to go to sleep, but the only other thing that comes to mind is to call [super viewDidLoad] before [tableView reloadData], but I doubt it'll help. If tableView is still showing up nil in your viewDidLoad method, it sounds like something screwy is going on in IB. That definitely should not be nil in viewDidLoad even if you don't set anything. Also, verify the delegate methods are being called as well. Hope this at least gives you a place to start. Good luck! – Stakenborg Jul 17 '13 at 04:34
  • 1
    One last thought, since this is a UITableViewController subclass, you shouldn't need to specify your own tableView property with an IBOutlet, it should come built in by using the TableViewController object in IB instead of a regular ViewController. You might have some sort of conflict going on between the tableView you have via a property and the default tableView from the subclass. – Stakenborg Jul 17 '13 at 04:38
  • that seems to have solved the tableview returning null problem, but the reloadData still doesnt work. – Spenciefy Jul 17 '13 at 04:54
  • If the delegate methods aren't being called (like numberOfRowsInSection) on reloadData, then it has something to do with IB, if they ARE being called and the table isn't being populated, then it's got something to do with the data, but at this point I'd think it's the former. – Stakenborg Jul 17 '13 at 11:43
0

I solved a similar situation by creating a "safe" reload method on the UITableViewController:

- (void)reloadTableViewData
{
    if ([self isViewLoaded])
        [self.tableView reloadData];
}

According to the docs for isViewLoaded:

Calling this method reports whether the view is loaded. Unlike the view property, it does not attempt to load the view if it is not already in memory.

Therefore it is safe to call reloadTableViewData on the table view controller at any time.

Brad
  • 181
  • 3
  • 13