UIImage将从服务器逐步显示

时间:2012-02-28 07:56:13

标签: iphone uiimageview uiimage http-headers nsurlconnection

我一直试图从服务器显示大图像,但我必须逐步显示它。

我使用了UIView的子类,并且我使用了UIImage对象,其中我使用了NSURLConnection及其委托方法,我也使用了

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;

我在其中附加数据并将其转换为UIImage对象,并使用drawInRect:的{​​{1}}方法绘制rect。

一切正常,但问题是,当在上下文中绘制图像时,我无法点击屏幕上的任何其他位置,直到将整个图像绘制到屏幕上。

是否有任何好的解决方案,即使在屏幕上绘制图像,我还可以点击其他任何地方?

任何帮助都会很明显。

编辑: 有没有有效的方法在UIImage中逐步绘制图像模糊?所以didReceiveData不需要太多时间来画画。或者,如果有人使用自定义drawInRect方法,可以有效地将图像作为drawRect中收到的数据逐步显示。

11 个答案:

答案 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模式进行异步图像加载:

  1. 创建一个GCD队列,在其中从Web服务器加载图像数据
  2. 在主队列中设置图像数据
  3. 示例:

    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秒的步骤定期更新图像。这应该更有效。

您也可以使用NSDataUIImage转换为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中,实际上非常简单

  1. 首先创建一个CGImageSourceRef mySource,使用CGImageSourceCreateIncremental()实例化它。

  2. 使用图片Url设置并启动NSURLConnection。

  3. 连接中的
  4. :didReceiveData :,将收到的数据附加到占位符数据,并通过调用

  5. 更新图像源

    CGImageSourceUpdateData(imageSource, (CFDataRef)imageData, NO);

    然后将图像的部分加载部分加载到UIImageView

    self.image = [UIImage imageWithCGImage:CGImageSourceCreateImageAtIndex(imageSource, 0, nil)];
    
      connectionDidFinishLoading中的
    1. :通过调用

      完成

      CGImageSourceUpdateData(imageSource,(CFDataRef)imageData,YES);

      self.image = [UIImage imageWithCGImage:CGImageSourceCreateImageAtIndex(imageSource,0,nil)];

      CFRelease(ImageSource的);

      imageData = nil;

    2. 这是我写的示例代码:

      https://github.com/mohammedDehairy/MDIncrementalImageView

答案 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