I'm encountering a problem where tableView:numberOfRowsInSection: is getting called, then a other methods are getting called that affect the underlying array, then tableView:cellForRowAtIndexPath: gets called and crashes because the contents of the underlying array is different than when numberOfRowsInSection was called. If the framework would call numberOfRowsInSection right before calling cellForRowAtIndexPath, there wouldn't be a problem.
Since I can't expect a fix in the framework in the short term, the easiest solution I'm looking at is returning a dummy cell if the underlying array doesn't contain the requested entry.
More details on how this problem occurs:
App uses a tab bar controller.
The root controller for each tab is a UINavigationController.
The view controller in question is invoked by pushing it on the nav stack of the second tab bar item's nav controller.
The viewWillAppear method of the view controller in question resets the underlying array and then calls [_tableView reloadData].
The app delegate which implements UITabBarControllerDelegate implements the tabBarController:didSelectViewController: method and calls popToRootViewControllerAnimated: on the view controller being selected.
The root view controller's viewWillAppear method calls a method on the view controller in question which affects the contents of the underlying array.
Steps to repro the problem:
Start app.
Tap second tab item.
Tap to bring up view controller in question. Perform work on that view to fill the table.
Tap first tab item.
Tap second item.
At step five, here is the order of the calls:
viewControllerInQuestion: viewWillAppear - reloads underlying array, calls [_tableView reloadData]
viewControllerInQuestion: numberOfRowsInSection - returns 1
App delegate: didSelectViewController - calls popToRootViewControllerAnimated
secondTabBarRootViewController: viewWillAppear - calls method on viewControllerInQuestion that clears contents of underlying array
viewControllerInQuestion: cellForRowAtIndexPath - crashes trying to access object at index 0 in underlying array.
Thanks for any help you can give me.
This turned out to be a problem with improper calls to reloadData on the table view. I inherited this code, but I'm new enough to iOS that I didn't understand the proper usage.
The code was calling [_tableView reloadData] in both loadView and in viewWillAppear. Removing both calls solved this particular problem.
I had a similar problem.
If you have an observer in your controller that calls reloadData, use the viewWillAppear method to add the observer and viewWillDisappear to remove it.
Related
I am using UICollectionView inside a UIPageViewController.
Each page is an instance of the same subclass of UICollectionViewController.
Whenever I do a page swipe to see a page I haven't seen before, the animation gets a small delay (few milliseconds) to start because of the time UICollectionView takes to go through all the delegate/datasource methods.
So I was trying to implement a pre-caching system to load the viewcontroller before the user actually starts swiping.
I am able to create it easily, but, for some reason, whenever I call reloadData on the non-visible ViewController, none of the UICollectionViewDelegateFlowLayout delegate methods get called.
So far I have tried to use: setNeedsDisplay, setNeedsLayout, setLayoutIfNeeded and invalidateLayout to force them to get called, but nothing is working.
The viewcontroller is not nil. The view is not nil.
Does anyone know what could be the reason? Is it because the viewcontroller is not in the viewport?
Loading the view controller first is probably not the best idea.
You should rather cache the data source.
About reloadData not working properly with a non displayed controller, you are right, it's because the controller is not present.
The delegates won't fire unless the view is visible.To counter the delay you are facing you would have to ditch the collectionview and use collectionview controller and add those collectionview controllers as child to page controller
Note: Set the frame appropriately for each collection controller
CollectionController *collectionController = [self.storyboard instantiateViewControllerWithIdentifier:#"Calculate"];
[self addChildViewController:collectionController];
collectionController.view.frame = self.calculationView.bounds;
[self.pageControllerView addSubview:collectionController.view];
[collectionController didMoveToParentViewController:self];
I have a table view, onclick of 7th row, I push another view. Then when I come back using pop, tableview is reloaded automatically in ios8. It does not happen is ios 7.
Problem is cellForRow and HeightForRow for 0,1,2,3 cell is not called. Hence table scrolls the 7th row up, and not visible. 9,10,11 cells are visible.
I want table to stay as it was when I come back to that view.
I saved selectedIndex and scrolled table to particular index in viewWillAppear.
[self.table_exhibitorProfile scrollToRowAtIndexPath:self.selectedIndexPath atScrollPosition:UITableViewScrollPositionNone animated:NO];
It shows 7th cell, but its position is not same as it was. It goes to top.
It shouldn't be reloading unless you tell it to. It's not automatically reloading in iOS 8 for me.
The only possibilities that I can come up with are that
you're using CoreData, an NSFetchedResutsController, and the same managedObjectContext in both view controllers. If something changes in the managedObjectContext in the second view controller, when you pop back, the fetchedResultsController will reload the table, or
you're calling tableView.reloadData() in viewWillAppear() or viewDidAppear().
I had the same problem and this is how I fixed it:
According to the Apple docs:
UITableView overrides the layoutSubviews method of UIView
so that it calls reloadData only when you create a new
instance of UITableView or when you assign a new data source.
In my case I was setting the table view's dataSource to nil in viewWillDisappear: and setting it back to self in viewWillAppear: thus causing the data to be reloaded.
The 'automatic' reload went away when I moved the dataSource assignment to viewDidLoad
Hope this helps!
In my app, there are tabs. I have one tableviewcontroller that contains messages and I have implemented pull to refresh so that works fine. However, if the user goes from the message tab to another tab and then back to the message tab, the uitableview doesn't reload and the user has to pull to refresh. I have thought of putting [self.tableview reloadData] or [self loadObjects] (i am using Parse) in viewDidLoad/viewWilAppear, but that doesn't seem to work...it's because they are only called when the view controller is initially visited right? So I'm wondering as to where I should put that code so that the table view can be reloaded every time the view controller is revisited?
viewWillAppear and viewDidAppear are both called every time the view controller appears. If you call reloadData in one of those methods then it will refresh the table view.
I think your problem is you aren't updating your data source. You will need to make another call to Parse otherwise your table view will just reload with the same data.
I don't think that this behavior is desirable. I suggest you tu wait for a reasonable timeout before updating data, or your user will experience lags and high network usage.
However to do that, you should set a delegate that fires when the corresponding view is loaded (take a look here how to get the event that switch tab menu on iphone ) and then call
[yourTableView reloadData]
inside didSelectViewController
When is the best time to call [reloadData] on a UITableView? viewDidAppear or viewWillAppear? If a view is unloaded, will the underlying tableview be unloaded as well?
The reason I am asking is due to some behavior I am seeing. Let's say a view is asked to reload its datasource, but is deallocated before it can finish. A scenario would be with a UITabBarController and navigating to a different view than the ViewController being selected. So what happens is that viewWillAppear gets called, but viewDidAppear does not (since I navigated away).
As a result, the ViewController gets deallocated (along with its model data), but if I am calling reloadData in viewWillAppear, the "cellForRow", and "numberOfRows" methods are invoked which causes a "deallocated instance" error. Does that make sense? Do you normally put in code that checks for nil if the model can be changed anytime the view is requested to appear?
I would load the table once the view has loaded, it allows for all of the UIObjects to be created before messing with them. Not sure, but I believe the table is automatically unloaded with the superview.
Take a look at this question>>>
Objective C - Correct way to empty and reload UITableViewController with NSMutableArray
before you get the datasource from server or anywhere, you can display the tableview with a blankcell(a cell that you can draw sth like 404 web page) , when you get the data, then reload the tableview.
remember to set number of cell to 1 for the blankcell
I got two viewControllers using a navigation bar. The first viewController displays some data I change on the second viewController.
So if I load the second viewController, a back button appears in the NavBar and I can change my values (and they are stored, I used the debugger). My problem is, after hitting the backButton to come to my firstView Controller, it does not call it's viewDidLoad method. It's clear, that there are no updated values at all, when this function is not called.
At the first start, the viewDidLoad method is called and does what I want it to do. After going back and forth between the viewControllers the method is not called again.
Any solutions?
Thanks!
EDIT:
SOLVED
I did not want to delete my question, maybe someone needs this too:
This method is called every time the view appears, it is probably not defined by default:
-(void)viewDidAppear:(BOOL)animated
{
NSLog(#"View appeared.");
}
But the update code (like [[self view] setNeedsDisplay];) in viewWillAppear.
To make it clear: viewDidLoad is called when your view is loaded. This happens at the first time the view is going to be displayed. When you then navigate to the next view the first view can (depending on your code) still be loaded. Therefore when you navigate back to that view viewDidLoad won't be called because the view is still there.
But every time the view is going to be shown (for example when you navigate back to this view) the method viewWillAppear and viewDidAppear will be called.
Hope that helps.
ViewDidLoad method will get called when there is view controller allocation-initialization happens. In the case of moving back in navigation controller, it is not creating any new view controllers, it is reusing previous references from navigation stack.
Hence viewDidLoad will not get called. As your view controller is already in memory stack. So it will just make the view to reappear on windows and it will call viewWillAppear respectively.