title: iOS: Is there a way to determine when an UIView did appear, after calling view.hidden = NO
body: <p>I have an UIView with 8 to 10 large images (each loaded into an UIImageView) as subviews. Those images are aligned next to each other, so that they compose a real long horizontal image. This view is put into an UIScrollView, so that the user can scroll along this long horizontal image.</p>
  
  <p>Loading all those images (from the web) takes quite some time, so I decided to put that loading code into a NSOperation subclass. Inside the operation's main method each ImageView gets added to the container view.</p>
  
  <p>The view is hidden during this process and instead there is a small rotating image (pretty much like an UIActivityIndicatorView). Here is some code:</p>
  
  <p>CategoryViewController.m</p>
  
  <pre><code>- (id)initWithFrame:(CGRect)frame
  {
      self = [super init];
      if(self)
      {
          // do some unrelated stuff
          [...]
  
          loadIndicator = [[UIImageView alloc] initWithImage:img];
          [loadIndicator setFrame:CGRectMake((frame.size.width - width) / 2, (frame.size.height - height) / 2, width, height)];
          [loadIndicator setHidden:YES];
  
          // this is the view which will contain those large images
          backgroundView = [[UIView alloc] init];
          backgroundView.hidden = YES;
  
          scrollView = [[UIScrollView alloc] initWithFrame:frame];
          [scrollView setDelegate:(id&lt;UIScrollViewDelegate&gt;)self];
          [scrollView addSubview:backgroundView];  
  
          [self.view setFrame:frame];
      }
      return self;
  }
  
  - (void)viewDidLoad
  {
      // do some unrelated stuff
      [...]
  
      [self.view addSubview:loadIndicator];
      [self.view addSubview:scrollView];
  
      // setup a custom activity indicator
      loadIndicator.hidden = NO;
      [UIView beginAnimations:nil context:NULL];
      [UIView setAnimationDelegate:self];
      [UIView setAnimationCurve:UIViewAnimationCurveLinear];
      loadIndicatorRotation = 0;
      rotationTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 target: self selector:@selector(updateRotation:) userInfo: nil repeats: YES];
      [UIView commitAnimations];
  
      // setup the operation to load all background images
      operationQueue = [[NSOperationQueue alloc] init];
      LoadBackgroundImagesOperation* operation = [[LoadBackgroundImagesOperation alloc] initWithImages:bgImageNames View:backgroundView Frame:frame];
  
      // register to recieve the finish notification
      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onFinishedLoadingBgImages) name:@"BgImagesFinishedLoading" object:operation];
  
      // Add the operation to the queue
      [operationQueue addOperation:operation];
      [operation release];
      [super viewDidLoad];
  }
  </code></pre>
  
  <p>main method of LoadBackgroundImagesOperation.m</p>
  
  <pre><code>- (void)main
  {
      int left = 0;
      int width, height;
  
      int i;
      for(i = 0; i &lt; imageNames.count; i++)
      {
          if([self isCancelled])
          {
              [[NSNotificationCenter defaultCenter] postNotificationName:@"BgImagesCancelled" object:self];
              return;
          }
  
          // code to retrieve the url of current image
          [...]
  
          NSData* data = [[NSData alloc] initWithContentsOfURL:imgURL];
          UIImage* image = [[UIImage alloc] initWithData:data];
          [data release];
  
          double scalingFactor = frame.size.height / image.size.height;
          width = image.size.width * scalingFactor;
          height = image.size.height * scalingFactor;
  
          UIImageView* imgView = [[UIImageView alloc] initWithImage:image];
          [image release];
  
          [imgView setFrame:CGRectMake(left, 0, width, height)];
          [background addSubview:imgView];
          [imgView release];
  
          left += width;
          [background setFrame:CGRectMake(0, 0, left, height)];
      }
      [[NSNotificationCenter defaultCenter] postNotificationName:@"BgImagesFinishedLoading" object:self];
  }
  </code></pre>
  
  <p>When the operation is finished I hide the rotating image and show the view, which contains the large images.</p>
  
  <p>(back in CategoryViewController.m again)</p>
  
  <pre><code>- (void) onFinishedLoadingBgImages
  {
      [scrollView setContentSize:backgroundView.frame.size];
  
      backgroundView.hidden = NO;
      loadIndicator.hidden = YES;
  
      if(operationQueue != nil)
      {
          [operationQueue cancelAllOperations];
          [operationQueue release];
          operationQueue = nil;
      }
      [rotationTimer invalidate];
      rotationTimer = nil;
  }
  </code></pre>
  
  <p>The problem is that it takes around 2 seconds for the simulator and up to 5 seconds on the real device untill the images are actually visible after calling <code>backgroundView.hidden = NO</code></p>
  
  <p>I would just continue showing the activity indicator during that time, but I don't know how to detect that <code>backgroundview</code> did appear in order to hide the indicator again.</p>
  
  <p>I tried to put <code>backgroundview</code> in its own UIViewController and notify my parent view controller when viewDidAppear is called, but that doesn't work. (I read that in nested view controllers I would have to call viewDidAppear manually, but that is not an option, because i don't know when it did appear in the first place).</p>
  
  <p>Also, I can't just do the dirty trick and change the order of <code>backgroundView</code> and <code>loadIndicator</code> to hide it, because most of the images have some transparency in them, so the user would still see the indicator.</p>
  
  <p>So I really hope, that any of you guys have a suggestion on how to detect when <code>backgroundview</code> is actually visible.</p>
  
  <p>Thanks,
  Chris</p>
  
  <p>Oh, and sorry if some of the code is somewhat unrelated to the problem. Just wanted to give you the big picture of what I am doing.</p>