我正在开发一个WatchKit
应用,我需要获取一些图像(有时是50个缩略图)来实现Table
。我正在iOS
应用中下载图片并将其传递给WatchKit Extension
,但我遇到了问题。
首先,我有三个按钮,如果我按其中一个按钮,我会看到Table
包含一些元素,所有元素都包含image
和label
。主要问题是当我下载这些图片并按下一个项目以查看其详细信息时,main thread
被屏蔽,应用程序无法push
到DetailsController
,直到所有图像都下载了。
有没有人处理过具有许多元素和图像的表?你是怎么解决这个的?
谢谢
答案 0 :(得分:0)
我遇到了一个非常类似的问题,这就是我解决它的方法。如果有人有更好的解决方案我会感兴趣,但这非常有效。基本上,您需要估计传输每个图像的延迟时间,并确保您只经常发送它们。如果你试图一次性发送它们,你将阻止主线程。
我试图将我的代码简化为此解决方案的基本部分。您不需要像我一样命名您的图像。关键部分是如何使用队列。当您需要停止发送图片时,请致电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];
}
}
}
答案 1 :(得分:0)
将图像缓存在后台线程上是安全的,因此从WatchKit扩展程序调度到后台队列,并使用WKInterfaceDevice
及其addImage
样式方法之一。然后,在实际更新界面之前,请务必调度回主队列。
我在WatchKit Image Tips帖子中详细介绍了其中一些技巧。