1

I'm trying to load images in the background using gcd. My first attempt didn't work:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{
  dispatch_async(dispatch_get_main_queue(), ^{
    // create UI Image, add to subview
  });
});

commenting out the background queue code and leaving just the main queue dispatch block didn't work:

dispatch_async(dispatch_get_main_queue(), ^{
  // create UI Image, add to subview
});

Is there some trick to doing ui stuff inside a gcd block?

In response to mattjgalloway's comment, I'm simply trying to load a big image in a background thread. Here's the full code that I tried originally:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{

    UIImage* img = [[UIImage alloc] initWithContentsOfFile: path];

    dispatch_async(dispatch_get_main_queue(), ^{

       imageView = [[[UIImageView alloc] initWithImage:  img] autorelease];
       imageView.contentMode = UIViewContentModeScaleAspectFill;
       CGRect photoFrame = self.frame;
       imageView.frame = photoFrame;
       [self addSubview:imageView];

  });

});

I simplified it so that I was running everything inside the main queue, but even then it didn't work. I figure if I can't get the whole thing to work in the main queue, no way the background queue stuff will work.


================ UPDATE ==============

I tried the above technique (gcd and all) in a brand new project and it does indeed work as expected. Just not in my current project. So I'll have to do some slow, painful process of elimination work to figure out what's going wrong. I'm using this background loading to display images in a uiscrollview. Turns out bits of the images do sometimes show up, but not always. I'll get to the bottom of this....

================ UPDATE ==============

Looks like the issue is related to the UIScrollView all of this is inside. I think stuff isn't getting drawn/refreshed when it should

morgancodes
  • 25,055
  • 38
  • 135
  • 187
  • What are you trying to achieve? You don't really want to just dispatch onto background queue and then the main queue - what did you expect that to achieve? Show us some more code of what you have tried. – mattjgalloway May 21 '12 at 21:30
  • @mattjgalloway Simply trying to load the image in the background. More detailed code pasted above. – morgancodes May 21 '12 at 21:47
  • The images don't show up. If I comment out everything involving gcd and blocks, they do show up. – morgancodes May 21 '12 at 22:06

3 Answers3

1

I ran your code. It works with one exception. Also did you mean self.view rather than self?

Case 1:

path is declared at a property.
imageView is declared at an ivar

- (IBAction)buttonImagePressed:(id)sender 
{
    self.path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"picture1.jpg"];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{
        UIImage* img = [[UIImage alloc] initWithContentsOfFile: self.path];  
        dispatch_async(dispatch_get_main_queue(), ^{
            imageView = [[[UIImageView alloc] initWithImage:  img] autorelease];
            imageView.contentMode = UIViewContentModeScaleAspectFill;
            CGRect photoFrame = self.view.frame;
            imageView.frame = photoFrame;
            [self.view addSubview:imageView];
        });
    });
}

Case 2:

path and imageView are both ivars. - no property is used.

- (IBAction)buttonImagePressed:(id)sender 
{
// will crash if path is defined here
//    path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"picture1.jpg"];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{
        //if path is defined here then it will work
        path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"picture1.jpg"];
        UIImage* img = [[UIImage alloc] initWithContentsOfFile:path];
        dispatch_async(dispatch_get_main_queue(), ^{
            imageView = [[[UIImageView alloc] initWithImage:  img] autorelease];
            imageView.contentMode = UIViewContentModeScaleAspectFill;
            CGRect photoFrame = self.view.frame;
            imageView.frame = photoFrame;
            [self.view addSubview:imageView];
        });
    });
}

On case 2, where it crashed, the message in the console said to file a bug at apple.com ...

user523234
  • 14,323
  • 10
  • 62
  • 102
  • Hmm. Thanks. I meant view, not self.view. My code is running inside a UIView subclass, which is trying to add a subview to itself. So case 2 (as presented, with the commented code commented) worked fine for you? – morgancodes May 22 '12 at 13:55
  • Yes. And I tried my code above within a subclass of UIView and experiencing a similar behavior. There is a bug. – user523234 May 24 '12 at 00:07
1

Try to call setNeedDisplay right after [self addSubview:imageView], by the way most of UIKit isn't thread safe, I don't know about UIImage methods specifically (maybe is ok on iOS4 and higher), but I wouldn't do that. If you want to load images from background better use ImageIO that is thread safe, or Core Graphics functions.
An alternative could be load an NSData object with your image data on a background thread an later call [UIImage alloc]inithWithData: data] on the main thread.

Andrea
  • 26,120
  • 10
  • 85
  • 131
0

Displaying my image as a background image, rather than a UIImageView added as a subview works. No clue why

@interface PhotoView(){
  UIImageView* imageView;
}

@end


@implementation PhotoView

-(id)initWithFrame:(CGRect)frame andPathToPhoto:(NSString*)path andSequenceView:(SequencerView*) sequencerView{
  if(self = [super initWithFrame:frame]){


    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{
      UIImage* img = [[UIImage alloc] initWithContentsOfFile: path];
      dispatch_async(dispatch_get_main_queue(), ^{
        self.backgroundColor = [UIColor colorWithPatternImage:img];
      });
      [img release];
    });

  }
  return self;
}

@end
morgancodes
  • 25,055
  • 38
  • 135
  • 187