141

I have a URL for an image (got it from UIImagePickerController) but I no longer have the image in memory (the URL was saved from a previous run of the app). Can I reload the UIImage from the URL again?

I see that UIImage has a imageWithContentsOfFile: but I have a URL. Can I use NSData's dataWithContentsOfURL: to read the URL?

EDIT1


based on @Daniel's answer I tried the following code but it doesn't work...

NSLog(@"%s %@", __PRETTY_FUNCTION__, photoURL);     
if (photoURL) {
    NSURL* aURL = [NSURL URLWithString:photoURL];
    NSData* data = [[NSData alloc] initWithContentsOfURL:aURL];
    self.photoImage = [UIImage imageWithData:data];
    [data release];
}

When I ran it the console shows:

-[PhotoBox willMoveToWindow:] file://localhost/Users/gary/Library/Application%20Support/iPhone%20Simulator/3.2/Media/DCIM/100APPLE/IMG_0004.JPG
*** -[NSURL length]: unrecognized selector sent to instance 0x536fbe0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSURL length]: unrecognized selector sent to instance 0x536fbe0'

Looking at the call stack, I'm calling URLWithString, which calls URLWithString:relativeToURL:, then initWithString:relativeToURL:, then _CFStringIsLegalURLString, then CFStringGetLength, then forwarding_prep_0, then forwarding, then -[NSObject doesNotRecognizeSelector].

Any ideas why my NSString (photoURL's address is 0x536fbe0) doesn't respond to length? Why does it say it doesn't respond to -[NSURL length]? Doesn't it know that param is an NSString, not a NSURL?

EDIT2


OK, the only problem with the code is the string to URL conversion. If I hardcode the string, everything else works fine. So something is wrong with my NSString and if I can't figure it out, I guess that should go in as a different question. With this line inserted (I pasted the path from the console log above), it works fine:

photoURL = @"file://localhost/Users/gary/Library/Application%20Support/iPhone%20Simulator/3.2/Media/DCIM/100APPLE/IMG_0004.JPG";
TheNeil
  • 3,321
  • 2
  • 27
  • 52
progrmr
  • 75,956
  • 16
  • 112
  • 147
  • It seems like photoURL is already an NSURL, not an NSString, given that NSLog handled it. – drawnonward May 07 '10 at 03:35
  • @drawn: Looks like an error in the docs. It says that UIImagePickerControllerMediaURL is an NSString but it's actually an NSURL object. – progrmr May 07 '10 at 03:51
  • The library DLImageLoader is INCREDIBLE. rock solid, no doco, one command and everything is perfect. What a find. – Fattie Dec 11 '13 at 22:27
  • I second (third?) DLImageLoader. I was skeptical about whether the comments about it on this page were objective - but I tried it anyway and it does work very nicely. I have a UIImageView within a UITableViewCell. All I did was replace the UIImageView with a DLImageView, then called the imageFromUrl: method to load the image, and it all just works -- asynchronous loading, caching, etc. Couldn't be easier. – itnAAnti May 13 '16 at 14:50
  • DLImageLoader remains fantastic, another good one is **Haneke**, although it suffers a little from being not quite maintained. – Fattie May 09 '17 at 10:30
  • All these replies are really not answering your question, you only need to deal with a local path, no need for all these complex async methods, see my answer below. – Peter Suwara Dec 28 '20 at 15:45

12 Answers12

327

You can do it this way (synchronously, but compact):

UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:MyURL]]];

A much better approach is to use Apple's LazyTableImages to preserve interactivity.

Krishnabhadra
  • 34,169
  • 30
  • 118
  • 167
