local async image loading... any possible hacks?

I have a dynamic side-scrolling image viewer that loads many images (maybe up to a hundred). The user can swipe between them and select one.

As of now, before the user can interact, all the images are pre-loaded and placed in a wide display container (mostly off screen obviously). This is not so smart I guess as if this image collection grows very large then I could be presented with memory issues. 

I could avoid this by simply loading a few images to the left and right of the ‘focus’ image and remove the ones outside of that. This would be fine if the user was swiping one at a time. The problem I have is that fast swipes will send the image container flying fast… again I could increase this pre-loaded amount to 10, but then another problem will likely occur. If I tell the program at any stage: “hey load another ten please”, the user interaction ( touching and animation ) will surely stutter or pause while these load making the whole experience a little unprofessional.

Async image loading would fix all of that. i.e start the loading AND allow the graphics to move about at the same time. I think we can do it with http image requests… but not when loading from a local directory? Is there a way to ‘hack’ this so I could async load from a local directory using the http api? 

Hope I communicated my problem and any advice would be appreciated. I know it’s probably a simple “no, it can’t do that”. Hopefully it’s something we can have added in the future. No fancy multithreading or anything - just assets being loaded into memory without disrupting Runtime “enterFrames” or “touch” events.

I love optimizing memory use, and I also love offering a smooth interactive experience.

Here’s a thought -

  • The tableView widget inherently does “lazy loading” – which sounds exactly like what you want. the widget only loads the images on the display (and one or two outside the display area) at a time, and it unloads them automatically as they scroll out of view. If I have a list of 1000 items with pictures, but only 7 fit onscreen, my print statements in the tableView onRowRender function tell me that only 9-10 images are actually loaded into memory at instantiation. As I scroll the list with my finger, the SDK calls to load up the new ones scrolling into view. It’s pretty sweet stuff, and pretty under-appreciated I think. (Throw a print into the onRowRender - it’s very comforting to know the sdk is handling all the loading/unloading magically behind the scenes)

I don’t know if the scrollView and other widgets are as sophisticated (lazy loading) - haven’t used them much / for this purpose.

It would be great if we could have a variant of tableView that scrolls sideways for this very reason!!!

I have worked on these kind of apps, not with Corona though. The standard solution, both in online and Appstore apps is to preload couple of images to the left and right as you suggested.

To combat fast scrolling, thumbnails are preloaded, they load without performance hit (not sure if this applies to Corona). Depending on size you could even preload all the pages thumbnails. So the images wooshing by are low res, then when you stop paging the hi res images are loaded in and around that area.

The quality of the thumbnails is very important for the user experience. To get highest possible quality on each platform is hard. Of course on iPad 3 you can afford to have much nicer thumbnails than iPhone 3g for example. 

Thanks jonjonsson for your reply. You seem to understand my problem well. In fact the solution you propose is the one I have been working on. I guess my real issue is the lag when the hi-res image is being fetched. As I understand it the Corona engine puts all Runtime and screen drawing on hold while a new image is being loaded. So really… I have to figure out a time the images are ‘still’ before I load this hi-res image. Problem is it can take a second or two and if the user tries interact during this time they will get no response - or worse it seems to kind of ‘cue’ up the users touches and then apply them afterwards.

Maybe there is no beautiful solution. I might just remove all touch listeners while the hi-res image is being loaded and give the user a quick bit of feedback “… loading” for that second they lose control. Am I making sense? Are the limitations I speak of a reality? Ideally we could make 'image load containers" that are free to animate and the images get loaded inside in their own time. I imagine this is how things can be done in Objective-C but Corona does not allow it.

Here’s a thought -

  • The tableView widget inherently does “lazy loading” – which sounds exactly like what you want. the widget only loads the images on the display (and one or two outside the display area) at a time, and it unloads them automatically as they scroll out of view. If I have a list of 1000 items with pictures, but only 7 fit onscreen, my print statements in the tableView onRowRender function tell me that only 9-10 images are actually loaded into memory at instantiation. As I scroll the list with my finger, the SDK calls to load up the new ones scrolling into view. It’s pretty sweet stuff, and pretty under-appreciated I think. (Throw a print into the onRowRender - it’s very comforting to know the sdk is handling all the loading/unloading magically behind the scenes)

