Search code examples
iosobjective-cuiviewblockdispatch-async

How to avoid twice adding UIView on not ended block?


I have a "loading" view added to an uiimageview to indicate the image is being loaded, then on a dispatch async I am charging the image and removing the "loading" view once it has finished, problem is that if I call twice this method the "loading" view is added twice and the second one is never removed.

Edited:

on view.h

UIView *loading;

on view.m

loading = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
loading.backgroundColor = [UIColor blackColor];
loading.alpha = 0.8;
loading.layer.cornerRadius = 10;
loading.center = CGPointMake(imageView.frame.size.width/2, imageView.frame.size.height/2);

if (![imageView.subviews containsObject:loading]) {
    [imageView addSubview:loading];
}
dispatch_queue_t downloadFoto = dispatch_queue_create("Get Photo", NULL);
dispatch_async(downloadFoto, ^{
    UIImage *image = [[UIImage alloc]initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[selectedImage objectForKey:@"url"]]]];

    dispatch_sync(dispatch_get_main_queue(), ^{
        if (image) {
            [imageView setImage:image];
            [imageView setNeedsLayout];
            if ([imageView.subviews containsObject:loading]) {
                [loading removeFromSuperview];
            }
        }
    });
});

If this is called just once or if I call it after the loading is already removed everything works fine, the problem is if I call this before the block had finished.

Thank you guys, this is my solution at the end.

if (!blackView) {
UIActivityIndicatorView *load = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
blackView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
blackView.backgroundColor = [UIColor blackColor];
blackView.alpha = 0.8;
blackView.layer.cornerRadius = 10;
blackView.center = CGPointMake(fotoVisual.frame.size.width/2, fotoVisual.frame.size.height/2);
load.center = CGPointMake(blackView.frame.size.width/2, blackView.frame.size.height/2);
[load startAnimating];
[blackView addSubview:load];


[fotoVisual setImage:[UIImage imageNamed:@"previewImagen.png"]];
descripcionFotoView.text = [selectedImage objectForKey:@"titulo"];

}
if (![fotoVisual.subviews containsObject:blackView]) {
    [fotoVisual addSubview:blackView];
}
dispatch_queue_t downloadFoto = dispatch_queue_create("Get Photo", NULL);
dispatch_async(downloadFoto, ^{
    [fotoVisual setImageWithURL:[NSURL URLWithString:[selectedImage objectForKey:@"url"]]
               placeholderImage:[UIImage imageNamed:@"previewImagen.png"]];
    dispatch_async(dispatch_get_main_queue(), ^{
            [blackView removeFromSuperview];
            blackView = nil;
    });
});
dispatch_release(downloadFoto);

Solution

  • It looks like the issue you have is that you are creating a new loading view each time, which is not what you want. Your [imageView.subviews containsObject:loading] will never be true as you make a new loading view each time.

    You could change the creation logic to do the checking like this

    if (!loading) {
      // configure loading
      [imageView addSubview:loading];
    }
    

    Then modify your call back to something like

    dispatch_sync(dispatch_get_main_queue(), ^{
      if (image) {
        [imageView setImage:image];
        [imageView setNeedsLayout];
        [loading removeFromSuperview];
        loading = nil;
      }
    });
    

    Is there any specific reason you are using dispatch_sync instead of dispatch_async?