Правильное использование Grand Central Dispatch с несколькими UIActivityIndicatorViews - PullRequest
0 голосов
/ 20 сентября 2011

Я создаю программу просмотра миниатюр, состоящую из кнопок UIB, и хочу, чтобы в каждой кнопке был вращатель, пока изображение не будет заполнено из облака. Раньше я делал это с одиночными прядильщиками, но никогда в массовом порядке. Спиннеры не появляются (может быть, проблема с пользовательским интерфейсом неосновной темы?) ... Я, по крайней мере, движусь в правильном направлении? Заранее спасибо.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_queue_t queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_apply(numberOfThumbs, queue, ^(size_t i){
    Post *post = [arrayOfPosts objectAtIndex:i];
    UIActivityIndicatorView *newSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    UIButton *currentThumb = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
    [currentThumb setFrame:CGRectMake((105.5 * (i%3))+3.5, ((i/3)*105.5) + 3.5, 102, 102)];
    [newSpinner setFrame:CGRectMake(54 - spinner.frame.size.width/2, 54 - spinner.frame.size.height/2, spinner.frame.size.width, spinner.frame.size.height)];
    [currentThumb addSubview:newSpinner];
    [newSpinner startAnimating];
    [thumbnails addObject:currentThumb];
    dispatch_async(queue2, ^(void){
        [currentThumb setImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:post.thumbUrl]]] forState:UIControlStateNormal];
        [newSpinner stopAnimating];
        [newSpinner removeFromSuperview];
        [newSpinner release];
    });
    [currentThumb release];
});
dispatch_release(queue);
dispatch_release(queue2);

Благодаря предложению Даниэля и Брэда, ниже, кажется, работает следующий танец:

    - (id)initWithArrayOfPosts:(NSArray*)posts
    {
        self = [super initWithNibName:nil bundle:nil];
        if (self) {
            // Custom initialization
            arrayOfPosts = [posts retain];
            numberOfThumbs = [arrayOfPosts count];
            oldDequeueFactor = 0;
            oldYOffset = 0.0;
            thumbnails = [[NSMutableArray alloc] initWithCapacity:15];
            mainScrollView = [[UIScrollView alloc] init];
            [mainScrollView setFrame:self.view.frame];
            [mainScrollView setContentSize:CGSizeMake(self.view.frame.size.width, 109 * (ceilf((float)numberOfThumbs/3.0)))];
            [mainScrollView setDelegate:self];
            [mainScrollView setBackgroundColor:[UIColor whiteColor]];
            [self setView:mainScrollView];

            for (int i=0; i<numberOfThumbs; i++) {
                UIActivityIndicatorView *newSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
                UIButton *currentThumb = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
                [currentThumb setFrame:CGRectMake((105.5 * (i%3))+3.5, ((i/3)*105.5) + 3.5, 102, 102)];
                [newSpinner setFrame:CGRectMake(54 - newSpinner.frame.size.width/2, 54 - newSpinner.frame.size.height/2, newSpinner.frame.size.width, newSpinner.frame.size.height)];
                [currentThumb addSubview:newSpinner];
                [newSpinner startAnimating];
                [thumbnails addObject:currentThumb];
                [currentThumb release];
                [mainScrollView addSubview:[thumbnails objectAtIndex:i]];
                [self getImageDataAsynchronouslyForThumbnail:[thumbnails objectAtIndex:i] forIndex:i andRemoveSpinner:newSpinner];
            }
        }
        return self;
    }

    - (void)getImageDataAsynchronouslyForThumbnail:(UIButton*)thumb forIndex:(int)index andRemoveSpinner:(UIActivityIndicatorView *)spinner
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

    dispatch_async(queue, ^(void){
        Post *post = [arrayOfPosts objectAtIndex:index];
        UIImage *thisImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:post.thumbUrl]]];
        NSArray *args = [NSArray arrayWithObjects:thisImage, thumb, spinner, nil];
        if ([args count] == 3) { //use this array to verify I have all info I need
            dispatch_sync(dispatch_get_main_queue(), ^(void) {
                [self setImage:[args objectAtIndex:0] ForThumbnail:[args objectAtIndex:1] andStopAnimatingSpinner:[args objectAtIndex:2]];
            });
        } else {
            [self getImageDataAsynchronouslyForThumbnail:thumb forIndex:index andRemoveSpinner:spinner];
            return;
        }
    });

    dispatch_release(queue);

}

- (void)setImage:(UIImage*)image ForThumbnail:(UIButton *)thumb andStopAnimatingSpinner:(UIActivityIndicatorView *)spinner
{
    [thumb setImage:image forState:UIControlStateNormal];
    [spinner stopAnimating];
    [spinner removeFromSuperview];
    [spinner release];
}

1 Ответ

1 голос
/ 20 сентября 2011

Да, я полагаю, что ваша проблема в том, что вы выполняете код из фонового потока, UIKit не является потокобезопасным, поэтому вам, вероятно, следует только получить данные изображения в фоновом режиме, а затем использовать executeSelectorOnMainTHread, чтобы установить изображение и остановить анимацию, что-токак

-(void)setDataStopAnimating:(NSArray*)args
{
   //get your arguments from the array and then run
 [thumb setImage:[UIImage imageWithData:imageData] forState:UIControlStateNormal];
                [newSpinner stopAnimating];
                [newSpinner removeFromSuperview];
                [newSpinner release];
}

//then in your queue code
      dispatch_async(queue2, ^(void){
               NSArray *args; //fill the array with the arguments
                [self performSelectorOnMainThread:@selector(setDataAndStopAnimating:) withObject:args] 
            });
...