Simon says, MonoTouch downloading images like AppStore app in less than 50 lines with CoreAnimation Transitions!
In Project-D I needed to dynamically load images in the same manner the Apple AppStore loads the application icons over the web and have them nicely render into place when the download is completed. Obviously this can’t occur in the UITableView methods because scrolling performance would be degraded significantly waiting for even 1 image to download.
I was amazingly surprised how simple this is in MonoTouch using ThreadPool.QueueUserWorkItem() and some Objective-C classes MonoTouch has wrapped bindings for. I did this all in 50 lines of code. I could have optimized it more to shrink it, but it’s 50 lines with broken out methods and Core Animation transitions. Very small and easy to do!
This is based on my blog about how to build custom UITableViewCell’s in Interface Builder which you can find here.
Inside the GetCell() method of my UITableViewDelegate I call GetImage() passing the UITableViewCell controller and the story (which contains the image uri). GetImage() queues a task with the ThreadPool to go fetch us the image so that the UITableView scrolling is unaffected.
The great thing about GetCell() on the iPhone is, it only gets called for cells which are being rendered. So we do not have to handle all the logic about calculating what is visible, what do we fetch etc. We already know what we need to download! I fade the images alpha so that they appear to fade in nicely once the download is complete.
[code]
public override UITableViewCell GetCell(UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
// ... Do your usual stuff...
GetImage(controller, story);
}
private void GetImage(StoryCellController controller, Story story)
{
controller.ImageThumbnail.Alpha = 0.0f;
if (story.Thumbnail != null && story.Thumbnail.Src != string.Empty)
{
if (images.ContainsKey(story.Thumbnail.Src) == true)
{
UIImage imageThumbnail = images[story.Thumbnail.Src];
controller.ImageThumbnail.Image = imageThumbnail;
controller.ImageThumbnail.Alpha = 1.0f;
}
else
{
controller.ImageUri = story.Thumbnail.Src;
ThreadPool.QueueUserWorkItem(RequestImage, controller);
}
}
}
private void RequestImage(object state)
{
StoryCellController controller = state as StoryCellController;
if (controller != null)
{
NSUrl imageUrl = NSUrl.FromString(controller.ImageUri);
NSData imageData = NSData.FromUrl(imageUrl);
controller.ImageThumbnail.Image = UIImage.LoadFromData(imageData);
images.Add(controller.ImageUri, controller.ImageThumbnail.Image);
InvokeOnMainThread(delegate { RefreshImage(controller); });
}
}
private void RefreshImage(StoryCellController controller)
{
UIView.BeginAnimations("imageThumbnailTransitionIn");
UIView.SetAnimationDuration(0.5f);
controller.ImageThumbnail.Alpha = 1.0f;
UIView.CommitAnimations();
}
[/code]