我可以从URL加载UIImage吗?

141

我有一张图片的URL(从UIImagePickerController获取),但是我不再拥有内存中的图片(该URL是从应用程序上一次运行中保存的)。我能够通过这个URL重新加载UIImage吗?

我看到UIImage有一个imageWithContentsOfFile:方法,但我有一个URL。我可以使用NSData的dataWithContentsOfURL:方法读取URL吗?

编辑1


根据@Daniel的答案,我尝试了以下代码,但它没有起作用...

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];
}

当我运行它时,控制台显示:

-[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'

查看调用堆栈,我调用了URLWithString,它又调用了URLWithString:relativeToURL:,然后是initWithString:relativeToURL:,然后是_CFStringIsLegalURLString,然后是CFStringGetLength,然后是forwarding_prep_0,然后是forwarding,最后是-[NSObject doesNotRecognizeSelector]。

有什么想法,为什么我的NSString(photoURL的地址是0x536fbe0)不响应length呢?为什么它说不响应-[NSURL length]?它难道不知道param是一个NSString而不是NSURL吗?

编辑2


好的,代码唯一的问题就在于字符串到URL的转换。如果我直接硬编码字符串,其他所有东西都能正常工作。所以我的NSString似乎有问题,如果我找不出原因,那么我猜这应该作为另一个问题提出。加入以下这行代码(我从上面的控制台日志中复制了路径),它可以正常工作:

photoURL = @"file://localhost/Users/gary/Library/Application%20Support/iPhone%20Simulator/3.2/Media/DCIM/100APPLE/IMG_0004.JPG";

看起来photoURL已经是NSURL,而不是NSString,因为NSLog已经处理了它。 - drawnonward
@drawn:文档似乎有错误。它说UIImagePickerControllerMediaURL是一个NSString,但实际上是一个NSURL对象。 - progrmr
DLImageLoader这个库真是太厉害了,非常稳定,没有文档,只需一个命令,一切都完美无缺。真是太棒了! - Fattie
我也推荐DLImageLoader。一开始我对这个页面上的评论是否客观持怀疑态度,但我还是试了一下,它确实非常好用。我有一个UITableViewCell中的UIImageView。我所做的就是用DLImageView替换UIImageView,然后调用imageFromUrl:方法来加载图像,一切都很顺利——异步加载、缓存等等。再也没有比这更简单的了。 - itnAAnti
DLImageLoader仍然很棒,另一个不错的选择是Haneke,尽管它有点因为没有得到很好的维护而受到影响。 - Fattie
所有这些回复都没有真正回答你的问题,你只需要处理本地路径,不需要使用所有这些复杂的异步方法,请看下面我的回答。 - Peter Suwara
12个回答

327

你可以这样做(同步,但更简洁):

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

更好的方法是使用苹果公司的LazyTableImages来保留交互性。


1
数据不需要从PNG(或JPG)文件格式转换为UIImage数据吗?还是UIImage会自动处理? - progrmr
2
我猜我应该只看RTFM,因为UIImage类引用中明确说明了它支持哪些文件格式,并且imageWithData:可以使用来自文件的数据,听起来应该可以工作。 - progrmr
@Daniel:如果我使用字符串常量而不是我传递的NSString,它确实可以工作。所以这个最新的问题是我的NSString出了问题,而不是NSURL/NSData/UIImage代码出了问题,这些代码都是正常工作的。谢谢你,Daniel! - progrmr
如果您只使用一张图片并且正在寻找快速解决方案,您可以编写一些代码来缓存单个图像。 - MrDatabase
将这个答案与这里的答案结合使用对我非常有帮助!https://dev59.com/J2Qo5IYBdhLWcg3wI8nx - Thane Brimhall
显示剩余2条评论

27
您可以尝试使用SDWebImage,它提供了以下功能:

  1. 异步加载
  2. 脱机缓存
  3. 在加载时显示占位图像
  4. 与UITableView良好配合

快速示例:

    [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

2
我稍微修改了那个库,并将其与UIImage+Resize集成,以添加调整大小/裁剪功能。如果您需要,请访问https://github.com/toptierlabs/ImageCacheResize。 - Tony
1
这个例子填充了一个 UIImageViewSDWebImage 还让你可以直接使用 SDWebImageManager- (void) downloadWithURL:... 方法来填充一个 UIImage。请参考他们在读我文件中的例子。 - bendytree

13

还有 Swift 版本:

   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)

7

如果你确认NSURL是一个文件URL,也就是说[url isFileURL] 会在你的情况下返回true,那么你可以直接使用如下代码:

[UIImage imageWithContentsOfFile:url.path]

7

获取DLImageLoader并尝试以下代码

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

                          }];

下面是一个使用DLImageLoader的典型实际应用案例,可能会对一些人有所帮助...

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
        }
    }];

如我上面提到的,现在考虑的另一个很棒的库是Haneke(不幸的是它没有那么轻巧)。


1
SDWebImage是Objective-C中最受欢迎的库,而KingFisher则是Swift版本的移植。 - X.Y.
1
真的,但DLImageLoader更好! :) - Fattie

6

试试这段代码,可以通过它设置加载图像,以便用户知道你的应用正在从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"];
            }
        });
    });

2
我更喜欢这个解决方案,因为它不需要加载任何第三方框架或实例化数据加载队列等。 - Andrew Aarestad

4

请查看这里提供的AsyncImageView代码。它提供了一些很好的示例代码,甚至可以直接使用。


有趣的例子,它在概念上非常类似于早期答案中提到的苹果公司的LazyTableImages示例。 - progrmr
它们很相似,但要注意AsyncImageView在表格中真的不起作用,至少在回收表格单元时不起作用(正如您应该做的那样)。 - n13

4

AFNetworking 提供了异步图片加载功能,可以在 UIImageView 中使用占位符。它还支持异步网络请求以便于与各种 API 进行交互。


3

请确保在iOS 9中启用以下设置:

Info.plist中的App Transport Security Settings,以确保从URL加载图像,以便允许下载图像并设置它。

enter image description here

并编写以下代码:

NSURL *url = [[NSURL alloc]initWithString:@"http://feelgrafix.com/data/images/images-1.jpg"];
NSData *data =[NSData dataWithContentsOfURL:url];
quickViewImage.image = [UIImage imageWithData:data];

2

使用Swift 扩展的方法来操作UIImageView(源代码在此):

为相关联的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()
            })
        }
    }

自定义初始化器和设置器

    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()
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接