Search code examples
c#iosuitableviewxamarin.ioscustom-cell

UITableView with Images scrolls very slowly Xamarin.iOS


I am developing a lessons' listing app using Xamarin.Ios and Visual Studio for Mac.

My main app screen is an ExploreTableView, where I am currently listing all the lessons I inserted through the apposite Azure Mobile Service Easy Table. In my TableView, I have a little bit complex custom cell, that needs to show the "Lesson Subject", "TeacherName", "Lesson Rating", "Lesson cost" and other variables. I have almost Implemented all with success, and it works. Here the cell structure:

Cell Preview

I am a High School IT Student, not very much expert in xamarin.ios programming, but today, following a Youtube Microsoft Guide, I also managed to implement Blob storage, in which I stored the lessons covers, which I can retrieve to show them up on the left side of the CustomCell.

The problem is that from now though, the TableView scrolling has become very slow, the cell correctly shows the Images stored on my Blob Azure storage, but it seems like I am doing something wrong in the way the TableView loads the cells.

I tried to read some guides, both here on Stack Overflow and on the Microsoft Developer Documentation, but I am honestly not able to understand how the cache system works, and how to implement it, so I am here to ask if someone could help me fix for my code performance issue, or advise me some easy to follow online guides.

Here is my ExploreViewController:

using Foundation;
using System;
using System.Collections.Generic;
using UIKit;
using LessonApp.Model;using System.Threading;
using System.Threading.Tasks;

namespace LessonApp.iOS
{
public partial class ExploreViewController : UITableViewController
{
    List<LessonsServices> lessonsServices;

    public LessonsServices lessonService;


    public ExploreViewController(IntPtr handle) : base(handle)
    {
        lessonsServices = new List<LessonsServices>();
    }

    public override async void ViewDidLoad()
    {
        base.ViewDidLoad();

        lessonsServices = await LessonsServices.GetLessonsServices();
        //lessonsServices = await 
        TableView.ReloadData();
    }

    //LISTING ZONE

    public override nint RowsInSection(UITableView tableView, nint section)
    {
        return lessonsServices.Count;
    }


    public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
    {
        var cell = tableView.DequeueReusableCell("servicePreviewCell") as LessonsServicesViewCell;

        var lessonService = lessonsServices[indexPath.Row]; 

 //LESSON TITLE
        cell.titleLabel.Text = lessonService.Subject + " Lesson"; //e.g. "Math Lesson"

        //TEACHER NAME AND LOCATION

        cell.teacherNameLocationLabel.Text = lessonService.Teacher + " • " + lessonService.Location;

        // PRO TEACHER BADGE

        switch (lessonService.IsPro)
        {
            case true:
                cell.proLabel.Hidden = false;
                break;

            case false:
                cell.proLabel.Hidden = true;
                break;

            default:
                cell.proLabel.Hidden = true;
                break;

        }

        cell.startingFromPriceLabel.Text = "Starting from " + lessonService.LowestPrice.ToString() + " €/h";

        //Showing Up the Lesson Cover Image in the cell

        var bytes = Task.Run(() => ImagesManager.GetImage(lessonService.Id+".jpeg")).Result; //Here I call the GetImage method, which connects to the Blob Storage Container and retrieve the image that has the same ID of the Lesson Service
        var data = NSData.FromArray(bytes);
        var uiimage = UIImage.LoadFromData(data);
        cell.teacherProfileImageView.Image = uiimage;

        return cell; 

    }


    //I need this Method to force the cell height
    public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
    {
        return 120;
    }

    //A Segue for another screen, that will copy some information from this page to another  
    public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
    {
        if (segue.Identifier == "ServiceDescriptionPageSegue")
        {
            var selectedRow = TableView.IndexPathForSelectedRow;
            var destinationViewController = segue.DestinationViewController as ServiceDescriptionView;
            destinationViewController.lessonService = lessonsServices[selectedRow.Row];


        }

        base.PrepareForSegue(segue, sender);
    }
  }
}

Thanks for your attention!


Solution

  • GetCell is called once for each row that is visible on screen and it is called additional times as new rows come into view during scrolling.

    You are calling the GetImage in GetCell using:

    Task.Run(() => ImagesManager.GetImage(lessonService.Id+".jpeg")).Result;
    

    So GetCell is waiting for GetImage to return leading to slow scroll.

    Quick solution would be to make your GetImage method asynchronous and call it within GetCell asynchronously, then invoking Image Update on mainthread once completed.

    Task.Run(async () => {
        var bytes = await ImagesManager.GetImage(lessonService.Id + ".jpeg");
        var data = NSData.FromArray(bytes);
        var uiimage = UIImage.LoadFromData(data);
        InvokeOnMainThread(() => {
          cell.teacherProfileImageView.Image = uiimage;
        });
    });