• Home
  • Nativescript
  • Load Images With Different Height In List Using Nativescript Image-Cache For Android/iOS

Load Images With Different Height In List Using Nativescript Image-Cache For Android/iOS


In this blog post, I am going to show a quick tip on how to show images with different height, downloaded from network, in nativescript list-view. Nativescript provides Image-Cache, to cache the downloaded image and fetch it from cache whenever requested next time. This image-cache is very useful in list-view kind of controls, where the list-item is re-created whenever the list-item comes back into visible area.

If you don’t know, how to use image-cache with list-view, I recommend you to check a very detailed tutorial by Peter-Staev on Using NativeScript’s ImageCache to cache images and display them in list like controls.

If the images that you want to show in list-view have different heights, Android will automatically update the image height once the image is downloaded and will update list-item’s height accordingly. But iOS will not resize the image height till we scroll the list and come back to that list-item. Check below images for the issue I am talking about.

Without Placeholder (iOS)

diff-height-wihtout-placeholder-example
If we don’t use any placeholder image, by default the image height will be 0 and it will not be updated automatically even after the image is downloaded. So when we scroll the list, then only the image’s dimension are being recalculated.

With Fixed Placeholder (iOS)

diff-height-with-fixed-placeholder-example
If we use fixed placeholder image for all the images, it will show cropped image instead of full image height. As explained above, the image dimensions are getting recalculated only when it is scrolled again into view and not after download.

Though in android it is automatically adjusting the height of image, it will create bad UX, like list scroll jumps.

To solve this issue, instead of setting fixed size placeholder image, we need to set placeholder image as per the aspect ratio of image being downloaded. By doing that, the placeholder image will occupy the same height that the image will occupy once downloaded, and will not create any list scroll jump and will create good UX.

For creating dynamic placeholder image, we will require width and height to be pre-defined for the image being downloaded. Otherwise it will not work.

Let’s add the code to create dynamic placeholder image. (Note: I will build up on the code shown by Peter-Staev to make it work for images with different height. If you haven’t checked that code yet, I recommend you to check it from here

export class ImageItem extends observable.Observable
{
    private _oImg: any; /* 1 */

    get imageSrc(): imageSource.ImageSource
    {
        var image = cache.get(this._oImg.url); 

        if (image)
        {
            return image; 
        }

        cache.push(
            {
                key: this._oImg.url
                , url: this._oImg.url
                , completed:
                (image) =>
                {
                    if(image){  /* 2 */
                        this.notify(
                        {
                            object: this
                            , eventName: observable.Observable.propertyChangeEvent
                            , propertyName: "imageSrc"
                            , value: image
                        });
                    }
                }
            });

        /* 3  starts */
        var scaledPlaceholderImageSrc;

        // resizing placeholder image for android.
        if(cache.placeholder.android){
            var resizedBitmap = android.graphics.Bitmap.createScaledBitmap(cache.placeholder.android, this._oImg.width, this._oImg.height, true);
            scaledPlaceholderImageSrc = resizedBitmap;
        }

        // resizing placeholder image for ios.
        if(cache.placeholder.ios){
            let cgSize = CGSizeMake(this._oImg.width, this._oImg.height);
            UIGraphicsBeginImageContextWithOptions(cgSize, false, 0.0);

            cache.placeholder.ios.drawInRect(CGRectMake(0, 0, this._oImg.width, this._oImg.height));
            let newImageSource = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            scaledPlaceholderImageSrc = newImageSource;
        }

        var newImg = new ImageSource();
        newImg.setNativeSource(scaledPlaceholderImageSrc);
        return newImg;
        /* 3 ends */
    }

    constructor(imageSrc : string)
    {
        super();
        this._imageSrc = imageSrc; 
    }
}

In above code, there are three things that we need to understand.
1. We need to pass image object which contains url, width and height of the image instead of just image url.
2. We need to update the image with the downloaded image, if it is successfully downloaded, otherwise it will replace the placeholder image with blank image.
3. Instead of returning same placeholder image for every image element, creating new placeholder image based on the aspect ratio of the image being downloaded. Note that, you need to use stretch="aspectFill" in your <Image> element to make it occupy proper height as per screen size.

After doing these changes, images with different height can be shown in list-view without any impact on UX. Check below images for final output on iOS.
working-diff-height-image-example-ios

To prevent creating multiple placeholder images for same aspect ratio, we can create an object with key as aspect-ratio and value as placeholder image. And we can reuse the placeholder image if any new image with same aspect ratio is being downloaded.

You can check the code of this demo application at: https://github.com/shripalsoni04/nativescript-image-list-demo

Hope you found this tutorial useful.

Menu