Daniel Blezek
  • 4,539
  • 1
  • 19
  • 20
  • 1
    Doesn't the data need to be converted from PNG (or JPG) file format to UIImage data? Or does UIImage figure that out somehow? – progrmr May 06 '10 at 19:03
  • 2
    Guess I should just RTFM, it says right in UIImage Class Reference what file formats it can support and imageWithData: says it can be data from a file, sounds like it should work. – progrmr May 06 '10 at 19:29
  • @Daniel: didn't work, I edited my question to include my actual code and the exception info. It's a little bizarre. – progrmr May 07 '10 at 03:03
  • @Daniel: it does work if I use a string constant instead of the NSString I was passing in. So this latest problem was something wrong with my NSString, not a problem with the NSURL/NSData/UIImage code which works. Thanks Daniel! – progrmr May 07 '10 at 03:28
  • If you're using just one image and you're looking for a quick fix you can just write a bit of code that caches the single image. – MrDatabase Feb 12 '12 at 20:09
  • Using this answer in conjunction with the answer here worked very well for me! http://stackoverflow.com/questions/16283652/understanding-dispatch-async – Thane Brimhall Nov 12 '13 at 23:53
  • This is one of the worse answer. – Jamil Mar 03 '20 at 05:01
27

You can try SDWebImage, it provides:

  1. Asynchronous loading
  2. Caching for offline use
  3. Place holder image to appear while loading
  4. Works well with UITableView

Quick example:

    [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
Muhammad Hassan Nasr
  • 2,718
  • 3
  • 18
  • 20
  • 2
    I have modified that library a bit and integrated it with UIImage+Resize in order to add Resize/Crop capabilities to it. If you need that check it out at https://github.com/toptierlabs/ImageCacheResize – Tony Oct 28 '12 at 02:48
  • 1
    This example populates a `UIImageView`. `SDWebImage` also lets you populate a `UIImage` directly using `SDWebImageManager` `- (void) downloadWithURL:...`. See their example on the read me. – bendytree Dec 05 '12 at 23:08
13

And the swift version :

   let url = NSURL.URLWithString("http://live-wallpaper.net/iphone/img/app/i/p/iphone-4s-wallpapers-mobile-backgrounds-dark_2466f886de3472ef1fa968033f1da3e1_raw_1087fae1932cec8837695934b7eb1250_raw.jpg");
    var err: NSError?
    var imageData :NSData = NSData.dataWithContentsOfURL(url,options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)
    var bgImage = UIImage(data:imageData)
user3763002
  • 277
  • 2
  • 3
7

get DLImageLoader and try folowing code

   [DLImageLoader loadImageFromURL:imageURL
                          completed:^(NSError *error, NSData *imgData) {
                              imageView.image = [UIImage imageWithData:imgData];
                              [imageView setContentMode:UIViewContentModeCenter];

                          }];

Another typical real-world example of using DLImageLoader, which may help someone...

PFObject *aFacebookUser = [self.fbFriends objectAtIndex:thisRow];
NSString *facebookImageURL = [NSString stringWithFormat:
    @"http://graph.facebook.com/%@/picture?type=large",
    [aFacebookUser objectForKey:@"id"] ];

__weak UIImageView *loadMe = self.userSmallAvatarImage;
// ~~note~~ you my, but usually DO NOT, want a weak ref
[DLImageLoader loadImageFromURL:facebookImageURL
   completed:^(NSError *error, NSData *imgData)
    {
    if ( loadMe == nil ) return;

    if (error == nil)
        {
        UIImage *image = [UIImage imageWithData:imgData];
        image = [image ourImageScaler];
        loadMe.image = image;
        }
    else
        {
        // an error when loading the image from the net
        }
    }];

As I mention above another great library to consider these days is Haneke (unfortunately it's not as lightweight).

Fattie
  • 27,874
  • 70
  • 431
  • 719
Muruganandham K
  • 5,271
  • 5
  • 34
  • 62
7

If you're really, absolutely positively sure that the NSURL is a file url, i.e. [url isFileURL] is guaranteed to return true in your case, then you can simply use:

[UIImage imageWithContentsOfFile:url.path]
Dhiraj Gupta
  • 9,704
  • 8
  • 49
  • 54
6

Try this code, you can set loading image with it, so the users knows that your app is loading an image from url:

UIImageView *yourImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"loading.png"]];
    [yourImageView setContentMode:UIViewContentModeScaleAspectFit];

    //Request image data from the URL:
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://yourdomain.com/yourimg.png"]];

        dispatch_async(dispatch_get_main_queue(), ^{
            if (imgData)
            {
                //Load the data into an UIImage:
                UIImage *image = [UIImage imageWithData:imgData];

                //Check if your image loaded successfully:
                if (image)
                {
                    yourImageView.image = image;
                }
                else
                {
                    //Failed to load the data into an UIImage:
                    yourImageView.image = [UIImage imageNamed:@"no-data-image.png"];
                }
            }
            else
            {
                //Failed to get the image data:
                yourImageView.image = [UIImage imageNamed:@"no-data-image.png"];
            }
        });
    });
