如何在UIImageView中asynchronous加载图像?

我有一个UIImageView子视图的UIView。 我需要在UIImageView中加载图像而不会阻塞UI。 阻止调用似乎是: UIImage imageNamed: 这是我想解决这个问题:

 -(void)updateImageViewContent { dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ UIImage * img = [UIImage imageNamed:@"background.jpg"]; dispatch_sync(dispatch_get_main_queue(), ^{ [[self imageView] setImage:img]; }); }); } 

图像很小(150×100)。

但是,加载图像时UI仍然被阻塞。 我错过了什么?

这是一个小的代码示例,performance出这种行为:

创build一个基于UIImageView的新类,将其用户交互设置为YES,在UIView中添加两个实例,并实现其touchesBegan方法,如下所示:

 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if (self.tag == 1) { self.backgroundColor= [UIColor redColor]; } else { dispatch_async(dispatch_get_main_queue(), ^{ [self setImage:[UIImage imageNamed:@"woodenTile.jpg"]]; }); [UIView animateWithDuration:0.25 animations: ^(){[self setFrame:CGRectInset(self.frame, 50, 50)];}]; } } 

将标签1分配给其中一个imageViews。

当你几乎同时点击两个视图时,会发生什么情况,从加载图像的视图开始? UI是否被阻塞,因为它正在等待[self setImage:[UIImage imageNamed:@"woodenTile.jpg"]]; 回来 ? 如果是这样,我怎么可以asynchronous做到这一点?

这是一个项目在github与ipmcc代码

使用长按,然后拖动以绘制黑色方块周围的矩形。 据我了解他的回答,理论上说,白色的select矩形不应该在第一次加载图像时被阻挡,但实际上是这样。

项目中包括两个图像(一个小:woodenTile.jpg和一个大:bois.jpg)。 结果与两者相同。

图像格式

我真的不知道这是如何与第一次加载图像时UI被阻塞的问题相关的,但是PNG图像解码时不会阻塞UI,而JPG图像会阻止UI。

事件的年表

步骤0步骤1

用户界面的屏蔽从这里开始

第2步

并在这里结束。

步骤3

