0

我正在开发一个WatchKit应用程序,我需要获取一些图像(有时是 50 个缩略图)来完成一个Table. 我正在下载iOS应用程序中的图像并将它们传递给WatchKit Extension,但我遇到了问题。

首先,我有三个按钮,如果我按下其中一个按钮,我会看到 aTable带有一些元素,它们都带有imageand label。主要问题是当我下载这些图像并按下一个项目以查看其详细信息时,它main thread被阻止并且应用程序在下载所有图像之前不会push生成。DetailsController

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

谢谢

4

2 回答 2

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];
        }
    }

}
于 2015-05-28T16:37:57.667 回答
0

在后台线程上缓存图像是安全的,因此从您的 WatchKit 扩展,分派到后台队列,并使用WKInterfaceDevice它的addImage-style 方法之一。然后,确保在实际更新接口之前调度回主队列。

我在我的WatchKit Image Tips帖子中更详细地介绍了其中一些技术。

于 2015-05-28T17:29:59.863 回答