Jouhar
  • 288
  • 4
  • 9
  • 2
    I preferred this solution since it didn't require loading any 3rd party frameworks or instantiating data loading queues, etc. – Andrew Aarestad Nov 05 '15 at 14:33
4

AFNetworking provides async image loading into a UIImageView with placeholder support. It also supports async networking for working with APIs in general.

pauliephonic
  • 2,127
  • 1
  • 13
  • 9
4

Check out the AsyncImageView provided over here. Some good example code, and might even be usable right "out of the box" for you.

marcc
  • 12,295
  • 7
  • 49
  • 59
  • Interesting example, it's very similar in concept to Apple's LazyTableImages example mentioned in the earlier answer. – progrmr May 07 '10 at 03:31
  • It's similar but beware that AsyncImageView really doesn't work in tables, at least not when you recycle table cells (as you should). – n13 Feb 07 '12 at 16:47
3

Make sure enable this settings from iOS 9:

App Transport Security Settings in Info.plist to ensure loading image from URL so that it will allow download image and set it.

enter image description here

And write this code:

NSURL *url = [[NSURL alloc]initWithString:@"http://feelgrafix.com/data/images/images-1.jpg"];
NSData *data =[NSData dataWithContentsOfURL:url];
quickViewImage.image = [UIImage imageWithData:data];
Nagarjun
  • 6,557
  • 5
  • 33
  • 51
2

The way using a Swift Extension to UIImageView (source code here):

Creating Computed Property for Associated UIActivityIndicatorView

import Foundation
import UIKit
import ObjectiveC

private var activityIndicatorAssociationKey: UInt8 = 0

extension UIImageView {
    //Associated Object as Computed Property
    var activityIndicator: UIActivityIndicatorView! {
        get {
            return objc_getAssociatedObject(self, &activityIndicatorAssociationKey) as? UIActivityIndicatorView
        }
        set(newValue) {
            objc_setAssociatedObject(self, &activityIndicatorAssociationKey, newValue, UInt(OBJC_ASSOCIATION_RETAIN))
        }
    }

    private func ensureActivityIndicatorIsAnimating() {
        if (self.activityIndicator == nil) {
            self.activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
            self.activityIndicator.hidesWhenStopped = true
            let size = self.frame.size;
            self.activityIndicator.center = CGPoint(x: size.width/2, y: size.height/2);
            NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
                self.addSubview(self.activityIndicator)
                self.activityIndicator.startAnimating()
            })
        }
    }

Custom Initializer and Setter

    convenience init(URL: NSURL, errorImage: UIImage? = nil) {
        self.init()
        self.setImageFromURL(URL)
    }

    func setImageFromURL(URL: NSURL, errorImage: UIImage? = nil) {
        self.ensureActivityIndicatorIsAnimating()
        let downloadTask = NSURLSession.sharedSession().dataTaskWithURL(URL) {(data, response, error) in
            if (error == nil) {
                NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
                    self.activityIndicator.stopAnimating()
                    self.image = UIImage(data: data)
                })
            }
            else {
                self.image = errorImage
            }
        }
        downloadTask.resume()
    }
}
fpg1503
  • 7,492
  • 6
  • 29
  • 49
0

The Best and easy way to load Image via Url is by this Code:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSData *data =[NSData dataWithContentsOfURL:[NSURL URLWithString:imgUrl]];

    dispatch_async(dispatch_get_main_queue(), ^{
        imgView.image= [UIImage imageWithData:data];
    });
});

Replace imgUrl by your ImageURL
Replace imgView by your UIImageView.

It will load the Image in another Thread, so It will not slow down your App load.

jalmatari
  • 331
  • 5
  • 8
0

Local URL's are super simple, just use this :

UIImage(contentsOfFile: url.path)
Peter Suwara
  • 781
  • 10
  • 16