下载监视工具包异步表中的图像

时间:2015-05-28 15:57:57

标签: ios objective-c watchkit apple-watch wkinterfaceimage

我正在开发一个WatchKit应用,我需要获取一些图像(有时是50个缩略图)来实现Table。我正在iOS应用中下载图片并将其传递给WatchKit Extension,但我遇到了问题。

首先,我有三个按钮,如果我按其中一个按钮,我会看到Table包含一些元素,所有元素都包含imagelabel。主要问题是当我下载这些图片并按下一个项目以查看其详细信息时,main thread被屏蔽,应用程序无法pushDetailsController,直到所有图像都下载了。

有没有人处理过具有许多元素和图像的表?你是怎么解决这个的?

谢谢

2 个答案:

答案 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帖子中详细介绍了其中一些技巧。