I'm developing a graphing application that on the main navigation/tab view displays a UIView that renders a graph using openGL. Beneath that view is a UITableView that displays the list of elements on the graph, and an add button.
Occasionally, when the user clicks on another tab, and then returns to the graph view tab, the table view does not get redrawn.
I have a [tableView reloadData] method being called in the navigation controllers' (also the table view's delegate and data source) viewDidAppear method.
numberOfSectionsInTableView and numberOfRowsInSection get called, but cellForRowAtIndexPath does not despite both latter methods returning positive values.
This is an intermittant problem, only happening some of the time, but it's not clear what (if anything) influences this.
Does anyone have any ideas?
[edit] Should just quickly add; clicking or otherwise interacting with the table view, whether faulted as described or not, ALWAYS causes it to be sucessfully redrawn.
[And again] A bit more information; when it faults as described, and calls the first two but not the cellforRowetc method, it seems to wait until the user interacts with the table before cellForRowetc is finally called (the first two methods are then not called) and is redrawn.
I had a UIActivityIndicator, started on a seperate thread, that showed when requests were being made to a web service. When the web service calls were completed, a notification was sent to the graph controller class to stop and hide the UIActivityIndicator. In this method, reloadData was being called on the table whether the view was visible or not, which seemed to be causing the problem.
The conclusion seems to be not to call reloadData on a table unless it's visible.
Related
I get used to put either of viewWillAppear and viewDidLoad, it's OK until know. However I'm thinking there should be some rules that guide when to put into viewWillAppear and when to put into viewDidLoad?
Simple rule I use is this. viewDidLoad is when the view's resources are loaded. The view is not drawn onscreen yet. So calculations and code dealing with the geometry and visuals of the view should not be put here. They should be in the viewWillAppear or viewDidAppear method.
Also viewWillAppear can be called multiple times
When a popover/modal view is displayed and remove
When an alert view/actionsheet/uiactivityController's view is displayed and removed.
For these reason, viewWillAppear should not contain codes that takes longer to finish. (at least the code running on the main thread). Neither should codes that only needs to be run once per view display.
There are more I am sure but these are simple to remember and I hope it helps.
viewDidLoad: Alerts you that a view has finished loading
viewWillAppear: Runs just before the view loads
viewDidLoad is things you have to do once. viewWillAppear gets called every time the view appears. You should do things that you only have to do once in viewDidLoad - like setting your UILabel texts. However, you may want to modify a specific part of the view every time the user gets to view it, e.g. the iPod application scrolls the lyrics back to the top every time you go to the "Now Playing" view.
However, when you are loading things from a server, you also have to think about latency. If you pack all of your network communication into viewDidLoad or viewWillAppear, they will be executed before the user gets to see the view - possibly resulting a short freeze of your app. It may be good idea to first show the user an unpopulated view with an activity indicator of some sort. When you are done with your networking, which may take a second or two (or may even fail - who knows?), you can populate the view with your data. Good examples on how this could be done can be seen in various twitter clients. For example, when you view the author detail page in Twitterrific, the view only says "Loading..." until the network queries have completed.
I want to add a refresh button in UINavigationBar that refreshes the entire table contents. Nothing happens when i click on the refresh button.
I searched google and found that reloadData method refreshes the tableview so I tried using it.
Are you sure your tableView outlet is linked correctly to your tableView property in Interface Builder ?
Is your internal datasource `refreshed' before you call reloadData?
-[UITableView reloadData] tells the table view to request again from you the number of rows in the table, and the cells at each section and row. It's up to you to update your internal data structures as appropriate. So in this case, your IBAction should call your own method to refetch the data from the server.
A few other notes:
Method names should have leading lowercase.
-initWithContentsOfURL: is blocking. This can hang your program for a long time. You should be using asynchronous fetching here (generally with NSURLConnection).
make sure you have the tableView outlet hooked up to the tableView.
I'm working on an app which allows users to send messages to people which can be selected from a variety of sections. One of these sections happens to be the contacts stored on the phone book (other contacts are selected from an online DB).
As such, I have a 'master list' of recipients. If the user, for example, chooses to select a contact from their online account, this will push a new view that will allow the user to select which contacts to add to the recipient list. When they go back to the master list, they should see the chosen participants selected there. If they were to return to add another person, the selected contacts should all be checked (each contact is displayed as a UITableViewCell).
This is working fine for all the online contacts however I'm having some issue implementing this functionality for local contacts using the ABPeoplePickerNavigationViewController. To check the selected contacts when the user returns to this screen, I need to have some way to now when the view has loaded.
Are any of the view delegates (i.e. ViewDidAppear) guaranteed to be called after the table has been loaded (I'm pretty doubtful on this one)?
If not, I had thought of counting the total number of rows in the datasource (using numberOfRowsInSection:) in a timer. If half a second or so has passed without the count incrementing, it should be a safe bet that all the records have been loaded. Somehow, however, I'm not so sure if this is going to work. It might be that all the records will be loaded in a single hit.
Any ideas on how to achieve this? Are my suggested methods workable? Is there a different workaround?
If you make view transition by standard methods, like presentModelView: or pushViewController:, then the methods about viewWillAppear:, viewDidAppear, viewWillDisappear:, and viewDidDisappear will be called. But if you make view transition by UIView's instance methods, like +transitionFromView:toView:duration:options:completion etc, then you have to call those viewWillAppears methods manually.
All UI related method will run in main thread, and ABPeoplePickerNavigationController is a sub-class of UIViewController. When it's view has been loaded, it will call -viewDidLoad. At this moment, it means the view has been loaded, maybe this view doesn't appear. So, the -viewWillAppear: should be executed after -viewDidLoad. But, the -viewDidLoad maybe executed before return from -init in practice. In my experiment, I set view's backgroundColor inside the -init, then when I use self.view.backgroundColor = [UIColor black], at this moment the view has been loaded. It means the -viewDidLoad will be executed when I set background color of view.
I have a fairly simple setup: RootViewController (which is a UITableViewController). Each cell in it shows some stats for a different person. You can tap on any of these cells to push on a DetailViewController (UIViewController), and modify the stats (which are stored in a model object). After the user is done modifying the stats for that person, they click the Back button, causing popViewControllerAnimated to get called.
My question is: What's the best way to know in the RootViewController that the stats for this player have been changed, and updated the cell accordingly? Do I have to record in the RootViewController which cell was tapped, and then call the appropriate setNeedsDisplay after the DetailViewController completes? And if so, which method should I do this from? viewWillAppear?
Or is there a nicer way? This seems like it'd be a fairly common task, and that I'm missing something obvious.
This depends on how your model is designed and whether or not you're using Core Data.
The basic principle is to observe the properties of your model objects that might change in the detail views. When something changes in the detail controller, somehow mark its table cell as dirty. Then, when the table view becomes visible again, get the changed cell with [tableView cellForRowAtIndexPath:] and reconfigure the cell. The cellForRowAtIndexPath: method will handily return nil if the cell is not visible which will keep you from spending time updating cells that don't need it.
If you're using Core Data (and you probably should be), NSFetchedResultsController does almost all of this work for you. It will observe your model objects and send the controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: message to its delegate for each changed cell. Then, you can make the proper changes to the table view.
I recommend creating a new Xcode project using the Navigation-based Application template with the Use Core Data for Storage checkbox ticked. The template has a good default implementation of the aforementioned pattern.
I was looking forward to find a cause for my application being crushed at some point and I found out that methods of UITableView are being called before or at the same time as viewWillAppear is called.
In viewWillAppear I have code that is initializing number of rows in each section but numberOfRowsInSection is being called before I finished setting up array that has amount of rows in each section.
I believe that viewDidLoad is not suitable in my case because it is being called only once after launching an application. Am I right? And I need to make my initialization function called each time a view appears on the screen.
How can I overcome this failure?
Thank you in advance.
Well, I think this 'problem' has to do with that there are multiple threads running taking care of the view and the UITableView.
The view calling this view could (before switching to this view) call a method on the View which gathers the data.
User pushed button
You fire a method on the destination view, gathering information
Switch to view
You could work with delegates to know when the destination view is ready loading the data you needed, so you can switch to that view then.
Hope this helps.
I think the right answer is that "[super viewWillAppear:animated]" will call UITableView's method.So it is necessary to put this code after the code that initialize the tableView.