UIImage будет отображаться постепенно с сервера - PullRequest
16 голосов
/ 28 февраля 2012

Я пытался отобразить большое изображение с сервера, но я должен отображать его постепенно.

Я использовал подкласс UIView, и в нем я взял UIImage объект, в котором я использовал NSURLConnection иего методы делегата, я также использовал

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

, в котором я добавляю данные и преобразую их в UIImage объект, и рисую прямоугольник, используя drawInRect: метод UIImage.

Все работает нормально, но проблема в том, что когда изображение рисуется в контексте, я не могу щелкнуть где-либо еще на экране, пока все изображение не будет выведено на экран.

Есть ли хорошее решение, где яможете щелкнуть где-нибудь еще, даже если изображение рисуется на экране?

Любая помощь будет заметна.

Редактировать: Есть ли эффективный способ постепенного размытия изображения в didReceiveData?поэтому drawInRect не занимает много времени на рисование.Или Если у кого-то есть пользовательский метод drawRect, который эффективно отображает изображение постепенно, как данные, полученные в didReceiveData.

Ответы [ 11 ]

8 голосов
/ 19 марта 2012

Я использовал NYXImagesKit для чего-то подобного, загружая изображения, не блокируя основной поток, и постепенно отображая изображение.Я написал очень быстрый и грязный пример, чтобы проиллюстрировать основные принципы работы.Я загружаю изображение в UITableview, чтобы показать, что оно не блокирует интерфейс пользователя (основной поток).Вы можете прокрутить просмотр таблицы во время загрузки изображения.Не забудьте добавить правильные рамки, есть несколько.Вот ссылка на проект на Github:

https://github.com/HubertK/ProgressiveImageDownload

Это действительно легко использовать, создать объект NYXProgressiveImageView, установить URL-адрес, и он будет делать всю работу за вас, когда вы вызываете:

loadImageAtURL:

Это подкласс UIImageView, работает как по волшебству!Вот ссылка на сайт разработчиков:

http://www.cocoaintheshell.com/2012/01/nyximageskit-class-nyxprogressiveimageview/

7 голосов
/ 02 марта 2012

Я предлагаю потянуть данные изображения в асинхронном режиме, а затем применить коррекцию, чтобы получить действительное преобразование из частично загруженных 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:] не будет на самом деле принимать частичные данные в качестве ввода по умолчанию. Как видите, создание принудительного и UIImage будет происходить в другом потоке, в то время как основной поток будет отображать обновления только по мере их поступления.

Если вы получаете слишком частые обновления и они все еще блокируют интерфейс, вы можете:

а. Сделайте запрос изображения также в другой теме. б. Уменьшите частоту обновлений UIImageView, вызывая setImage только один раз из 10 или 100 обновлений, в зависимости от размера вашего изображения.

2 голосов
/ 08 марта 2012

Вы можете создать подкласс UIImageView с URL-адресом изображения и методом startDownload. Это базовый образец, который нужно улучшить.

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

}
2 голосов
/ 08 марта 2012

Если у вас есть контроль над сервером, разбейте изображение на плитки, а также создайте изображение с низким разрешением. Сначала отобразите версию с низким разрешением в нижнем слое и загрузите плитки сверху, рисуя их по мере их загрузки?

2 голосов
/ 02 марта 2012

Рассматривали ли вы разделение ваших изображений на более мелкие куски на сервере, а затем перерисовывание при получении полного фрагмента?Это даст вам возможность контролировать «прогрессивность» нагрузки и частоту перерисовок, изменяя размер фрагмента.Но я не уверен, что это тот тип прогрессивной нагрузки, к которому вы стремитесь.

2 голосов
/ 02 марта 2012

Возможно didReceiveData вызывается слишком часто! Просто используйте NSTimer и регулярно обновляйте изображение за 1-2 секунды. Это должно работать более эффективно.

Также вы можете использовать performSelectorInBackground для преобразования NSData в UIImage; А затем вызовите performSelectorOnMainThread, чтобы установить изображение в UIImage View. Таким образом, преобразование не будет блокировать основной поток.

2 голосов
/ 28 февраля 2012

Я обычно использую действительно простой шаблон GCD для асинхронной загрузки изображения:

  1. Создайте очередь GCD, в которую вы загружаете данные изображения с вашего веб-сервера
  2. Установите данные изображенияв вашей основной очереди

Пример:

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]];
  });
});
1 голос
/ 09 ноября 2014

Ответ находится в ImageIO.framework, его очень просто на самом деле

  1. сначала вы создаете CGImageSourceRef mySource, создаете его экземпляр с помощью CGImageSourceCreateIncremental ().

  2. установите и запустите NSURLConnection с URL-адресом изображения.

  3. в соединении: didReceiveData:, добавьте полученные данные к вашим заполнителям и обновите источник изображения, вызвав

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

, затем загрузите частично загруженную часть изображения в ваш UIImageView

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

in connectionDidFinishLoading: завершить с помощью вызова

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

self.image = [UIImage imageWithCGImage: CGImageSourceInre 0, ImageSourceCreate (0)nil)];

CFRelease (imageSource);

imageData = nil;

вот пример кода, который я написал:

https://github.com/mohammedDehairy/MDIncrementalImageView

1 голос
/ 02 марта 2012

Почему бы вам не использовать запрос 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];
}
0 голосов
/ 20 марта 2012
//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
...