I am currently developing a Core Data application. I have a table view that shows a list of items whose attribute scheduled (which is a date) is nil and a BOOL attribute is NO. There is a button in each of the table view cells that allows the user to set the date in a modal view. There is a date picker in the modal view. The item is passed to that modal view controller.
The date is set when the user taps the Done button in the modal view. The date is set with this line of code:
self.item.scheduled = self.datePicker.date;
Apparently this line of code causes the UI to be blocked for ~1 second (on a 5th generation iPod touch), which is undesired behavior. I used Instruments and discovered that -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] took over 900.0ms.
Can someone please enlighten me as to what is causing the slowness? I am using just one MOC at the moment. Am I supposed to use another to make the change?
Edit: The method that took the longest time I got from Instruments tells me that the table view controller seems to have tried to re-fetch or update the cell, causing the slowness. I have -com.apple.CoreData.SQLDebug 1 passed on launch though, but no message was shown when the date was set.
This is the call tree I got from Instruments.
CoreDataTableViewController is taken from a book about Core Data that I read. It is mostly the same as the one found here.
I finally found the answer. It turns out Auto Layout is extremely slow when offscreen. This is why it can be seen in the call tree that the layout operation took the majority of time.
The solution I chose was to remove the table view controller as the NSFetchedResultsController's delegate in viewWillDisappear:. Then, in viewWillAppear:, I perform a re-fetch and call reloadData on the table view, and set the NSFetchedResultsController's delegate property back to self (which is the table view controller).
EDIT: As amb mentioned in the comments, the solution above is a bad approach. This is the better approach. I should add that my table view only correctly reloaded when I added
if(self.tableView.window == nil)
return;
also to controllerWillChangeContent:.
Related
In my app, being developed by Swift and XCode 6, I have to query a database and according to that answer then I'll modify and show some UI widgets which, in my case, are three colored buttons whose I have to change their text which consists in a number.
These buttons indicate the number of tasks assigned to an user and they have different colors according to the tasks' priority.
So, shall I query the database in viewDidLoad() function and then change the buttons' text in viewWillAppear according to the answer? Is that right?
Yes you can query in viewDidLoad and do UI modifications in viewDidAppear.
Actually it depends on your need.
If your result will change every time OR if you wanna refresh data every time your View's display then query and modify UI in viewWillAppear OR viewDidAppear as viewDidLoad will query only once at the time your View first loads.
You can query a database whenever you want.
ViewDidLoad will only get called once per instance of your view controller. It is typically used for initializing objects.
In view[Will/Did]Appear you typically update your UI to reflect the newest data you have by setting label text and image view images.
Your described approach is correct if you don't need to re-query the database every time a view appears.
yes you can query it in viewDidLoad but if you do it in viewWillAppear will be more good because viewDidLoad calls once when view loading first time and never called again until unless view will deloc but viewWillAppear call every time for example you have one view and it have navigation view inside and you navigate inside the other view so when you press back button viewDidLoad will not call but yes viewWillAppear will always call.
I have a WKInterfaceController subclass that shows a 6 row WKInterfaceTable. This class is also initial interface controller. When receiving a notification action via handleActionWithIdentifier:forLocalNotification I am modally presenting a second interface controller. The user takes no action on the modally presented interface controller, but loading it modifies the state of one of the 6 objects used by the row controllers in the initial interface. When the modal controller is dismissed, my intent is to reflect the updated state in the table.
I have no issues retrieving the updated objects since their management is handled by another class that I can simply query for updated data. Since the initial interface controller has already executed awakeWithContext:, I can only rely on willActivate to updating the interface. This seems to be in line with Apple's guidelines since I'm not initializing the interface as part of that method, merely updating it.
TL;DR;
When I update the rows via the row controller, only the value that was updated is displayed properly on the cells. The other cells use the default values assigned to the labels in my cell at design time on the storyboard.
For example
Row 1: Count - 3 Best - 3
Row 2: Count - 42 Best - 99
Row 3: Count - 42 Best - 99
The extremely curious part is that this only happens on subsequent calls to willActivate. When just opening the watch app and updating the interface for the first time in willActivate it behaves as expected. When being activated as a result of the modal dismissing, this behavior creeps up.
Thanks for any ideas.
I can think of two possible issues.
First, the WatchKit interface controllers behave as though they are snapshotted in page sets. After they have been displayed once, the values won't update after being deactivated until they are fully on screen and your willActivate method has been called. Switching images has the same as switching a table row label when the interface isn't visible. It MUST be done in willActivate otherwise it appears to get dropped.
The other problem I've seen related to this is when you setNumberOfRows, you need to update the values in ALL rows. Otherwise you will end up with customized row values for the ones you set, and the default values from the storyboard for all other rows. If you're only updating the values for a particular row in willActivate and not calling the setNumberOfRows on the Table, then I'd say you should file a bug with Apple's Bug Reporting System.
In my app, I have a very custom UITableView. The cells are all statically defined in Interface Builder, but based on the data structure the table morphs in many various ways. For example, if some data doesn't exist, some cells (or entire sections) are not displayed, custom separator lines are added to account for missing cells, extra views are loaded into the cells, VoiceOver labels change, etc. Because all the cells are static, I set up the table layout in viewDidLoad because I always have the data available at that time. I have always presented this view controller modally, which has worked great. If the user wants to display different data in this table they have to dismiss the view controller and pick a different item to present it again, and it gets rendered appropriately in all cases.
But now I am converting this into a split view controller for iPad, so this UITableViewController never disappears off screen, but I need to set up the table again when the user taps an item. The problem is, because the table is never deallocated, its previous layout still exists when I load more data into it. It would be a lot of work (and an excellent opportunity for many difficult to reproduce bugs to pop up) to test all possible scenarios and try to reset it back to its "pre viewDidLoad state" or undo those previous layout changes if not relevant anymore, if not impossible because I don't have references to the many different custom separator lines generated.
My question is, is it possible to completely reset the table view controller every time a row is selected in the master view controller, therefore allowing it to properly set up the layout because it is not stuck with the previous layout?
I essentially need some way to completely wipe it clean as if it never did any setup, then instantiate it again to cause viewDidLoad get called (or I can move that code to its own method or viewWillAppear). I'm basically looking for a way to reset the tableView back to how it is defined in Interface Builder.
I believe this would result in a flash because the table would completely disappear then reappear in a different format, but that would be acceptable. If that can be animated that'd be nice. If this is really not recommended at all, how do you suggest I proceed to ensure the layout is always appropriate for the data it is presenting?
I was over-thinking this. There's really no need to completely throw away the table and generate a new one. It turned out to be simpler than I had thought to reset the table back to its default state. Just had to be sure to catch every possible thing that could change, including VoiceOver labels, and reset to nil or the default value. Then it can run through the reset code then the layout code every time the data changes and render an appropriate layout. The most difficult part was to remove the custom separator lines, which I solved by adding each one to an array when it's created, then index through it and remove each one from its superview then remove the Autolayout constraints associated with it. One can wrap all of this into a UIView animation block to get a nice fading effect. It's working quite well.
I've been searching for a few days, and have continued to learn from reading the apple docs and various tutorials, but there's a problem which I can't seem to get a handle on.
I have a simple app that keeps track of projects, with a single Core Data Entity (ProjectEntry). All the attributes are strings at the moment. It's basically a combination of Paul Hegarty's Core Data lectures and Tim Roadley's web tutorial.
I can add and save Entities and populate a tableview with the data. For the moment, I'm using the CoreDataTableViewController subclass that Hegarty provides in the lecture. I'm mentioning that because...
When my UITableView isn't divided into sections, the new information "instantly" appears in the UI after adding a new entry. However, when I add sections ( via sectionNameKeyPath), the new data still saves, but shows up only after refreshing several times OR waiting 30 seconds before refreshing(via a pull-to-refresh mechanism, which Hegarty also provided).
The tableview delegate methods all seem to be working, as do the NSFetchedResultsController's methods. Using the Stanford/Hegarty CoreDataTableViewController subclass in the past has yielded success, and I've learned a lot reading through the implementation file itself.
The controllers are embedded in a Navigation controller, with the managed object context being passed among the controllers via the prepareForSegue method. Some simple logging shows me the managed object context, initially obtained via a UIManagedDocument, is being successfully passed along.
I've tried the [self.tableview reloadData] and/or the beginUpdates/endUpdates in viewWillAppear, but the delay persists.
What is it about dividing the tableview into sections that's causing the delay? Would calling reloadSections on the tableview be necessary? Like I said, the entries are saved with Core Data and the fetchedResultsController populates the non-sectioned tableview instantly...
It's probably something obvious that I'm just missing, but any help would be appreciated.
Warren Burton's comment above made me re-check if my managed object context behavior was consistent while being passed among the view controllers via the prepareForSegue method.
So, as mentioned in my last comment above, it seems to solve the problem for the moment:
from above:
The initial view controller is non-tableview, which can segue to a UIViewController to add a ProjectEntry, or segue to a UITableViewController which lists saved project entries. This initial view controller creates or uses the UIManagedDocument. By setting a one-line check to see if the UIManagedDoc is being used in this initial view controller's viewWillAppear method, the "delay" in displaying new section data in the tableview seems to stop. I'll test it some more before saying the issue is solved. This way, the shared managed object contexts seem better "bound together" via the managed doc - vapul
Is it best practice to Nil out NSFetchedResultsController Delegate when offscreen?
For example, I have a list managed by a NSFRC. When I tap a list item, I get a detail view. I can potentially change something on a detail view that will remove it from the list. Or, if I continue to slide through the detail views, I can cause it to load in more data (which would subsequently update the parent table view NSFRC).
I'm getting strange behavior when I DO nil it out. Can't seem to find advice on this anywhere.
No reason to nil out your NSFRC here. In fact, this is the main convenience of having your tableView managed by an NSFRC, is that it will update itself appropriately while you are off in other views changing data. This also applies data changes sequentially, rather than having to refetch all data when returning to your list.
I have posted a similar question time ago, looking for a technical explanation of this 'best practice'. However I didn't find anything.
I suppose that much of the suggested 'best practices' came from older iOS version, where viewDidUnload could have been called in case of memory warning, but that's not the case anymore.
In my experience, I found out that it is only mandatory to resign in case of background update and merge. All the other case depends on your application logic.
For example when you have nested CoreData entities with subsequent UIView, like so:
child1 (UITableView)-->child2 (Detail)-->child3 --> (UITableView)-->ecc...
Then, a change on child3 will have the child 1 table view react on that change, so you may want to avoid unnecessary call to a remote fetched controller, by resigning as delegate or introspecting the changement and returning YES or NO within the delegate method. It could be simpler and much efficient to refetch the query.
Keeping the same structure, suppose you have a mass update on latest UITableView by merging two context, if you have used the same context for all the controllers, then they will be trying to update its view even if not needed.
So for a simple case like UITableView-->UIView (detail) I can see no problem of keeping delegate assigned, all the other case, I would do a little analysis.