Displaying web images with MonoTouch - UIWebImageView

Posted by ESCOZ on Friday, December 18, 2009

When displaying images from the web on iPhone applications, we should always make sure the images are never be downloaded in the application thread, to avoid locking the application; we should also always display feedback to the user that the image is being loaded. There are several libraries that take care of this functionality in Objective-C, but I couldn’t find anything in C#, using MonoTouch, so I decided to create a simple one.

Here’s how it works. The new UIWebImageView control inherits from UIImageView with either C# or NIB files. For that to happen, we need a series of constructors, like below:

    [Register("UIWebImageView")]
    public class UIWebImageView : UIImageView
    {
        NSMutableData imageData;
        UIActivityIndicatorView indicatorView;
    
        public UIWebImageView (IntPtr handle) : base(handle)
        {
            Initialize ();
        }

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

        public UIWebImageView(RectangleF frame){
            Initialize();
            
            indicatorView.Frame = new RectangleF (
                        frame.Size.Width/2,
                        frame.Size.Height/2,
                        indicatorView.Frame.Size.Width,
                        indicatorView.Frame.Size.Height);
        }
        
        public UIWebImageView(RectangleF frame, string url):base(frame){
            DownloadImage(url);
        }
    }

The NSMutableData class simply works as a buffer of bytes, and will be used to keep the data while its being downloaded from the url. We also set the indicatorView frame, so that it shows right in the middle of the image that will be downloaded. The Initialize() method simply sets up the indicatorView object:

        void Initialize ()
        {
            imageData = new NSMutableData();
                        
            indicatorView = new UIActivityIndicatorView(UIActivityIndicatorViewStyle.Gray);
            indicatorView.HidesWhenStopped = true;
            var width  = (this.Frame.Width-20)/2;
            var height = (this.Frame.Height-20)/2;
            indicatorView.Frame = new RectangleF(width, height,20,20);
            this.AddSubview(indicatorView);
        }

While the DownloadImage() method turns the indicatorView visible and starts the download:

        public void DownloadImage(string url){
            indicatorView.StartAnimating();
            NSUrlRequest request = new NSUrlRequest(new NSUrl(url));
            new NSUrlConnection(request, new ConnectionDelegate(this), true);
        }

Here we’re using two other classes from the Apple Foundation framework (MonoTouch.Foundation namespace). We could have used .NET’s classes to download the objects as well. The ConnectionDelegate object we’re passing is an inner class, that is responsible for receiving the callbacks from the UrlConnection object when the data is being downloaded.

        class ConnectionDelegate : NSUrlConnectionDelegate {
            
            UIWebImageView _view;

            public ConnectionDelegate(UIWebImageView view){
                _view = view;
            }

            public override void ReceivedData (NSUrlConnection connection, NSData data)
            {
                _view.imageData.AppendData(data);   
            }
            
            public override void FinishedLoading (NSUrlConnection connection)
            {
                System.Threading.Thread.Sleep(5000);
                _view.indicatorView.StopAnimating();
                UIImage downloadedImage = UIImage.LoadFromData(_view.imageData);
                _view.Image = downloadedImage;
            }
        }

That’s it! To instantiate it using Interface Builder, simply drag and drop a UIImageView into your view, and then change the class type to “UIWebImageView”. Don’t forget then to call the method “DownloadImage(url)” from your code to start the download.

UPDATE: The full class can be found on GitHub.. You can also download the entire solution with a sample app from here.