UIImage будет отображаться постепенно с сервера
Я пытаюсь отобразить большое изображение с сервера, но я должен постоянно его отображать.
Я использовал подкласс UIView и в том, что я взял объект UIImage
, в котором я использовал NSURLConnection и его методы делегирования, я также использовал
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
в котором я добавляю данные и преобразовываю их в объект UIImage
, и вычерчивая rect с помощью метода drawInRect:
UIImage
.
Все работает нормально, но проблема заключается в том, что когда изображение рисуется в контексте, я не могу щелкнуть нигде на экране, пока на экране не отобразится изображение целиком.
Есть ли хорошее решение, где я могу щелкнуть в другом месте, даже если изображение отображается на экране?
Любая помощь будет заметна.
Изменить:
Есть ли эффективный способ рисования размытия изображения постепенно в didReceiveData
? поэтому drawInRect
не занимает слишком много времени для рисования. Или, если у кого есть пользовательский метод drawRect
, который эффективно отображает изображение по мере поступления данных в didReceiveData
.
Ответы
Ответ 1
Я использовал NYXImagesKit для чего-то подобного, загружая изображения, не блокируя основной поток и показывая изображение постепенно. Я написал очень быстрый и грязный пример, чтобы проиллюстрировать основные работы. Я загружаю изображение в UITableview, чтобы показать, что он не блокирует пользовательский интерфейс (главный поток). Вы можете прокручивать представление таблицы во время загрузки изображения. Не забудьте добавить правильные рамки, есть несколько. Heres ссылка на проект на Github:
https://github.com/HubertK/ProgressiveImageDownload
Он очень прост в использовании, создает объект NYXProgressiveImageView, задает URL-адрес и будет выполнять всю работу для вас, когда вы вызываете:
loadImageAtURL:
Это подкласс UIImageView, работает как волшебство! Здесь ссылка на сайт разработчиков:
http://www.cocoaintheshell.com/2012/01/nyximageskit-class-nyxprogressiveimageview/
Ответ 2
Я предлагаю вытащить данные изображения асинхронно, а затем применить исправление, чтобы получить правильное преобразование из частично загруженных 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 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 обновлений в соответствии с размером вашего изображения.
Ответ 3
Обычно я использую очень простой шаблон GCD для загрузки асинхронного изображения:
- Создайте очередь 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]];
});
});
Ответ 4
Вероятно, didReceiveData
вызывается слишком часто! Просто используйте NSTimer
и регулярно обновляйте изображение в 1-2 шагах. Это должно работать более эффективно.
Также вы можете использовать performSelectorInBackground
для преобразования NSData
в UIImage
;
Затем вызовите performSelectorOnMainThread
, чтобы установить изображение в представление UIImage. Таким образом, конвертирующий материал не будет блокировать основной поток.
Ответ 5
Рассматривали ли вы раскалывание изображений на более мелкие куски на сервере, а затем перерисовку всякий раз, когда получен полный фрагмент? Это даст вам контроль над "прогрессивностью" нагрузки и частотой перерисовки, изменив размер куска. Не уверен, что это прогрессивная нагрузка, которую вы после.
Ответ 6
Если у вас есть контроль над сервером, разделите изображение на фрагменты и создайте изображение с низким разрешением. Показывать версию с низким разрешением сначала в нижнем слое и загружать плитки сверху, рисуя их при загрузке?
Ответ 7
Вы можете создать подкласс 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];
}
});
}
Ответ 8
Почему вы не используете запрос 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];
}
Ответ 9
Ответ в ImageIO.framework, очень простой на самом деле
-
сначала вы создаете mySource CGImageSourceRef, создайте его с помощью CGImageSourceCreateIncremental().
-
настроить и запустить NSURLConnection с изображением Url.
-
в соединении: didReceiveData: добавьте полученные данные в данные вашего заполнителя и обновите источник изображения, вызвав
CGImageSourceUpdateData(imageSource, (CFDataRef)imageData, NO);
затем загрузите частично загруженную часть изображения в ваш UIImageView
self.image = [UIImage imageWithCGImage:CGImageSourceCreateImageAtIndex(imageSource, 0, nil)];
-
в подключенииDidFinishLoading: завершить, вызывая
CGImageSourceUpdateData (imageSource, (CFDataRef) imageData, YES);
self.image = [UIImage imageWithCGImage: CGImageSourceCreateImageAtIndex (imageSource, 0, nil)];
CFRelease (ImageSource);
imageData = nil;
вот пример кода, который я написал:
https://github.com/mohammedDehairy/MDIncrementalImageView
Ответ 10
Я не уверен, как реализованы другие части вашего кода (reg this module), но дайте следующую попытку,
Попробуйте использовать этот селектор с режимом цикла запуска, установленным на NSDefaultRunLoopMode
[self performSelectorOnMainThread:@selector(processImage:)
withObject:objParameters
waitUntillDone:NO
modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]]
Это выполнение освободит ваши взаимодействия с пользовательским интерфейсом, сообщите мне, помогло ли это.
Для получения дополнительной информации: APPLE DOCS
Ответ 11
//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