NSTableView load image only when cell comes into view ?

With an NSTableView, is it possible to load images only when cell comes into view ? Also maybe unload when out.

Accepted Reply

>> So you saying it is possible but as Cell not View based ?


No. It's not something even to consider, because it's NSCell-based, but even if it were available, it wouldn't solve the problem (because it doesn't tell you when the table cell is no longer needed for display).


As I said before, NSTableView already contains the functionality you want, except that it will keep a couple of extra non-visible cells around, in line with its own caching heuristic. So the built-in functionality won't strictly limit the images to what's actually visible, but it will be close.


But you do have to be careful how you implement your part of this. You don't want to keep the images in your data model, since that will obviously end up keeping all images in memory. Instead, you should keep URLs to the images in your data model, then load the images when needed to populate a cell (that is, in your tableView(_:viewFor:row:) delegate method).

Replies

Not sure if this is the right func it's the closest I could find but doesn't seem to be called ... func tableView(_ tableView: NSTableView, willDisplayCell cell: Any, for tableColumn: NSTableColumn?, row: Int)

No, that's not the right method. It's for NSCell-based table views only, which are deprecated.


There's no simple answer to your original question. NSTableView itself manages the lifetimes of table cells, creating them when they are needed for display, and discarding them when they're scrolled out of the view. It's a bit more complicated than that, because the table view typically pre-creates a row or two above and below what's visible (or may choose a different strategy when the user is scrolling fast through the table).


Usually, this behavior is near enough to "when the cell comes into view" that you don't need to do anything yourself. Are you saying you have a use-case where the existence of a cell must track its visibility more precisely than this?

Thanks for the response.


The problem is holding hundreds of images in an array, so thought maybe just have ones that are viewed to be loaded as and when, and not worry about the rest. I look at iTunes for what may be possible and noticed (am pretty sure) that it is.

Sorry if I missed it - are those images in the app, or downloaded remote?


async loading and the use of thumbnails might help in your example. You might want to search here on: lazy table loading

Forgot to add ... So you saying it is possible but as Cell not View based ?

>> So you saying it is possible but as Cell not View based ?


No. It's not something even to consider, because it's NSCell-based, but even if it were available, it wouldn't solve the problem (because it doesn't tell you when the table cell is no longer needed for display).


As I said before, NSTableView already contains the functionality you want, except that it will keep a couple of extra non-visible cells around, in line with its own caching heuristic. So the built-in functionality won't strictly limit the images to what's actually visible, but it will be close.


But you do have to be careful how you implement your part of this. You don't want to keep the images in your data model, since that will obviously end up keeping all images in memory. Instead, you should keep URLs to the images in your data model, then load the images when needed to populate a cell (that is, in your tableView(_:viewFor:row:) delegate method).

Just to clarify ... "But you do have to be careful how you implement your part of this" not sure what you're referring to. I've set it up as suggested not keeping the images in a data model but load from an URL when "viewFor" is called, but they still load before viewed.

>> not sure what you're referring to


Just what you said — making sure you don't accidentally load all the images.


>> they still load before viewed


Depending on where the URLs lead, some may actually get loaded before their row appears onscreen, because table view does some things behind the scenes. That load might be finished before you actually see the rows.


I suggest you try logging the URLs when you load the corresponding image, and you should get a clearer idea of what's going on. As I said before, the table view might try to anticipate cell loading, so it might load a few rows ahead of time. If your logging shows *all* images getting loaded, you will need to start debugging with your delegate code to try to figure out what's gone wrong.

I had a look at the Memory Usage in the Debug Nav and as I scroll it does increase the size, so I guess it does kinda work. No idea how to do it but shall have a look at the logging, thanks.


Do you mean by using Instruments ?

I meant by logging in your "viewFor" method, since that's where you need to fetch the image.

Sorry, still not sure what you mean by logging ?

I mean using NLog or (since this is Swift) just print:


     print("Creating image for row \(row)")


When you first run the app, you should see one of these for each visible row, plus perhaps a couple more. You shouldn't be seeing any more than that until you scroll.

Yeh thanks it does load as I scroll down didn't notice at first. Just had to check what you meant as things can be logged in different ways. I wonder if there is any way to specify how many rows ahead.