Monotouch UITableView image "flashing" while background requests are fetched

Posted by Themos Piperakis on Stack Overflow See other posts from Stack Overflow or by Themos Piperakis
Published on 2012-09-30T13:01:11Z Indexed on 2012/10/05 15:37 UTC
Read the original article Hit count: 286

Filed under:

I am in need of some help from you guys. I have a Monotouch UITableView which contains some web images. I have implemented a Task to fetch them asynchronously so the UI is responsive, an even added some animations to fade them in when they are fetched from the web.

My problems start when the user scrolls down very fast down the UITableView, so since the cells are resusable, several background tasks are queued for images. When he is at the bottom of the list, he might see the thumbnail displaying an image for another cell, then another, then another, then another, as the tasks are completed and each image replaces the other one. I am in need of some sort of checking whether the currently displayed cell corresponds to the correct image url, but not sure how to do that.

Here is the code for my TableSource class.

using System;
using MonoTouch.UIKit;
using MonoTouch.Foundation;
using System.Collections.Generic; 
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

{
    public class ListDelegate:UITableViewDelegate
    {
        private UINavigationController nav;
        public override float GetHeightForRow (UITableView tableView, NSIndexPath indexPath)
        {
            return 128;
        }
        public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
        {
            DealViewController c = new DealViewController(((ListDataSource)tableView.DataSource).deals[indexPath.Row].Id,nav);
            nav.PushViewController(c,true);
            tableView.DeselectRow(indexPath,true);
        }
        public ListDelegate(UINavigationController nav)
        {
            this.nav = nav;
        }
    }
    public class ListDataSource:UITableViewDataSource
    {
        bool toggle=true;
        Dictionary<string,UIImage> images = new Dictionary<string, UIImage>();
        public List<MyDeal> deals = new List<MyDeal>();
        Dictionary<int,ListCellViewController> controllers = new Dictionary<int, ListCellViewController>();
        public ListDataSource(List<MyDeal> deals)
        {
            this.deals = deals;
        }
        public override int RowsInSection (UITableView tableview, int section)
        {
            return deals.Count;
        }
        public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
        {
            UITableViewCell cell = tableView.DequeueReusableCell("cell");
            ListCellViewController cellController = null;
             if (cell == null || !controllers.ContainsKey(cell.Tag))
             {
                cellController = new ListCellViewController();
                NSBundle.MainBundle.LoadNib("ListCellViewController", cellController, null);
                cell = cellController.Cell;
                cell.Tag = Environment.TickCount;
                controllers.Add(cell.Tag, cellController);
             }
             else
             {
                cellController = controllers[cell.Tag];
             }
            if (toggle)
            {
                cell.BackgroundView = new UIImageView(UIImage.FromFile("images/bg1.jpg"));
            }
            else
            {
                cell.BackgroundView = new UIImageView(UIImage.FromFile("images/bg2.jpg"));
            }
            toggle = !toggle;
            MyDeal d = deals[indexPath.Row];
            cellController.SetValues(d.Title,d.Price,d.Value,d.DiscountPercent);
            GetImage(cellController.Thumbnail,d.Thumbnail);
            return cell;
        }
        private void GetImage(UIImageView img, string url)
        {
            img.Alpha = 0;
            if (url != string.Empty)
            {
                if (images.ContainsKey(url))
                {
                    img.Image = images[url];
                    img.Alpha = 1;
                }
                else
                {
                    var context = TaskScheduler.FromCurrentSynchronizationContext ();
                    Task.Factory.StartNew (() => {
                        NSData imageData = NSData.FromUrl(new NSUrl(url));
                        var uimg = UIImage.LoadFromData(imageData);
                        images.Add(url,uimg);
                        return uimg;
                    }).ContinueWith (t => { 
                        InvokeOnMainThread(()=>{
                            img.Image = t.Result;
                            RefreshImage(img);
                        });             
                    }, context);
                }
            }
        }
        private void RefreshImage(UIImageView img)
        {
            UIView.BeginAnimations("imageThumbnailTransitionIn");
            UIView.SetAnimationDuration(0.5f);
            img.Alpha = 1.0f;
            UIView.CommitAnimations();
        }
    }
}

Here is the ListCellViewController, that contains a custom cell

using System;
using System.Collections.Generic;
using System.Linq;
using MonoTouch.Foundation;
using MonoTouch.UIKit;


{
    public partial class ListCellViewController : UIViewController
    {
        #region Constructors

        // The IntPtr and initWithCoder constructors are required for items that need 
        // to be able to be created from a xib rather than from managed code

        public ListCellViewController (IntPtr handle) : base(handle)
        {
            Initialize ();
        }

        [Export("initWithCoder:")]
        public ListCellViewController (NSCoder coder) : base(coder)
        {
            Initialize ();
        }

        public ListCellViewController () : base("ListCellViewController", null)
        {
            Initialize ();
        }

        void Initialize ()
        {
        }
        public UIImageView Thumbnail
        {
            get{return thumbnailView;}
        }
        public UITableViewCell Cell
        {
            get {return cell;}
        }
        public void SetValues(string title,decimal price,decimal valuex,decimal discount,int purchases)
        {

        }
        #endregion
    }
}

All help is greatly appreciated

© Stack Overflow or respective owner

Related posts about monotouch