I don’t know if the scrollView and other widgets are as sophisticated (lazy loading) - haven’t used them much / for this purpose.

It would be great if we could have a variant of tableView that scrolls sideways for this very reason!!!

I have worked on these kind of apps, not with Corona though. The standard solution, both in online and Appstore apps is to preload couple of images to the left and right as you suggested.

To combat fast scrolling, thumbnails are preloaded, they load without performance hit (not sure if this applies to Corona). Depending on size you could even preload all the pages thumbnails. So the images wooshing by are low res, then when you stop paging the hi res images are loaded in and around that area.

The quality of the thumbnails is very important for the user experience. To get highest possible quality on each platform is hard. Of course on iPad 3 you can afford to have much nicer thumbnails than iPhone 3g for example. 

Thanks jonjonsson for your reply. You seem to understand my problem well. In fact the solution you propose is the one I have been working on. I guess my real issue is the lag when the hi-res image is being fetched. As I understand it the Corona engine puts all Runtime and screen drawing on hold while a new image is being loaded. So really… I have to figure out a time the images are ‘still’ before I load this hi-res image. Problem is it can take a second or two and if the user tries interact during this time they will get no response - or worse it seems to kind of ‘cue’ up the users touches and then apply them afterwards.

Maybe there is no beautiful solution. I might just remove all touch listeners while the hi-res image is being loaded and give the user a quick bit of feedback “… loading” for that second they lose control. Am I making sense? Are the limitations I speak of a reality? Ideally we could make 'image load containers" that are free to animate and the images get loaded inside in their own time. I imagine this is how things can be done in Objective-C but Corona does not allow it.

We are currently working on a UI design where we could run into exactly this same issue. @craig stowers, I am wondering if you have been able to find a better solution?

Also for the Tableview, I know it does lazy loading. But is the lazy loading done asynchronously? If not, it seems like it’ll still run into the freeze issue that @craig stowers is talking about.

Any pointers & insight are appreciated.

Thanks!

Here is another idea. Since timer.performWithDelay is asynchronous, would you be able to fake the async image loading by basically creating a short delay timer that does the lazy loading of the image you want?

Would this work? Seems like it should…

I have a tableView which loads images as they become available. Text is loaded first and then the images trickle down. I can confirm that the experience is asynch and nothing freezes. I am very pleased with how its working.

Thanks ksan!

ksan correct me if i’m wrong but you are probably loading images from the net so you are doing async http requests and loading one or two images at a time as they become available. The problem the OP is facing is loading many large images into view as this blocks the app which has been discussed in the forums many times. If you try to load 20 images of 5MB after a fast scroll it will definitely block.

You could however try loading one at a time to let the app update between loads.

so just load an image then call a performWithDelay with a delay of at least 1 to load the next image so you let the app refresh.

Indeed. My bad. I am loading the images from the internet so that does introduce a natural delay for each image coming down in an asynch manner. My code (thanks mpappas!!!) does check for local cache though so next time I load the same tableView all gets pulled from local storage and I still don’t have a freeze. Having said that these are small image files and there are not that many so perhaps that is why I am not experiencing a freeze.

We are currently working on a UI design where we could run into exactly this same issue. @craig stowers, I am wondering if you have been able to find a better solution?

Also for the Tableview, I know it does lazy loading. But is the lazy loading done asynchronously? If not, it seems like it’ll still run into the freeze issue that @craig stowers is talking about.

Any pointers & insight are appreciated.

Thanks!

Here is another idea. Since timer.performWithDelay is asynchronous, would you be able to fake the async image loading by basically creating a short delay timer that does the lazy loading of the image you want?

Would this work? Seems like it should…

I have a tableView which loads images as they become available. Text is loaded first and then the images trickle down. I can confirm that the experience is asynch and nothing freezes. I am very pleased with how its working.

Thanks ksan!

ksan correct me if i’m wrong but you are probably loading images from the net so you are doing async http requests and loading one or two images at a time as they become available. The problem the OP is facing is loading many large images into view as this blocks the app which has been discussed in the forums many times. If you try to load 20 images of 5MB after a fast scroll it will definitely block.

You could however try loading one at a time to let the app update between loads.

so just load an image then call a performWithDelay with a delay of at least 1 to load the next image so you let the app refresh.