AFNetworking解决scheme

  NSURL * url = [ [NSBundle mainBundle]URLForResource:@"bois" withExtension:@"jpg"]; NSURLRequest * request = [NSURLRequest requestWithURL:url]; [self.imageView setImageWithURLRequest:request placeholderImage:[UIImage imageNamed:@"placeholder.png"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { NSLog(@"success: %@", NSStringFromCGSize([image size])); } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { NSLog(@"failure: %@", response); }]; // this code works. Used to test that url is valid. But it's blocking the UI as expected. if (false) if (url) { [self.imageView setImage: [UIImage imageWithData:[NSData dataWithContentsOfURL:url]]]; } 

大多数情况下,它会logging: success: {512, 512}

它也偶尔logging日志: success: {0, 0}

有时: failure: <NSURLResponse: 0x146c0000> { URL: file:///var/mobile/Appl...

但图像从未改变。

问题是, UIImage实际上并没有读取和解码图像,直到第一次实际使用/绘制。 为了强制这个工作发生在后台线程上,你必须在执行主线程之前在后台线程上使用/绘制图像-setImage: . 这对我工作:

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ UIImage * img = [UIImage imageNamed:@"background.jpg"]; // Make a trivial (1x1) graphics context, and draw the image into it UIGraphicsBeginImageContext(CGSizeMake(1,1)); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), [img CGImage]); UIGraphicsEndImageContext(); // Now the image will have been loaded and decoded and is ready to rock for the main thread dispatch_sync(dispatch_get_main_queue(), ^{ [[self imageView] setImage: img]; }); }); 

编辑:用户界面不被阻止。 你已经专门设置了它,使用UILongPressGestureRecognizer ,默认情况下等待UILongPressGestureRecognizer ,然后再做任何事情。 主线程仍在处理事件,但在GR超时之前不会发生任何事情。 如果你这样做:

  longpress.minimumPressDuration = 0.01; 

…你会注意到它变得更快捷。 图像加载不是这里的问题。

编辑2:我已经看了代码,发布到github上,在iPad 2上运行,我根本没有得到你所描述的打嗝。 事实上,这很平稳。 以下是在CoreAnimation仪器中运行代码的截图:

在这里输入图像说明

正如你可以在顶部的图表看到的,FPS直到60FPS,并保持整个手势。 在底部的图表中,您可以在大约16秒处看到图像首次加载的位置,但是您可以看到帧速率没有下降。 仅仅通过视觉检查,我发现select层相交,并且在第一个交点和图像的外观之间有一个小的但可观察的延迟。 据我所知,后台加载代码正在按预期工作。

我希望我可以帮助你,但我只是没有看到这个问题。

您可以使用AFNetworking库,其中通过导入类别

“UIImageView + AFNetworking.m”并使用如下方法:

 [YourImageView setImageWithURL:[NSURL URLWithString:@"http://image_to_download_from_serrver.jpg"] placeholderImage:[UIImage imageNamed:@"static_local_image.png"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { //ON success perform } failure:NULL]; 

希望这可以帮助 。

我有一个非常类似的问题,我的应用程序,我不得不下载很多图像,并伴随着我的用户界面不断更新。 以下是解决我的问题的简单教程链接:

NSOperations&NSOperationQueues教程

这是好方法:

 -(void)updateImageViewContent { dispatch_async(dispatch_get_main_queue(), ^{ UIImage * img = [UIImage imageNamed:@"background.jpg"]; [[self imageView] setImage:img]; }); } 

为什么不像AsyncImageView那样使用第三方库? 使用这个,你所要做的就是声明你的AsyncImageView对象,并传递你想要加载的url或者image。 活动指示器将在图像加载期间显示,没有任何东西会阻止用户界面。

– (void)touchesBegan:在主线程中调用。 通过调用dispatch_async(dispatch_get_main_queue),您可以将该块放入队列中。 当队列准备好时(即系统结束处理触摸),该块将由GCD处理。 这就是为什么你看不到你的woodenTile加载并分配给self.image,直到你释放你的手指,让GCD处理在主队列中排队的所有块。

replace:

  dispatch_async(dispatch_get_main_queue(), ^{ [self setImage:[UIImage imageNamed:@"woodenTile.jpg"]]; }); 

通过:

 [self setImage:[UIImage imageNamed:@"woodenTile.jpg"]]; 

应该解决您的问题…至less为展示它的代码。

考虑使用SDWebImage :它不仅在后台下载和caching图像,还加载和渲染它。

我已经在桌面视图中使用了很好的结果,即使在下载之后也有很大的图像加载缓慢。

https://github.com/nicklockwood/FXImageView

这是一个可以处理背景加载的图像视图。

用法

 FXImageView *imageView = [[FXImageView alloc] initWithFrame:CGRectMake(0, 0, 100.0f, 150.0f)]; imageView.contentMode = UIViewContentModeScaleAspectFit; imageView.asynchronous = YES; //show placeholder imageView.processedImage = [UIImage imageNamed:@"placeholder.png"]; //set image with URL. FXImageView will then download and process the image [imageView setImageWithContentsOfURL:url]; 

要获得文件的URL,您可能会发现以下有趣的内容:

在应用程序启动时获取包文件引用/path

在应用程序中使用AFNetwork时,您不需要使用任何块来加载图像,因为AFNetwork为其提供了解决scheme。 如下:

 #import "UIImageView+AFNetworking.h" 

  Use **setImageWithURL** function of AFNetwork.... 

谢谢

我实现它的一个方法是:(虽然我不知道它是否是最好的)

首先,我使用串行asynchronous或并行队列创build一个队列

 queue = dispatch_queue_create("com.myapp.imageProcessingQueue", DISPATCH_QUEUE_SERIAL);** or queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0); 

**

哪一个你可能会find更好的您的需求。

之后:

  dispatch_async( queue, ^{ // Load UImage from URL // by using ImageWithContentsOfUrl or UIImage *imagename = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]]; // Then to set the image it must be done on the main thread dispatch_sync( dispatch_get_main_queue(), ^{ [page_cover setImage: imagename]; imagename = nil; }); });