在手表套件中asynchronous下载图片
我正在开发一个WatchKit
应用程序,我需要获取一些图像(有时50个缩略图)来完成一个Table
。 我在iOS
应用程序中下载图像并将它们传递给WatchKit Extension
,但是我遇到了问题。
首先,我有三个button,如果我按下其中一个button,我会看到一个带有一些元素的Table
,所有元素都带有image
和label
。 主要的问题是当我下载这些图像,我按下一个项目来查看它的细节, main thread
被阻止,应用程序不会push
送到DetailsController
直到所有的图像下载。
有没有人处理与许多元素和图像表? 你怎么解决这个问题?
谢谢
我遇到了一个非常类似的问题,这就是我如何解决这个问题。 如果有人有更好的解决scheme,我会感兴趣,但是这工作得很好。 基本上,你需要估计延迟多长时间来传输每个图像,并确保你只能经常发送它们。 如果您尝试一次全部发送,则会阻塞主线程。
我试图把我的代码烧到这个解决scheme的基本部分。 你不需要像我这样命名你的图像。 关键部分是如何使用队列。 当你需要停止发送图像时调用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]; } } }
在后台线程上caching图像是安全的,所以从你的WatchKit扩展,调度到后台队列,并使用WKInterfaceDevice
和它的addImage
风格的方法之一。 然后,确保在实际更新接口之前将其发回主队列。
我在WatchKit Image Tips中更详细地介绍了其中的一些技巧 。