Загрузка изображений в таблицу в наборе часов 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".

Другие вопросы по тегам