Загрузка изображений в таблицу в наборе часов Async
Я разрабатываю WatchKit
приложение, мне нужно получить несколько изображений (иногда 50 эскизов), чтобы выполнить Table
, Я загружаю изображения в iOS
приложение и передать их в WatchKit Extension
, но у меня проблемы.
Во-первых, у меня есть три кнопки, если я нажимаю одну из них, я вижу Table
с некоторыми элементами, все они с image
а также label
, Основная проблема заключается в том, что когда я загружаю эти изображения и нажимаю один элемент, чтобы увидеть его детали, main thread
заблокирован и приложение не делает push
к DetailsController
пока все изображения не будут загружены.
Кто-нибудь имел дело с таблицей со многими элементами и изображениями? Как вы решили это?
Спасибо
2 ответа
Я столкнулся с очень похожей проблемой, и вот как я ее решил. Мне было бы интересно, если у кого-нибудь есть лучшее решение, но это сработало довольно хорошо. По сути, вам нужно оценить, как долго будет задерживаться передача каждого изображения, и убедиться, что вы отправляете их только через это время. Если вы попытаетесь отправить их все сразу, вы заблокируете основной поток.
Я пытался свести мой код к основным элементам этого решения. Вам не нужно будет называть ваши изображения, как я. Ключевые части - это то, как используется очередь. Когда вам нужно прекратить отправку изображений, звоните cancelCurrentImageProcessQueue
,
@interface GPWatchDataController()
@property NSMutableArray* stack;
@property NSMutableArray* stackLocations;
@property NSArray* listData;
@property dispatch_queue_t imageProcessQueue;
@property NSMutableDictionary* cancelImageProcessCreation;
@property NSInteger total;
@property GPWatchMapController* mapController;
@end
@implementation GPWatchDataController
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
}
- (void)willActivate {
// This method is called when watch view controller is about to be visible to user
[super willActivate];
[self loadTableData];
}
- (void)didDeactivate {
// This method is called when watch view controller is no longer visible
[super didDeactivate];
[self cancelCurrentImageProcessQueue];
}
-(void)cancelCurrentImageProcessQueue
{
if (self.imageProcessQueue != nil)
{
if (self.cancelImageProcessCreation == nil)
{
self.cancelImageProcessCreation = [[NSMutableDictionary alloc] init];
}
NSString* key = [self keyForQueue:self.imageProcessQueue];
[self.cancelImageProcessCreation setObject:@(YES) forKey:key];
self.imageProcessQueue = nil;
self.total = 0;
}
}
-(NSString*)keyForQueue:(dispatch_queue_t)queue
{
NSString* key = [NSString stringWithFormat:@"%p", queue];
return key;
}
-(BOOL)shouldCancelQueue:(dispatch_queue_t)queue
{
if (queue != nil && self.cancelImageProcessCreation != nil)
{
NSString* key = [self keyForQueue:queue];
return [self.cancelImageProcessCreation objectForKey:key] != nil;
}
return NO;
}
-(void)loadTableData
{
NSArray* listData = [self FETCH_DATA];
[self.table setNumberOfRows:listData.count withRowType:@"GPWatchSimpleTableRow"];
for (int i = 0; i < self.listData.count; i++)
{
NSDictionary* data = [listData objectAtIndex:i];
BOOL sendImageImmediatly = [data objectForKey:@"KEY"];
NSString* imgName = [data objectForKey:@"KEY"];
UIImage* img = [data objectForKey:@"KEY"];
[self addRowAtIndex:i withTitle:title andImageName:imgName image:img sendImageImmediatly:sendImageImmediatly];
}
}
-(void)addRowAtIndex:(NSInteger)index withTitle:(NSString*)title andImageName:(NSString*)imgName image:(UIImage*)theImage sendImageImmediatly:(BOOL)sendImageImmediatly
{
GPWatchSimpleTableRow* row = [self.table rowControllerAtIndex:index];
[row.label setText:title];
__block NSString* iconType = type;
__block UIImage* image = theImage;
if (type != nil && imgName != nil)
{
NSString* key = imgName;
if (![[WKInterfaceDevice currentDevice] cacheHasManagedImage:key])
{
if (self.imageProcessQueue == nil)
{
//Uses a serial queue
self.imageProcessQueue = dispatch_queue_create("pk.glacier.watchImageProcess", NULL);
}
__block dispatch_queue_t queue = self.imageProcessQueue;
self.total++;
//NSLog(@"Add: %ld", (long)self.total);
CGFloat dispatchDelay = 1.2 * self.total;
void (^processImage)() = ^() {
if (![self shouldCancelQueue:queue])
{
NSData* rowImgData = UIImageJPEGRepresentation(theImage, .7);;
if (sendImageImmediatly)
{
self.total--;
[[WKInterfaceDevice currentDevice] addCachedImageWithData:rowImgData name:key];
[row.image setImageNamed:key];
}
else
{
//Transfering the image can take a long time. When you have a long list of routes
//going though and generating the images goes very fast and then the extension spends
//a lot of time sending the images. It makes the call to send them all very fast.
//The watch will wait until it has gotten all images before it updates the UI again.
//So I put a delay on each send which lets me cancel them and move on and do something
//else.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, dispatchDelay * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
if (![self shouldCancelQueue:queue])
{
[[WKInterfaceDevice currentDevice] addCachedImageWithData:rowImgData name:key];
[row.image setImageNamed:key];
}
//NSLog(@"Done: %ld", (long)self.total);
});
}
}
};
if (sendImageImmediatly)
{
processImage();
}
else
{
dispatch_async(self.imageProcessQueue, processImage);
}
}
else
{
[row.image setImageNamed:key];
}
}
}
Кэшировать изображения в фоновом потоке безопасно, поэтому из расширения WatchKit отправьте в фоновую очередь и используйте WKInterfaceDevice
и один из его addImage
методы Затем обязательно отправьте обратно в основную очередь, прежде чем вы на самом деле обновите интерфейс.
Я расскажу о некоторых из этих методов более подробно в своем посте "Советы по созданию изображений WatchKit".