I have NSFetchedResultsController like datasource of my UITableView. It displays some entities with predicate from my database. I try to find an elegant solution to insert utility row between my data rows. I don't want to create fake entity in my database cause I don't want to mix View and Model. But I need to have ability to recreate this utility row (e.g. on other application launch). Any suggestions?
It should look something like this:
You're best bet, in my opinion, is to use a section header or footer for that "utility" row. In the case of Tweetbot, they're most likely caching results locally and then merge in data when the plus button is tapped. Your table will take multiple data sets as arrays (an array of arrays) and treat each separate array as a chunk and put it into its own section.
Any way you implement you'll want to wrap your results from the database with some sort of metadata. I think you're going to have to get away from a fetched results controller, unless you use a separate instance for each chunk, keeping track of the date range for each chunk.
Related
I have been looking around everywhere online and can't seem to get a solid answer. If I have an event with multiple attributes stored in Core Data in an array and I can display all of these individual events in a tableView. Lets say one of the attributes is a date. Is there away to search for all of the events that contain a certain date and display only those in the tableView? I was thinking of using NSPredicate from what I am seeing online but I am not familiar with this. Maybe somehow find the index of the event that contains that date and only display that index? Any ideas?
Table views are designed to implement a one to one relationship with the underlying array of data. The usual approach is to use a fetchRequest to only get the data you want to display ( I personally like to have all my requests pre-defined in the data model so I can keep track of how data is accessed).
But, if your array of managed record must contain more elements than what you want to display, you should consider basing your tableView datasource responses on an intermediate array that only contains (or refers to) the objects you want to show.
For example:
let allEvents:[EventRecord] = GetAllEvents()
var filteredEvents:[EventRecord] = allEvents.filter({ $0.eventDate.isEqualToDate(dateToDisplay) })
and use filteredEvents as your underlying store for your datasource.
This will make it easy to dynamically change the filtering conditions and reload the tableview without having to go back to the database for each filter change.
I have a view that contains a tableView. I'm using NSFetchedResultsController to display my results from Core Data.
I'd like to add a UISearchController (not a UISearchDisplayController as this one is deprecated in iOS 8) but I don't know how to link them.
Should I have only one NSFetchedResultsController or two?
I guess there is a way to fetch all the data with the NSFetchedResultsController and then just sort them according to the UISearchController am I wrong?
Thanks for the help you can give me on this.
A few thoughts...
When you create the resultsController for the searchController, you can pass to it the data that you wish to search. If you are using an NSFetchedResultsController in your main table, you could pass the fetchedObjects array. Then in response to changes to the search text, you filter the array (you could use a predicate and filteredArrayUsingPredicate to create a separate array with the search results, or you could iterate through the array to build it). The disadvantages of this route are that (unless you implement it manually) the search results will not be broken into separate sections, and the search results will not update automatically if the underlying data changes (eg. on a background thread).
I guess you could have a second NSFetchedResultsController: that would facilitate using sections, and could potentially permit the results to be updated automatically (using the delegate methods) if your data is being updated in the background, for example. But I would be nervous of the complexity that it introduces.
Another option, if you opt to apply the search in situ (i.e. specify resultsController = nil), would be to use the search criteria to update the NSFetchedResultsController itself (i.e. amending the underlying predicate and reperforming the fetch). That way your search table looks and feels exactly like the main table (including sections, if you use that) but the rows displayed obviously reduce as the search criteria become finer. This option needs care to ensure that the FRC is properly rebuilt, and might be unacceptable performance-wise, if you have a large dataset.
I have a UITableView with data coming from NSFetchedResultsController.
Here is my tablewView:
I need to add a row "All types". It also needs to be:
Sortable with all other items
Selectable (Design is now selected)
Selecting "All types" should deselect other rows
Give something to understand that it's an "All types" row when selected
I've read Add extra row to a UITableView managed by NSFetchedResultsController and NSFetchedResultsController prepend a row or section. Given approaches makes impossible to sort data or will look so hacky and produce so much hard-maintailable code, that it will be impossible to change logic and maintain code.
Are there any other good options?
PS. I understand, that my question may sound "broad" and doesn't containt code, but I think it's very common problem.
I do not think this is a very common problem at all. I can see it seems natural to do what you are trying but lets analyse your situation: What you generally have are 2 arrays of objects which you wish to sort as a single array. Now that is quite a common situation and I believe everyone knows how to solve this issue. You need to create a single array of objects and then sort it.
The way I see it you have 3 options:
Fetch all the items, merge the 2 arrays, sort and present them. This is not a very good idea since your memory consumption can be a bit too large if there are a lot of items in the database.
Put the extra data into the database and use a fetch result controller as you would normally. This should work good but you will probably need to mark these items so they are later removed or keep it in the database but ignore them where you wish not to display them.
Create a temporary database combined with what needs to be fetched from the database and your additional data. This approach is great if your data are meant for read-only in this list (which actually seems to be the case in what you posted). Still it is best if you create some kind of link between the objects. For instance some kind of ID would be great, this way when user selects an object from the second database you simply read the ID and fetch the object from the original database.
I have one section of a table which will display an initial set of comments, and then as new comments are entered and merged into core data, it will display those as well. I could simply tack all the new comments onto the end of the current array using a standard fetch, but I would also like to handle any potential comments that were added in the meantime, and may be mixed into the sort order of the current comments.
I was trying to decide how easiest to do all of this with NSMutableOrderedSet, but really nsfrc already does everything I need, except it works with multiple sections.
Is it wise to try and shoe-horn that in there?
If you are using Core Data, NSFetchedResultsController should probably be used for all your table views which display core data entities. Even those which are not updating in the background.
In this case you do have things being merged in the background, so NSFetchedResultsController is the logical and correct choice.
Setup: I have a UICollectionView or UITableView that’s backed by a simple array data source. I keep a copy of that data source in the controller.
Now, I get a notification from the system that there’s new data available. I get a new array where items may have been added, removed and changed positions.
So now I have two data objects:
previous array that's in sync with what the UI is currently showing
new array where items have been added, removed, moved
To get the UI in sync with the new array, I need to generate a bunch of UI calls. In case of UICollectionView, those are
- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths
And there’s a similar set of methods for UITableView.
I specifically don’t want to reload the whole table because that’s more expensive than just working with a few items.
So, the question is: given the previous and new data source array, how do I generate the correct set of UI calls, and when do I "swap out" my old data source for the new?
I'd see this as largely equivalent to the diff / patch problem, where the aim is to find the minimal number of changes between one text file and another and then to apply those changes. In that case, the implementation defines the operations:
add or insert
delete
change
... but not move. The reason for omitting move isn't immediately obvious to me but I'd strongly suspect that including move would require a very costly computation to find the optimal movement.
So, if we restrict the operations to those listed above, the Hunt-McIlroy algorithm described in An Algorithm for Differential File Comparison, or one of its descendents, would find a close-to-optimal set of changes.
The difference between your problem and the classic diff / patch is that you have a two-dimensional table, while diff / patch deals with a one-dimensional set of items (lines of text). The best way to convert the 2-D problem into the 1-D problem would depend on the particular characteristics of the changes that tend to be made in your data table.
For example, if the table is n rows by m columns and changes tend to be grouped in rows or rows are inserted or deleted as a whole, then you would likely be best to consider the table as if it was a text file and do the diff line by line. Alternatively, if changes tend to be grouped in columns or columns are inserted or deleted, you could do the diff column by column. If the alterations include inserting or deleting individual cells (which result in the subsequent cells shifting right or left as a result), you could treat the table as if each cell in the table was on a separate line of the text file, linearising the table in either row-first or column-first order.
Without knowing the details of your problem in this respect, however, my inclination would be to avoid premature optimisation. Thus, I'd tend to start by implementing the Hunt-McIlroy algorithm row-by-row if m < n, or column-by-column if n < m, then profile the application in use for a while before deciding that either a more sophisticated version of the algorithm or an alternative mapping of your problem to the Hunt-McIlroy solution was warranted.
A good discussion of a variety of diff algorithms can be found here on stackoverflow.
I have worked on an app that deals with a very similar problem that i was able to solve. I was expecting a complicated solution but it was really simple. Here is how you can solve it:
1) Create a new array in which you will receive new items and call it let us say 'moreItems', also synthesise it of course.
#property (nonatomic, readonly) NSMutableArray *moreItems;
2) In viewDidLoad of the ViewController that is linked with your TableView or CollectionView, alloc/init this array:
moreItems = [[NSMutableArray alloc] init];
3) Now you need to add your existing array let us say it was "items" to the new array that you have just created named 'moreItems'. You can do this using 'addObjectsFromArray'
[moreItems addObjectsFromArray:[channel items]];
'channel items' contains the objects it has received earlier and it will add these items to the newly created array called 'moreItems'. I am assuming you are collecting data from some web service, so you can implement this step in connectionDidFinsihLoading.
4) Now change your data source of TableView/CollectionView and replace it with the new array that is 'moreItems'
5) Next issue is that you don't want to reload the whole tableview and want to deal with new items. To have this functionality, you need to persist items. You can use archiving or core data whatever you are comfortable with.
6) The items that are already fetched, you will have to persist them let us say with archiving. And show them in the tableview when the user opens the app while it grabs more items that are updated on the web service.So first show the persisted items immediately and then handle the new items.
7) You need to look for some unique object, in my case it was the 'link' as every item has a different link and i can sort and handle them on this basis. You also need to use
- (BOOL) isEqual:(id)object
to compare the links on the web service and the links that are already in the tableview. This step is necessary because then your app won't add the items with links that are already in the tableview.
8) If there is some date associated with each item, you can use that date to sort them and show the new ones on top by using 'sortUsingComparator'
9) Also you need to use "[[self tableView] insertRowsAtIndexPath:rows withRowAnimation:UITableViewRowAnimationTop" to have that effect that shows user the new items have been added on top of the earlier ones.
Hope this helps.
I specifically don’t want to reload the whole table because that’s
more expensive than just working with a few items.
Are you sure this is true? Unless you have pathological case where every single cell is visible on your screen and there are hundreds of them, I find it hard to believe how will the "efficient" way be any faster than the "brute force" way.
Calling reloadData will only ask your delegate to update those cells that will end up visible on screen, it will not cause the entire table view to recreate every single of its cells.
If you're implementing some methods that change the complexity of calculating dimensions (like tableView:heightForRowAtIndexPath) you won't get any benefit using "efficient" way since the table view will have to recalculate all its heights anyway.
I'm not sure if solutions you are looking for will fix the problem, but let's just hope that I'm terribly wrong.
If your data source already has diff information, UITableView reloadRowsAtIndexPaths:withRowAnimation: will make it less-performance intensive: