title: Paged UIScrollview with lazy delayed loading
body: <p>I need help with the uiscrollview implementation with the following requirements:</p>
  
  <p>1) pagingEnabled = true.<br>
  2) lazy pages loading.<br>
  3) pages are loading in the background. So i need at first run loading the page X, then get the notification that the page is fully loaded and only then allow the user to scroll to it.<br>
  4) ability to change the current page.</p>
  
  <p>My first attempt was to override the scrollViewDidEndDeacelerating and scrollViewDidScroll, but I had troubles with stucking on half of pages (when you stop the scroll on the half of the page and then wait for new page to add to the scroll) and empty pages (when the user scrolled too fast).  </p>
  
  <p>My second attempt was to override the layoutSubviews method of the UIScrollView and do all calculations there. But it seems to be very sofisticated.</p>
  
  <p>So, I'd love to find any examples of similar implementations.</p>
  
  <p><strong>Now I have the code like this:</strong></p>
  
  <p>I've implemented <em>scrollViewWillBeginDragging</em> and <em>scrollViewDidEndDecelerating</em>:</p>
  
  <pre><code>- (void)scrollViewWillBeginDragging:(UIScrollView *)aScrollView
  {
      isScrolling = YES;
  } 
  - (void)scrollViewDidEndDecelerating:(UIScrollView *)aScrollView
  {
     isScrolling = NO;
     // Here we load the page that was prepared when the user was scrolling
    if (needDisplay &gt; -1) {
          //NSLog(@"Loading queued page %d", needDisplay);
          [self loadScrollViewWithPage:needDisplay forceAddToSuperview:NO animated:YES];
          needDisplay = -1;
    }
  
    // minLoadedPageIndex - index of the first loaded page.
    int selectedIndex = MIN(floor((aScrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1 + minLoadedPageIndex, [photoItems count] - 1); 
  
      [self loadScrollViewWithPage:selectedIndex - 1 forceAddToSuperview:NO animated:YES];
      [self loadScrollViewWithPage:selectedIndex forceAddToSuperview:NO animated:YES];
      [self loadScrollViewWithPage:selectedIndex + 1 forceAddToSuperview:NO animated:YES];
  }
  </code></pre>
  
  <p>In <em>loadScrollViewWithPage</em> I create the page view controller which loads the data from the server in the background. I don't add the view to the scrollview until it loads the data from the server.</p>
  
  <pre><code>- (void)loadScrollViewWithPage:(int)page forceAddToSuperview:(BOOL)value animated:(BOOL)animated
  {
      DetailController *controller = page &gt;= viewControllers.count ? [NSNull null] :[viewControllers objectAtIndex:page];
  
      if ((NSNull *)controller == [NSNull null])
      {
          controller = [[DetailController alloc] initWithNibName:@"DetailController" bundle:nil];        
          controller.delegate = self;
          controller.view.hidden = NO; //this will call viewDidLoad.
  
          if (page &gt;= viewControllers.count) {
              [viewControllers addObject:controller];
          }
          else {
              [viewControllers replaceObjectAtIndex:page withObject:controller];
          }
  
          [controller release];
      }
  
      // add the controller's view to the scroll view
      if (controller.view &amp;&amp; controller.view.superview == nil &amp;&amp; (controller.isLoadedOrFailed || value)) {
          [self setNumberOfVisiblePages:visiblePagesCount+1];  
  
          if (page &lt; selectedIndex) {
             // We are adding the page to the left of the current page,
       // so we need to adjust the content offset.
              CGFloat offset = (int)scrollView.contentOffset.x % (int)scrollView.frame.size.width;
              offset = scrollView.frame.size.width * (selectedIndex - minLoadedPageIndex) + offset;
              [scrollView setContentOffset:CGPointMake(offset, 0.0) animated:animated];       
          }
  
          CGRect frame = scrollView.frame;
          frame.origin.x = frame.size.width * (page - minLoadedPageIndex);
          frame.origin.y = 0;
          controller.view.frame = frame;        
          [scrollView addSubview:controller.view];
          [controller viewWillAppear:NO];
      }
  }
  </code></pre>
  
  <p>Also I have a <em>detailControllerDidFinishDownload</em> method which is called when data for the page view controller has been loaded.</p>
  
  <pre><code>- (void)detailControllerDidFinishDownload:(DetailController *)controller
  {
  
    ... //here I calculate new minLoadedPageIndex value
  
   // reset pages frames
   if (minLoadedPageIndex &lt; oldMinPage) {
          for(int i=oldMinPage;i &lt; [viewControllers count]; i++) {
              DetailController *detailsController = [viewControllers objectAtIndex:i];
              if ((NSNull *)detailsController != [NSNull null]) {
                  CGRect frame = scrollView.frame;
                  frame.origin.x = frame.size.width * (i - minLoadedPageIndex);
                  frame.origin.y = 0;
  
                  [detailsController.view setFrame:frame];
              }
          }
      }
  
     // load the page now or delay load until the scrolling will be finished
     if (!isScrolling) {
          [self loadScrollViewWithPage:[photoItems indexOfObject:controller.photoItem] forceAddToSuperview:NO animated:NO];
      }
      else {
          needDisplay = [photoItems indexOfObject:controller.photoItem];
          //NSLog(@"pageControlUsed is used!!! %d", [photoItems indexOfObject:controller.photoItem]);
      }
  }
  </code></pre>
  
  <p>The problem I have now is that sometimes the scroll stucks on the middle (or somewhere near to middle) of the pages and it won't go to the nearest page bounce until I slightly more it. My tests showed that this situation happens if I scroll out of content view frame (the scroll view have bounces on) and wait for the new page to load. In 1 of the 10 times the scroll stucks. </p>
  
  <p>Thanks, Mikhail!</p>