In my view I have a table view along with a segmented control.
The table view is getting its data from an NSFetchedResultsController.
When clicking the segmented control I try to change to another NSFetchedResultsController (which is using another predicate, sort descriptor and even another type of entity).
Something like suggested in one of the answers to this question.
I also have typedefed my own enum, to keep track of which state the table view is in (which segment that currently is selected).
The problem is, when the table view reloads its data, I get an "NSInternalInconsistencyException", since the number of sections and/or the number of rows have changed from X to Y - but "(0 inserted, 0 deleted)".
My questions are:
Is it even possible to solve this the way I'm trying to?
Where should the beginUpdates, endUpdates and reloadData go (in relation to the switch of NSFetchedResultsController)?
Anyone with better solutions to this problem?
Thanks in advance!
If you are doing lazy loading of the FRC, then kill the FRC and call reloadData on the UITableView to get the new FRC with the new predicates/sort descriptors to be used.
This is the same thing you do when using a UISearchBar, as answered in this question
Related
Right, so my UITableView loads and puts all the cells in Alphabetical order. Information is then downloaded from a server and calculations are done, with the TableView being reloaded once everything is complete. Currently this is quite a simple procedure as once the information is downloaded from the server, the cells don't even move, they are left in their alphabetical order. Nothing really happens other than half of the information is filled in and small changes are made depending on the calculations. I was wondering if there was an easy way of putting the cells into sections depending on the calculations done after the download is complete? I did have an idea of creating 4 arrays (there will only be 4 sections ever) and once isLoading is set to no, changing the data source of the TableView to have sections, however, that sounds a bit... iffy. I know this is a theoretical question as opposed to a coding problem, but before I go and mess up my code, in what is sure to be a stupidly inefficient way of doing things, is there an easy way of "assigning" UITableViewCells to sections?
My main issue with my way of doing it is that should the user delete a cell, deleting the appropriate entry in Core Data will be a little tricky and prone to errors. This lead me on to another idea. What if I added an extra attribute to my Core Data entity. That attribute would be assigned and then saved once the calculations were done. The problem with this is that no existing databases would work. There has to be a neat way of achieving this.
Thanks for the help. If you need me to post any code just say so and I will.
You should be fine if you implement the data source methods related to sections.
For example:
numberOfSectionsInTableView
sectionIndexTitlesForTableView.
Any time the table data is reloaded (e.g., [self.tableView reloadData]), these methods will be called and the data will be placed into their sections.
Keep in mind that the cells are just the visual representation of your model, which in this case is your fetched data. The cells are not assigned to sections; they are simply created however you specify for your model (via the table view data source and delegate methods).
Regarding deletion of entries while using Core Data, I suggest taking a look at NSFetchedResultsController. The latter will monitor any changes to your table's data and message its delegate, your table view controller, when updates are made.
For example, a deletion would start with a call to the table view delegate like normal (i.e., via tableView:didEndEditingRowAtIndexPath). Within the latter, you would then delete the entry from core data (e.g., [self.myDatabase.managedObjectContext deleteObject:entity]). Assuming you initiated the NSFetchedResultsController w/ the same managed object context, the deletion would be automatically reflected back to your user.
If you're using a remote DB, however, you'll also have to perform a save (however you've implemented that) to ensure the DB is updated too.
Note also that if you use an NSFetchedResultsController, you don't need to implement the section data source methods since NSFetchedResultsController can handle that for you. Just define the key-path in your data model that will return the section name when initializing the NSFetchedResultsController.
I am having a view to display a list of Shops within a UITableView.
The user should have the possibility to modify the shops displayed by some filters.
For example it should be possible to display the shops ordered by name (with the first letter as section header). Further the user should be able to display just the favourites or sort the shops by category (category name is section header):
I did not want one table view, handling all the filters because I thought it would be a mess of if-then-else tags depending on the current filter. So I decided to create 4
UITableViewControllers with each of them handling one filter.
That's exactly what I need, no redundancy so far, because the tableVC delegate and datasource methods are the ones which need to be implemented differently.
But now I do need a header which appears depending on the scroll position of the table. This is exactly the same in all of the 4 tables. But how should I solve this issue now?
Implement the UIScrollViewDelegate methods in each of the four UITableViewControllers redundant? Create a ParentViewController handling the scrollView methods (which would not be so easy because of the table datasource and delegate methods...
Any suggestions how to solve this properly? How would you do this?
I would appreciate any help, thanks in advance.
I would create a parent class, taking care of the header and derive the UITableViewControllers for the filters from it.
The superclass takes care of the common elements (like the header), the subclasses take care of the actual display.
Alternatively, you can create an overloaded method, which returns an Array of cells to display.
Store this array and when the delegate (in the superclass) asks for the 'cell data', give him the corresponding element in the array.
I have a UITableView which reloads every 3 seconds. I reload it only if my Boolean variable is true. In some conditions that variable is always true and that time table is difficult to scroll. (performance is not very good). Other times its ok. What should I do?
Note: I have coded my table according to apple's recommended way(UITableView best practises. Except I add subviews to UITableViewCell, instead drawing on it. I'm confident with other techniques).
What's the solution for this?
Are you sure that because you refresh a lot that makes you difficult to scroll. What kinds of refreshing you mean here, refreshing data from network or refresh the table view your cell.
Everytime you refresh your table view or when you scroll the table view, the table view will keep asking for the corresponding cell, and now the whole performance depends on how fast you return the cell.
If you are doing custom UITableViewCell, you are in risk of having a slow performance. Double check these things:
Do you reuse your cell correctly? Check if [tableView dequeueReusableCellWithIdentifier:] always return nil or not. If it always return nil, then you do it wrongly.
Check if you block the main thread somewhere by loading images from network or file, if it is, using multithread.
After you check everything and still has a slow performance, then you either need to reduce the times a cell is returned (by less refreshing) or draw the cell yourself
If you are fetching large data set for UITableView, try to fetch the data on need basis. Get the data for each cell in cellForRowAtIndexPath instead of getting all the data for table view and storing.
For the app I'm currently working on, one of my main ViewControllers contains two UITableViews whose contents vary based on what's stored in my model (Core Data backed by SQL). Both tableviews set their delegate and datasource properties to the shared VC they're both subviews of, and I provide the necessary methods in the VC to respond to tableview-specific events.
I'm finding that when I add new data to my database that should cause the TableViews to show new rows, and I return to the VC and call reloadData on both TableViews in the VC's viewWillAppear method, I see that numberOfRowsInSection returns the correct number of rows based on the new entries in CoreData, but cellForRowAtIndexPath is not called the correct number of times, almost like it's not responding to the change in the model data. For example, if I had 3 rows to show then the app started, and I added another entry in CoreData, then called reloadData, numberOfRowsInSection returns 4 correctly, but cellForRowAtIndexPath is only called 3 times still.
Besides the fact that I probably be using something more suitable like NSFetchedResultsController to link my TableViews to CoreData, is there any reason why cellForRowAtIndexPath is not being called the correct number of times, as dictated by the correct number of rows returned by numberOfRowsInSection?
I would put in some of my code, but it's really standard boilerplate TableView stuff and I feel like there's something else that I'm missing.
Just to make sure you understand correctly, the cellForRowAtIndexPath: is called with the number of cell that can be in the screen. If you have 4 cells, but because the height of the cell, only 3 cells can be available to users, then the cellForRowAtIndexPath: is called 3 times
If the user can only see only 3 cells, not more, then the cell loading callbacks may only be called 3 times, even if there are 100's of rows in your table.
This seems like it should be straight forward. Technically I can make this work, but if I want to perform certain UI animations, (using deleteRowsAtIndexPaths:withRowAnimation:) things get hairy. It seems as though the datasource and UI are fighting over who should delete first. Then I have to artificially keep data around for the UI update.
My current setup:
In my model:
Create a multidimensional NSArray to
hold my data. (each sub array
represents a section).
Place a NSDictionary in each section
array to represent the data for an
individual cell.
In my View Controller:
Create a multidimensional NSArray to
hold my Cell Controllers (mimicking
the data structure).
Assign each model dictionary to an
ivar in the appropriate Cell
Controller.
Hook up the data to the cell
This gets my UITableView on screen with cells properly displayed. Now if I want to filter the data in the table I do the following:
In my model:
Create an instance of NSPredicate.
Filter each subarray of the
multidimensional array.
Set a flag in each dictionary that
is within the results of the
filtered array.
In my View controller:
I create a new Multidimensional
NSArray. This array will hold the
cell controllers of the "filtered
data"
Loop through the current
Multidimensional Controller array.
Add it to the new "filtered array if
it's model's flag is set.
Replace the unfiltered Controller
array with the new one.
Update the table.
This also works very well. As long as I don't mind the UI updating instantaneously. If I want to delete rows with animation, I must pass those indexes to the table to be deleted.
To accomplish this, I add a few more steps to the view controller:
In my View controller:
I create a new Multidimensional
NSArray. This array will hold the
cell controllers of the "filtered
data"
Loop through the current
Multidimensional Controller array.
Add it to the new "filtered array if
it's model's flag is set.
Create a new NSArray to hold indexes
to be deleted.
Loop through the current
Multidimensional Controller array.
Add its cell's index to the
"toBeDeleted" array if its model's
flag is NOT set.
Replace the unfiltered Controller
array with the new one.
perform
deleteRowsAtIndexPaths:withRowAnimationn.
So far this method doesn't work the greatest. I am having issues deleting the rows with edge cases (empty sections and all visible cells being deleted).
This made me think maybe my methodology is flawed. Has anyone implemented this in an app? How did you solve this? Do your row animations work?
Not to make matters worse, but have you tried NSPredicate on an actual device?
I found that NSPredicate worked on the Simulator, but much like NSXMLDocument, wasn't available on the device itself (2.2.1).
update: Given that NSPredicate is available, all I can add is that I've had unpredictable results with deleteRowsAtIndexPaths:withRowAnimation as well - I had to settle for calling reloadData and having a non-animated transition.
Fortunately, since I have a highly "subtractive" filter in my app, there's typically a lot of movement, so it's obvious to the user that something has happened.
In your case where visible changes are potentially non-obvious, I suspect that from a usability perspective, it might be sufficient simply to use an animated UIImageView moving from north to south across the UITableView area. (a challenge in it's own right, unfortunately).
This would have the added bonus of confirming the filter's application to the user even if no visible changes occurred.