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;
Related
I just encouter a problem, that I call UITableView reloadData method, but nothing happened. No tableView:numberOfRowsInSection is called.
The scenario is:
Table works fine in view A (has the UITableView), then view B shows and hide, after this step, the UITableView reloadData is not working anymore. I am sure reloadData is called in main UI thread and the data source has been changed. Also NO IBOutlet connection issue.
Try putting your call to reloadData in viewWillAppear.
Set BreakPoint on [tableView reloadData]; check that your tableView object is not nil.
I have 5 view controllers, which are in a navigation controller hierarchy. When I reach my last view controller, there's a button that lets me go back to the "Home" view controller (named CardWalletViewController) which is a table view controller. Here's the method I have in my last VC (PointsResultsVC)
- (IBAction)homePressed:(id)sender {
NSArray *VCs = [self.navigationController viewControllers];
[self.navigationController popToViewController:[VCs objectAtIndex:0] animated:YES];
}
CardWalletVC's cells is being filled up by values coming from the saved instances of a Card in NSUserDefaults and it works fine.
Now, what I want is to update the value in my CardWalletViewController, which is the points of a card coming from my PointsResultsVC. Note that this points is saved in NSUserDefaults.
Upon the process of trying to update of the value shown in my CardWalletVC, I placed [self.tableView reloadData]; inside -viewDidLoad, -viewWillAppear, and -viewDidAppear of the said class. I tried placing it one by one in each of this methods, yet it doesn't seem to work.
Advice Please.
EDIT: problem solved
This one served as my guide Save data from one tab and reloadData in another tab.
And as I have discovered, -viewDidLoad of a certain class will only be called once, all throughout runtime of app. Whereas -viewWillAppear, it will be called every time the view appears.
So, I just moved the way I am loading the values saved in NSUserDefaults from -viewDidLoad to -viewWillAppear. Also, inside -viewWillAppear I placed the [self.tableView reloadData]. Then there, problem solved.
There's a lot of things that could be going wrong so you'll need to provide more info to narrow it down.
First of all, viewDidLoad will not be called so you can remove it from here. Both viewWillAppear and viewDidAppear will be called, so you only need it one of these 2 methods.
Next, you need to determine what the actual issue is.
1 - is self.tableView a proper reference to the table? Are you properly maintaining the reference?
2 - is the table actually reloading but using the old data, or is it not reloading at all? You can log the willDisplayCell method to see what is going on when the view appears.
With that info, the root cause can be narrowed down to a solvable problem.
I have a view controller managed in a UINavigationController. My view controller puts up a "loading" screen, and uses ASIHTTP to asynchronously load table data. Then it renders its table, and in each cell there's an image that it uses ASIHTTP to asynchronously load. When it lands each image, it uses [self.tableView reloadRowsAtIndexPaths] to reload that row, inside which the image is fed to the UIImageView in each row.
Works great. But if I back out of the view before it's done loading, it crashes.
Where it crashes is in my -tableView:numberOfRowsInSection method, and NSZombies tells me it dies because it's asking for the -count of an NSArray called self.offers that has been deallocated.
That method looks like this:
-(NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
return [self.offers count];
}
Wrapping that return in if (self.offers) made no difference.
My -dealloc releases and sets-to-nil every one of these properties, including both self.offers and self.tableView itself. I even tried setting up a BOOL disappearing property, hitting it with YES in -viewWillDisappear, and hanging conditional behavior off that, but it doesn't work because viewWillDisappear doesn't seem to get called! Far as I can tell we're not getting ANY method called when the navigation bar pops us off.
What do I do about this?
EDIT:
Thanks to #cduhn (who's bucking for a check!), I did a bunch more looking at this. The problem has been, my -dealloc just isn't getting called when I pop this viewcontroller (nor my -viewWillDisappear nor -viewDidUnload or anything else I could use to unhook the delegation structure that's at the root of this problem).
Then I realized: THIS viewController isn't the one on the NavController stack! What's at the top of the stack right here is a shell view, just a segmented controller and a big empty UIView. I toggle the contents of that UIView between two other UIViewController subclasses depending on the state of my segmented controller. So when my view with the table on it's PARENT view gets popped from the nav stack, this CHILD I'm working on doesn't seem to get any notice about it. Which is odd, because I'm definitely releaseing it.
I can call its -dealloc from my shell controller. I could call its -viewWillDisappear too, for that matter. Is that how I should be handling this? Probably I should put something into my shell controller's viewWillDisappear like:
[[self.mainView subviews] makeObjectsPerformSelector:#selector(viewWillDisappear)];
...so that message propagates down to my child views.
Am I on the right track here, you think??
(Oh man... and that also explains why actions from inside this child table view can't get to self.navigationController! I've been puzzled about that for weeks!)
Bugs like this, where a method gets called on an object after it's been deallocated, often happen when a method gets called on a delegate after that delegate has been deallocated. The recommended practice to avoid bugs like these is to set any delegate (or delegate-like) properties of an object to nil before you release that object in the delegate's dealloc method. I know that's a confusing sentence, so I'll explain it in the context of your bug.
You have an asynchronous image download that finishes after you've backed out of your table view controller. When this happens, you're calling reloadRowsAtIndexPaths:withRowAnimation:, which results in a call to tableView:numberOfRowsInSection: on the table view's dataSource. This call is failing because that dataSource no longer exists.
The problem is that the table view object still has your controller set as its dataSource and delegate properties, even after your controller has been deallocated. The solution is to simply set these properties to nil in your controller's dealloc, like this:
- (void)dealloc {
self.tableView.dataSource = nil;
self.tableView.delegate = nil;
self.tableView = nil; // Releases as a side-effect due to #property (retain)
[super dealloc];
}
Now when your table view tries to call tableView:numberOfRowsInSection: on its dataSource, it will send the message to the nil object, which swallows all messages silently in Objective C.
You should also do the same thing with your ASIHTTPRequest's delegate, by the way.
Any time you have an asynchronous operation that calls delegate methods upon completion, it's particularly important that you set that delegate to nil. When using UITableViewController under normal circumstances you can typically get away without setting the dataSource and delegate to nil, but in this case it was necessary because you're calling methods on the tableView after its controller has gone away.
As far as I can tell, the user cannot actually "back out" of a view while the UITableView is loading it's data. The methods are not run on a thread and block the main one, also blocking UI interaction. I cannot replicate your results. Even, scrolling the table view quickly and then pressing the back button.
I suggest that the stack popping is not the problem here.
I have a UITableView which is populated by an array, I have a button on the navigaton bar which (when pressed) adds an item to the array and calls [self.tableView reloadData] in the UITableView. This results in numberOfRowsInSection being called and returning the correct number of rows (the number of items in the array) BUT doesn't call cellForRowAtIndexPath.
I have created a new navigation based application to try and find a solution but have exactly the same problem!
If anyone knows the answer it would be greatly appreciated, i've been tearing what's left of my hair out for the last day!
I have put the source for the test project up on my site at www.sofaracing.com/Downloads/Test3.zip
Works for me! ;)
In MainWindow.xib, You added an object Root View Controller (that's not part of the Navigation Controller) - Delete it, not necessary. Then connect outlet refreshFriendsList to the Bar Button of the NavigationItem of RootViewController. Wha-la, magic!
BTW: You may need to clean up the warning. And you might want to think about creating a class for your data model instead of using UIApplication sharedApplication.
I quickly debugged your test app. I couldnt't spot the root cause, but it seems that you have two different table views due some mess up in Interface Builder setup.
If you initialize original array with an item, cellForRowAtIndexPath is correctly called. If you examine self.tableView instance in this call and later in subsequent calls to refreshFriendsList, self.tableView points to a different instance.
2009-05-17 14:33:07.591 Test3[33580:20b] cellForRowAtIndexPath 0
2009-05-17 14:33:07.594 Test3[33580:20b] self.tableView: <UITableView: 0x52b9b0>
2009-05-17 14:42:36.810 Test3[33762:20b] numberOfRowsInSection: tableView <UITableView: 0x53bcd0>
I have a RootViewController and a SubViewController. I traverse the views in either direction. The RootViewController is a UITableView SubClass. Depending upon the row selected I want to change the title of the subview using self.navigationItem.title = [chunks objectAtIndex:1];
It works when the subview is first time loaded. But when I return to the RootViewController and load the subview again the previous title persists.
Any ideas what am I missing out on?
In your particular case, you probably want to set your title in viewWillAppear: so that the title gets set every time the view comes on the screen.
I just tried it in an app of mine. When I set the title using
self.navigationItem.title = #"Foo"
the name in the navigation bar changes instantly. I think you have a bug somewhere else that you code is only getting called the first time you invoke your SubViewController. Stick a break point on that line and see if it actually gets called a second time. Or perhaps [chunks objectAtIndex:1] is always returning the same string.
Or perhaps I'm not understanding your question. As far as I can tell it does work like you are expecting it to.
I guess you are trying to change the title in the viewDidLoad method. The viewDidLoad is called only the first time the view is loaded. In case you are reusing the same viewcontroller's instance, viewDidLoad will be called only once.
Instead try setting the title in viewWillAppear method. This method is called every time the view is going to be displayed. That should work.