For an app I am creating I am looking for the correct way to display a medium sized records set (around 130 records, with about 13 columns). The data is now in a sqlite database, which I want to use to display in a UITableView. With this quantity of records and columns I think an array is way too memory intensive?
From looking around (and previous experiences) I think I best go for Core Data and use an NSFetchedResultsController, but I am not sure about this. There is no need for the data to be changed or added. So using this might be overkill?
If this is the way to go though, I would love to know what components to use, as I cannot really find an answer to this sort of question.
If this is not the way to go, please point me in the right direction in which I should go.
Core Data is a good match. It can also be used with sqlite as a datastore however you would probably have to reimport your data. The NSFetchedResultsController makes it fairly easy to use a core data collection as a datasource for the table-view. The modeling tools in XCode make it also pretty convenient to create your data structure and creating the NSManagedObject subclasses from it. I would also take into consideration that you might want to extend your dataset or structure in the future and core data has battle tested facilities to assist you with these tasks.
There is a little trick, you needn't select all data from this table and transform into an array, just select data like this:
SELECT `recordid` FROM `yourtable`;
in the delegate method "tableView:cellForRowAtIndexPath:", you can now load all rest data from this record:
SELECT * FROM `yourtable` WHERE `recordid` = [indexPath row];
If your database records is still growing, you can fetch your data by paging query:
SELECT * FROM `yourtable` LIMIT 10 OFFSET 0;
The answer depends on what you need. In particular, if you have to release the application in a day or so, well, refactoring to Core Data would be an obstacle. It has a very steep learning curve so it cannot be digested in a day. On the contrary if you have more time to spend, yes go with Core Data and reuse the know-how in the future.
So, stick with two options here. Stay with plain SQL or just migrate to Core Data. Well, if you have chosen the second, the master component you need is the NSFetchedResultsController.
This class works in combination with UITableView offering a lot of functionalities. One of the most important is to maintain a small memory footprint. In fact, if you work with batch sizes you can take advantage of lazy loading mechanism. Say for example, you want a batch of 20 items. The NSFetchedResultsController will load the first 20 items, if you scroll down, it will load other 20 and so on.
The main problem will be not how to organize your internal data structures, but how effectively present 13 columns of to the user, taking into account very little screen real estate you have on iPhone. Have you thought about that?
There are few options here
you can present just some kind of brief information into main table view and for each cell have detail view with the rest of the information
you can dynamically generate bunch of labels inside each cell and populate them with your data, but now you're really facing space limitation, since you probably won't fit that much of a data in one row. And you also need to create custom headers in your table view (if you need to show header)
or you can invent something else. When I had to deal with very similar issue I went and implemented something similar to Excel "freeze columns" functionality - where header columns is fixed and stays on the screen always and all data columns can be scrolled horizontally and basically go underneath the header. See screenshot attached (note scroll bars around columns with stat data):
Related
Consider a chat application scenario, where you have a very large group with 100,000+ messages and realtime communication.
Just like most chat applications, we want the latest messages to appear at the bottom i.e. new items are added at the bottom.
What's the best practice for using lazy loading with NSFetchedResultsController? Changing the fetch-request by increasing the fetchLimit doesn't seem like a good idea. Also, using an extra array instead of fetchedObject also doesn't seem like a very elegant or convenient solution either.
What's the best practice for showing latest messages at the bottom i.e. reversing the UITableView direction? Transform doesn't seem to be an elegant solution, or is it?
Looking for an elegant solution, that's working for people. Please advise.
fetchLimit doesn't work with NSFetchedResultsController. To limit the controller you can do the following:
Do a single fetch with a fetchLimit=1 and a fetchOffset=BATCH_SIZE (where the batch size is something large, but not huge = ~200)
get the date of that message that you fetched
limit the fetchedResultsController to the date of that message.
Now you have a fetchedResultsController that is 200 messages. Note that it may increase in size as long as it is open.
When a user scroll back you can do some similar adjusting of the fetchedResultsController by doing a fetch to figure out a correct date range.
For display the cells I have used the double-inverse method (apply a 180 rotation to the collectionView and a 180 rotation to every cell). It is not that elegant but it works and it is not as expensive as it first seems - the whole screen is already in an openGL layer behinds the scenes anyways. There are a lot of little things that it caused (like the scrollIndicator being on the wrong side), but also a lot of little things that it fixed (like dealing with a chat with very few messages). If I had to do it again I would make a custom layout, but I wouldn't be so quick to dismiss the double-inverse method.
This is a pretty basic question, but I have searched all over for an answer that caters to my situation and found little.
I'm using ECSlidingviewController to make a left drawer/menu control like Facebook, Pulse, and many others. (Actually quite surprised this isn't part of the Apple SDK)
When I select an option from my menu controller, it pops the current top controller off, and pushes the view controller selected by menu. My main view is a tableview of local places, and its quite expensive/time consuming to retrieve local spots from a server every time the view is loaded, so I'd like to save the last loaded collection object (My object that wraps a dictionary with simple accessor methods).
I've explored NSCoding/NSKeyed* and Core Data so far and NSCoding seems like a good plan, but I'm not sure if it would be suited to 30 entries of custom objects (each has 640x320 picture and a few strings). At the same time, using core data for a simple collection object seems like building a mansion for a closet. I've also considered trying to keep the view controller around in memory, but that seems way too wasteful. Also, NSUserDefaults doesn't seem to fit the job either. Would the UIKit state preservation techniques work even when the app is in foreground? And finally- would NSURLCache be a good solution to saving the tableview data?
All I want is a quick and standardized way to reload my tableview's last data source without any network requests, or 100 lines of code.
I'd use Core Data and a fetched results controller. If you start off with a master detail template for this then the code is all generated for you. The main benefit is that the saving is transparent for you and memory management is also transparent (you just set the fetch request page size to a suitable value).
A solution using NSCoding will work, and for 30 results may well be faster. If you know you'll never have more than 30 then it could be attractive. But you need to code (and save) and decode yourself and you still have basically the same table data source code.
I am using core data in my app to store entities that could have as many as 50k objects or more. I have this paired to an NSFetchedResultsController in a table view. The table view works fine due to cell reuse however my biggest problem is queuring the actual database to get the dataset.
When i first load the table view i need all results from the db. I am using the default fetch request with a single sort descriptor and I have set the batchSize to 1,000. On an iPad 2 this query takes up to 15 secs to finish! I also have to run this query after a search has been cancelled so overall it makes the app unusable. My assumption is that CD still has to resolve all those results or setup the sections or something, i really have no idea but just using the batchSize doesn't help?? The content is also very dynamic in the sense that new rows are always getting added, sort order changing etc.. so caching has a limited benefit.
I am thinking now that the best option would be to use a fetchLimit in the fetchRequest and then implement some basic paging. When the table view scrolls to the end fetch the next "page" of results? My only problem with this approach is that i lose the sectionIndex and i cant think of any way around that.
Anyone have any ideas or dealt with this issue already?
When you set the fetch request for the FRC the batch size should be just a few items bigger than, maybe twice the size as, the number of items that can be seen on screen at any one time. The FRC already does the pagination for you you just need to set the page size better.
s.newave,
Do your rows have variable height? If so, then the table view asks you to calculate each height and that causes every row to be fetched. 15 seconds is not an unreasonable time to fetch 50K items.
The bigger problem is your statement about not wanting to change your design. Frankly, a 50K item tableview is useless. You should change your design -- not because CD is slow, it isn't -- but because your design is not pragmatically usable.
Andrew
P.S. The fetched results controller is designed for mainstream applications. a 50K table view is not a mainstream app. If you insist on keeping with a 50K table view design, you will have to make your own controller.
When building a view for an iPhone app, one must consider how variable data will be determined by the view. Two design options jump readily to mind:
An NSArray of items
A dataSource property, which implements a protocol and returns the items.
The former is used by views such as UITabBar, while the latter is used by UITableView. What are the pros and cons of these options? Is there a reason for the two distinct paradigms, or is one universally superior?
It's mostly about the amount of data and limited amount of memory in relation to simplicity.
Simpler is always better if you can get away with it. A tab bar probably have less than 10 items which is no problem to hold in memory at once so the simplest solution is the best.
A table view however may have thousands of rows that may contain expensive data like images. Therefor it has a more complex design to be able to keep only the necessary data in memory.
I am currently working on an iPad application that uses a table view to present data, I was inspired by the iTunes application in iPad that present it's data in multiple columns in a very nice and neat manner, and the most interesting thing is that during the portrait mode the itunes application displays data in 2 columns but when the user switches to landscape mode, it switches the display to 3 columns (since there are plenty of space to display data horizontally).
This is what i'm talking about:
but i found out that iOS SDK only supports single column for tableview (it would be nice to utilize the entire space provided on iPad screen to present data), i did some research and i found out that the best way to present data in multiple columns yet like spreadsheet style is to use datagridview instead, but iOS SDK did not provide any data grid view controls for iOS developers.
I found out over the internet some customized tables like:
AQGridView.
DTGridView.
and also the one from this:
http://usxue.is-programmer.com/posts/14176.html
and the one from this:
http://xebee.xebia.in/2011/04/14/building-editable-gridview-for-iphone-apps/
But sadly none of these ever met the requirements of the application i was working on.
Could you guys provide me some ideas or share some sample codes or links on how to display data in somehow-data grid view, to achieve similar effect used in iTunes application (as shown above).. Any form of help would be pretty much appreciated. Thank you guys!
The summary answer is, place multiple data "views" across in a single cell.
The more detailed answer:
Create custom views that represent the single cells you want. You can for this purpose make them resizable enough to work two across or three across (they will get loaded into 1/2 or 1/3 of the cells bounds).
Then make a custom UITableView cell, that can take two or three data items - load up an instance of the custom view previously created in the cell for each data item you have, placing them next to each other. You can have the cell do the view layout when groups of data items are added.
In the cellForRow code in the table delegate/datasource, you use your data source in groups of two or three (and report the row count accordingly) to pass along to the custom cell.
Sorry I can't share code, but I have used this technique before in other applications.
What's wrong with creating a UIView class to represent a single cell, and another that lays out an array of those cells in a grid? Put your grid view in a UIScrollView and you're about done.
UITableView is obviously a pretty complex class. Much of that is to make it very general, very reusable, and able to support a huge number of rows. Your class doesn't necessarily need to be that complicated -- if you have a fairly small number of cells, your "grid" could really just be a UIView in which you lay out cells in rows and columns. UITableView removes cells that aren't seen in order to save memory; you might need to do something similar if you have hundreds of cells, especially if they're large, but a few dozen probably won't require that.
In short, since you need a grid view for a particular use, you don't need to do all the extra work that would be required for a general, reusable solution.