On iOS, an NSTableView (UITableView) is loaded lazily, in the sense that the table only displays the cell that are needed for the current view and not more. As the user scrolls up or down, more information is loaded from the data source.
Is there something similar for OSX? I have an NSTableView with 1000+ records to load (+multiple columns) which is resulting in laggy loading as well as lag when scrolling up or down.
Would this 'lazy' loading be a good solution(if possible)? Or maybe something along the same lines?
Thanks.
P.S. Regarding the loading process - I use the two usual methods
-(id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
and
-(NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
to load the data. Within them there are if statements to check some variables, since I have 2 combo boxes that contain data, that when containing "ALL" and "ALL" load these 1000+ records. If the combo boxes are not "ALL" and "ALL", then only a couple of records are loaded, which load just fine.
I think the problem, given that you have explained that lazy loading is done automatically, is that I am loading the data from XML files, so maybe the parsing is taking up a large chunk of processing time.
I though of loading the data from the XML file into an NSDictionary at runtime and keeping it in memory, available for use when needed, so that I can avoid the loading time when the information is actually needed to display.
What do you think? Thanks!
You definitely want to cache your XML file into some sort of data structure. The tableView:objectValueForTableColumn:row: method will be called once for every visible cell. If you have a lot of columns, it's easy to have 200 cells visible. Page down, and those cells all change, your XML file loads 200 times, and you get laggy scrolling.
Even if the XML file is only loaded and parsed for every cell in a specific column, it will be slow and should be fixed.
You can verify that the caching is necessary by making tableView:objectValueForTableColumn:row: return nil where it would otherwise parse the XML file. Once you make this change, does it scroll smoothly, or do you have other problems? It would obviously make the application unusable, but you should always check if an optimization is necessary and sufficient before implementing it.
Related
Is there a memory issue regarding the number of items in the list I am displaying in my table view? If the user keeps adding items will the application eventually crash or something due to memory issues?
To answer your questions:
There's no black and white answer to this. To try my best to explain, think of UITableView as sort of like a visual data array. In fact, most people (myself included) use a source data object like an NSArray or an NSDictionary to provide the display data for a UITableView. All the memory limitations that would apply to these objects (arrays and dictionaries) apply to your UITableView, assuming your UITableView is set up properly and you're using the recommended techniques for reusing cells. What this boils down to is: Is it possible to have a very large UITableView? - Yes. How long though, this I don't know. I've created UITableViews with complex subclassed cells and 200 rows and they worked fine. It depends on how you set up the table and the data source you're using. Bear in mind again that the limitation is due to the data source. Have an extremely large array and eventually your device will throw a memory warning. As a best practice, use pagination. There are tonnes of tutorials online to enable paging on UITableViews. Lazy load your images (if any) if they are being downloaded.
Yes you can. You can do lots of amazing things if you're creative enough while subclassing UITableViewCells. Otherwise, you can use the standard UITableViewCell as well. There are two labels on there: The textLabel and the detailTextLabel. Use these two to display the data you want.
Your UITableViewDelegate has a didSelectRowAtIndexPath method which you can implement. As long as your view controller housing the UITableView is set to be it's delegate, it will respond to didSelectRowAtIndexPath.
Just empty the array you're using as a data source (bear in mind that your numberOfRowsInSection data source method MUST use the array count) and call reloadData on the UITableView
EDIT: The question got edited, so only point 1 from the above 4 points applies to the question :) The others are nice to know though
Nothing table-specific, but yes, you will run out.
The most important thing is probably to use dequeueReusableCellWithIdentifier: in your tableView:cellForRowAtIndexPath: delegate method. This basically cuts down memory usage to the number of visible cells (plus one being scrolled into view).
I have an application with lots of view, about 20, accessible from two main menus, in each view there are labels, textfields and buttons, over a background image.
As data i've a global class and some array filled by xml files.
If memoryWarning fires, I should release what is not necessary, but if you are in one view, all in the other view is already deallocated right? What should I deallocate? Removing the global class i will lose essential data, the same for array.
Suggestions? (Sorry for bad english).
The views you have left are not necesserily deallocated, it all depends on the code. So you possible hold everything in the memory at the same time and memory is a scarse resource on iOS. So if you have a lot of views deallocate those that you don't currently use.
After receiving memory warning you should remove everything that you not need. Keep an eye on those XML files in arrays, XML files tend to be big: as the source and as a DOM tree in the memory after being parsed. You can try to make them all being lazy loaded and parsed, throw them away if not more needed, reload if needed again (you have then to save their loading state, nut this could be a simple array of boolean value instead of whole XML data).
I need to load data dynamically as a user scrolls through an NSTableView. For example, the table might display 50 rows, and as it scrolls further I need to fetch more data from the network. The number of of objects/rows is known beforehand, so I want the table to have the right number of rows from the start, but showing empty cells while data is loading.
I'm using Core Data so it's easy to connect the table to my model using bindings. This would also take care of cells being updated as data comes in and is parsed. I've tried to figure out how I could do this by subclassing NSArrayController but from what I can tell there is no information flowing from the table to the controller about which rows actually need data. Therefore, I'm thinking of implementing NSTableViewDataSource instead, where I can easily check if the table has scrolled beyond the rows for which data is available. On the other hand, I don't know if I will get cells automatically updating as easily with this solution.
In case anyone comes across this, here's my own answer:
Yes, you need to implement NSTableViewDataSource on a controller, observe changes on the data and call reloadData manually on the table when changes occur. The main reason for doing this is that you can defer loading of data until it's actually needed (when the table view scrolls). Using the data source protocol keeps you informed of which indexes are requested.
I have two very simple, identical UITableViews in my app that are populated with thumbnail images named "thumb1.jpg", "thumb2.jpg", etc. These thumbnails have associated original images "1.jpg" and text files "1.txt" used for image processing. Everything is stored in the app's Documents folder.
I want to keep the numbered, ordered naming for these files since it makes displaying the thumbnails in the UITableViews very easy with cellForRowAtIndexPath. I'm currently using a NSMutableArray (index: 1 object:"thumb1.jpg", etc) to track all images in the app.
The issue is that users can add/delete images so maintaining the order is important. For handling adding/deleting I'm looking at using insertObjectAtIndex and removeObjectAtIndex on the NSMutableArray, which will maintain order but will require programmatically changing image and text file names when this happens. For example, if there are five images in the array "0.jpg","1.jpg","2.jpg","3.jpg","4.jpg" and the user deletes the second image ("1.jpg") the array will now have "2.jpg" at index 1 so filenames will have to be changed to "1.jpg", "1.txt", and "thumb1.jpg".
How does this approach sound? I'm new to Objective-C so if you have other functions you'd use, etc I'd be interested to hear your opinion.
Renaming the actual image files themselves doesn't sound like a good idea.
I would implement 2 NSMutableArrays, one to hold the title/description of the image and one to hold the filename (or, if you wanted, the actual UIImage instead). Then if you need to delete, for example the item at index 2, deleting the same object from each of the two arrays will then leave them in sync.
If you start wanting more and more things to be stored for each row, I suggest you implement your own class. You can then implement an array of multiple instances of that class, and each class would have properties such as image, text, thumbnail etc. In fact, because you've already named three separate properties (main image, text and thumbnail) I'm tempted to say you should implement a custom class straight away.
Let me know if this makes sense or if you'd like some code to further illustrate it.
I have a UITableView that show a long list of data. Use sections and follow the sugestion of How to solve slow scrolling in UITableView .
The flow is load a main UITableView & push a second selecting a row from there.
However, with 3000 items take 11 seconds to show. I suspect first from the load of the records from sqlite (I preload the first 200). So I cut it to only 50.
However, no matter if I preload only 1 or 500, the time is the same.
The view is made from IB and all is opaque.
I run out of ideas in how detect the problem. I run the Instruments tool but not know what to look.
Also, when the user select a cell from the previous UITable, no visual feedback is show (ie: the cell not turn selected) for a while so he thinks he not select it and try several times. Is related to this problem.
What to do?
NOTE: The problem is only in the actual device:
iPod Touch 2d generation
Using fmdb as sqlite api
Doing the caching in viewDidLoad
Using NSDictionary for the caching
Using a NSAutoreleasePool for the caching part.
Only caching the row ID & mac 4 fields necesary to show the cell data
UIView made with interface builder, SDK 2.2.1
Instruments say I use 2.5 MB in the device
The -[FMResultSet next] call can be a very expensive call to make, depending on the data that's getting loaded. It'd during this call that sqlite is actually going to the database, finding the next row to return, and giving you back the appropriate fields. It's not just an enumerator.
You might want to consider pre-caching all of the data before actually displaying the table. This means that you would do all of your FMDB calls before the table gets shown on the screen.
If that takes too long, you might want to show the tableview with its initial rows, and then use NSOperations or just a second thread to load the data in the background and cache it that way.
Without seeing any code, I think I would be inclined to think that you need to index you tables.
You should see an enormous speed up by adding an index on the attributes you query on. You can do this by using the CREATE INDEX SQL command.