5

In relation to How to determine if a user has scrolled to the end of an NSTableView Thanks Josh.

Is there a way to use this mechanism to implement a NSTableView that provides some sort of infinite scroll or pagination.

The idea is to tell NSTableView to load up to a certain number of records, say 1k records at once and than as user scrolls closer to the end pull another 1k records and maybe forget the first 1k records.

This pattern is well defined/used in web applications and java. Only the visible number of rows is loaded initially and the rest is pulled async as user scrolls up and down the table.

I am interested in some obj-c code or tips on how to code this. I know about filtering/limiting the number of records that go into the tableview but lets ignore that for a moment.

Thanks.

Community
  • 1
  • 1
Klajd Deda
  • 355
  • 2
  • 7

2 Answers2

3

Given the details you've provided, I'll generalize a bit but here's how I might solve it:

First, I'd set a MUCH SMALLER batch size than 1000 records. If the result count or "the most anybody is ever going to want to see" is indeterminate (and it sounds like it is in your case), the user probably doesn't even care past the first 100 or so. If your user often requests a large, expensive list and immediately wants to see stuff so far away from the beginning they hurl the scroller downward for two minutes straight before they stop and look around, perhaps a more intuitive sort order is needed instead of asking Google Image for 1000 more animated kitten gifs. ;-)

The controller behind the (definitely view-based for view reuse) table view will need some sort of request queue since I assume you're batching things in because they're expensive to retrieve individually. This will manage the asynchronous requesting/okay-now-it's-loaded machinery (I know that's vague but more detail is needed to get more specific). You'll make sure any "currently alive" views will somehow get this "it's ready" notification and will go from some "busy" UI state to displaying the ready item (since we NEVER want to keep the table waiting for a ready-to-display view for the object at a given row, so the view should at least show some "still waiting for details" indication so quick scrolls over lots of rows won't stall anything).

Using a view-based NSTableView and associated data source methods will let the table view handle only keeping enough copies of your custom NSTableCellView around to reuse during scrolling. Since you have to provide a configured view when asked, the view's default state can either be "draw nothing if not ready" or some visually generic placeholder until the object is realized and ready (then you can just reload that row instead of the whole table). This way the table keeps scrolling and drawing rapidly because it doesn't care about what your controller is doing to fulfill the promise of updating the visible rows (that custom cell view of yours will observe its represented object's updates).

You probably want the scrollers to reflect the total number of rows batched in so far if the upper bound is astronomical - reflecting that size would make the scroll grip both tiny and very sensitive. Instead, just grow the scroller (via the table view's row count) by what the user has "requested" so far, all the way back to the beginning of the list. Any time more are batched in, you'll want to add the batch size to your controller's total batched row count. This still lets the scroller zoom by rows the user couldn't distinguish at that speed anyway. You communicate the row count change to the table view by sending it -noteNumberOfRowsChanged and replying to its resulting data source request ( -numberOfRowsInTableView: ) with the updated total row count you stashed in a property of your controller. It'll ask for views for the newly visible rows as needed (which will be in some neutral, unfulfilled visual state until it's realized as before), update the scroll view, lather, rinse, repeat.

You could use NSCache to keep memory usage low. Set its countLimit to several times your batch size and let it drop previous batches if it decides it needs to dump the first n model objects, then batch them back in if the table view suddenly asks for a view for a row no longer in the batch window's range.

Without knowing more about your requirements and architecture, it's hard to get more specific. If I haven't hit the mark, consider editing your question to include more detail. If I'm totally off base from what you're asking for, please clarify. :-)

Community
  • 1
  • 1
Joshua Nozzi
  • 60,946
  • 14
  • 140
  • 135
0

I know more about iOS, but I think the answer is similar. Table views are intrinsically finite, but you can roll your own custom scroll view to do this. The trick is to set a large content size and implement layout in your subclass (which will get called on every scroll change). In that method, check to see if the content offset is near zero or near the content size. If it is, then translate the content offset back to the center of the content size and translate all the subviews (keep them on one parent content view) by the same distance so the user doesn't see any motion. Make a datasource protocol and keep asking your datasource for "cells" that tile the visible part of the view.

It should be up to the datasource to recognize what we would have called a page-fault in the olden days, to decide that some of the model in memory should be discarded in favor of the model where the user is scrolling.

I poked around for an NS equivalent, but didn't see one on cursory search. Here's a decent-looking reference on the idea done in iOS.

danh
  • 62,181
  • 10
  • 95
  • 136