我一直试图从服务器显示大图像,但我必须逐步显示它。
我使用了UIView的子类,并且我使用了UIImage
对象,其中我使用了NSURLConnection及其委托方法,我也使用了
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
我在其中附加数据并将其转换为UIImage
对象,并使用drawInRect:
的{{1}}方法绘制rect。
一切正常,但问题是,当在上下文中绘制图像时,我无法点击屏幕上的任何其他位置,直到将整个图像绘制到屏幕上。
是否有任何好的解决方案,即使在屏幕上绘制图像,我还可以点击其他任何地方?
任何帮助都会很明显。
编辑:
有没有有效的方法在UIImage
中逐步绘制图像模糊?所以didReceiveData
不需要太多时间来画画。或者,如果有人使用自定义drawInRect
方法,可以有效地将图像作为drawRect
中收到的数据逐步显示。
答案 0 :(得分:8)
我使用NYXImagesKit做类似的事情,下载图像,同时不阻塞主线程并逐步显示图像。我写了一个非常快速和肮脏的例子来说明基本的工作。我在UITableview中加载图像,以显示它不会阻止用户界面(主线程)。您可以在加载图像时滚动桌面视图。不要忘记添加正确的框架,有几个。下面是Github项目的链接:
https://github.com/HubertK/ProgressiveImageDownload
它非常容易使用,创建一个NYXProgressiveImageView对象,设置URL,当你打电话时它将为你完成所有工作:
loadImageAtURL:
它是UIImageView的子类,像魔术一样工作!这是开发者网站的链接:
http://www.cocoaintheshell.com/2012/01/nyximageskit-class-nyxprogressiveimageview/
答案 1 :(得分:7)
我建议以异步方式提取图像数据,然后应用更正以获得从部分下载的NSData到UIImage的有效转换:
NSURLRequest *theRequest = [NSURLRequest requestWithURL:
[NSURL URLWithString: imageRequestString]
cachePolicy: NSURLRequestReloadIgnoringCacheData
timeoutInterval: 60.0];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest: theRequest
delegate: self];
if (theConnection)
receivedData = [[NSMutableData data] retain];
.......
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData: data];
NSInvocationOperation *operation =
[[NSInvocationOperation alloc] initWithTarget: self
selector: @selector(loadPartialImage)
object: nil];
[[[NSOperationQueue alloc] init] autorelease] addOperation: operation];
[operation release];
}
- (void)loadPartialImage {
// This is where you would call the function that would "stitch up" your partial
// data and make it appropriate for use in UIImage's imageWithData
NSData *validPartialData =
[self validImageRepresentationFromPartialImageData: receivedData];
UIImage *partialImage = [UIImage imageWithData: validPartialData];
[imageView performSelectorOnMainThread: @selector(setImage:)
withObject: partialImage
waitUntilDone: NO];
}
+ (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
UIImage *fullImage = [UIImage imageWithData: receivedData];
imageView.image = fullImage;
}
请注意,我没有提供validImageRepresentationFromPartialImageData
的代码,因为目前我没有明确的具体想法,如何实现这样的修正,或者[UIImage imageWithData:] wouldn' t实际上默认接受部分数据作为输入。正如您所看到的,强制和UIImage创建将在不同的线程上发生,而主线程只会在它们到来时显示更新。
如果您收到过于频繁的更新且他们仍在阻止界面,您可以:
一个。也可以在不同的线程上发出图像请求。 湾根据图像的zise,通过仅在10或100次更新中调用setImage一次,降低UIImageView更新的频率。
答案 2 :(得分:2)
我通常使用非常简单的GCD模式进行异步图像加载:
示例:强>
dispatch_queue_t image_queue = dispatch_queue_create("com.company.app.imageQueue", NULL);
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_async(image_queue, ^{
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[record imageURLString]];
dispatch_async(main_queue, ^{
[imageView setImage:[UIImage imageWithData:imageData]];
});
});
答案 3 :(得分:2)
可能经常调用didReceiveData
NSTimer
!只需使用performSelectorInBackground
并以1-2秒的步骤定期更新图像。这应该更有效。
您也可以使用NSData
将UIImage
转换为performSelectorOnMainThread
;
然后调用{{1}}将图像设置为UIImage视图。所以转换内容不会阻塞主线程。
答案 4 :(得分:2)
您是否考虑过在服务器上将图像切割成较小的块,然后在收到完整的块时重新绘制?这可以让您通过更改块大小来控制负载的“渐进性”和重绘的频率。但不确定这是你所追求的渐进式负载。
答案 5 :(得分:2)
如果您可以控制服务器,请将图像拆分为图块并创建低分辨率图像。首先在最低层显示低分辨率版本,并在加载时将瓷砖加载到顶部绘制它们?
答案 6 :(得分:2)
您可以使用图像的URL和startDownload方法创建UIImageView的子类。 这是一个必须改进的基本样本。
@property (nonatomic, strong) NSURL *imageURL;
- (void)startDownload;
@implementation ImgeViewSubClass
{
NSURLConnection *connection;
NSMutableData *imageData;
}
开始下载方法:
- (void)startDownload
{
NSURLRequest *request = [NSURLRequest requestWithURL:imageURL];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
imageData = [NSMutableData data];
}
来自NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(imageData)
{
[imageData appendData:data];
}
// this part must be improved using CGImage instead of UIImage because we are not on main thread
UIImage *image = [UIImage imageWithData:imageData];
if (image) {
[self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
}
});
}
答案 7 :(得分:1)
为什么不使用ASIHTTPRequest请求:
#import "ASIHTTPRequest.h"
这有助于在后台加载/绘图,也可以执行其他任务。
试试这个:
#import "ASIHTTPRequest.h"
[self performSelectorInBackground:@selector(DownLoadImageInBackground:)
withObject:YOUR IMAGE ARRAY];
-(void) DownLoadImageInBackground:(NSArray *)imgUrlArr1
{
NSURL * url = [Image URL];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
}
-(void)requestFailed:(ASIHTTPRequest *)request
{
NSLog(@"URL Fail : %@",request.url);
NSError *error = [request error];
// you can give here alert too..
}
-(void)requestFinished:(ASIHTTPRequest *)request
{
/////////// Drawing Code Here////////////////////
NSData *responseData = [request responseData];
UIImage *imgInBackground = [[UIImage alloc] initWithData:responseData];
[imageView setImage: imgInBackground];
}
答案 8 :(得分:1)
答案在ImageIO.framework中,实际上非常简单
首先创建一个CGImageSourceRef mySource,使用CGImageSourceCreateIncremental()实例化它。
使用图片Url设置并启动NSURLConnection。
:didReceiveData :,将收到的数据附加到占位符数据,并通过调用
CGImageSourceUpdateData(imageSource, (CFDataRef)imageData, NO);
然后将图像的部分加载部分加载到UIImageView
self.image = [UIImage imageWithCGImage:CGImageSourceCreateImageAtIndex(imageSource, 0, nil)];
:通过调用
完成CGImageSourceUpdateData(imageSource,(CFDataRef)imageData,YES);
self.image = [UIImage imageWithCGImage:CGImageSourceCreateImageAtIndex(imageSource,0,nil)];
CFRelease(ImageSource的);
imageData = nil;
这是我写的示例代码:
答案 9 :(得分:0)
我不确定您的代码的其他部分(注册此模块)是如何实现的,但请尝试以下内容,
尝试将此选择器与运行循环模式设置为NSDefaultRunLoopMode
[self performSelectorOnMainThread:@selector(processImage:)
withObject:objParameters
waitUntillDone:NO
modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]]
此执行将释放您的UI互动,如果有帮助请告诉我。
了解更多信息:APPLE DOCS
答案 10 :(得分:0)
//JImage.h
#import <Foundation/Foundation.h>
@interface JImage : UIImageView {
NSURLConnection *connection;
NSMutableData* data;
UIActivityIndicatorView *ai;
}
-(void)initWithImageAtURL:(NSURL*)url;
@property (nonatomic, retain) NSURLConnection *connection;
@property (nonatomic, retain) NSMutableData* data;
@property (nonatomic, retain) UIActivityIndicatorView *ai;
@end
//JImage.m
#import "JImage.h"
@implementation JImage
@synthesize ai,connection, data;
-(void)initWithImageAtURL:(NSURL*)url {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self setContentMode:UIViewContentModeScaleToFill];
if (!ai){
[self setAi:[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]];
[ai startAnimating];
[ai setFrame:CGRectMake(27.5, 27.5, 20, 20)];
[ai setColor:[UIColor blackColor]];
[self addSubview:ai];
}
NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData {
if (data==nil)
data = [[NSMutableData alloc] initWithCapacity:5000];
[data appendData:incrementalData];
NSNumber *resourceLength = [NSNumber numberWithUnsignedInteger:[data length]];
NSLog(@"resourceData length: %d", [resourceLength intValue]);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(@"Connection error...");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[ai removeFromSuperview];
}
- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self setImage:[UIImage imageWithData: data]];
[ai removeFromSuperview];
}
@end
//Include the definition in your class where you want to use the image
-(UIImageView*)downloadImage:(NSURL*)url:(CGRect)frame {
JImage *photoImage=[[JImage alloc] init];
photoImage.backgroundColor = [UIColor clearColor];
[photoImage setFrame:frame];
[photoImage setContentMode:UIViewContentModeScaleToFill];
[photoImage initWithImageAtURL:url];
return photoImage;
}
//call the function
UIImageView *imagV=[self downloadImage:url :rect];
//you can call the downloadImage function in looping statement and subview the returned imageview.
//it will help you in lazy loading of images.
//Hope this will help