title: Defining scroll amount with UIScrollView and PageControl
body: <p>For the application I am working on I need a scrollview with paging along with a number of views (in this case tableviews). Setting this up (using Apple's PageControl example) is easy enough and I have no problem making it work in regards to scrolling and snapping between pages. I want the tableviews to be almost like thumbnail representations of the tableviews in the scrollview. Once you click one it opens or zooms in to let the tableview cover the entire screen. Again, setting this up is not really a problem, I can make the views inside the scrollview any size I want and the clicking I'm sure I can get to work when I get to that part. :)  </p>
  
  <p>My Problem is this: In any given view, regardless of what page you happen to be on I want the next page and the previous page to be slightly visible on the right and left side respectively. Of course on the first page there would only be one more page shown on the right side, or left side for the last page. Again, by setting the x-origin of the views (tableviews) shown I can get the first page to look right. However, as soon as you scroll the problem becomes apparent, namely that the screen scrolls too far.  </p>
  
  <p>Breaking it down I guess my main issue is that I can't seem to change the amount of scrolling done when switching pages.  </p>
  
  <p>I'd appreciate any help I can get on this, thank you.</p>
  
  <p>AppDelegate.h   </p>
  
  <pre><code>#import &lt;UIKit/UIKit.h&gt;
  
  @interface AppDelegate : NSObject &lt;UIApplicationDelegate, UIScrollViewDelegate&gt; {
  UIWindow *window;
  UIView *background;
  UIScrollView *scrollView;
  UIPageControl *pageControl;
  NSMutableArray *viewControllers;
  
  // To be used when scrolls originate from the UIPageControl
  // I've commented this out (and all references to it) since I've disabled user interaction with the UIPageControl
  //BOOL pageControlUsed;
  }
  
  @property (nonatomic, retain) IBOutlet UIWindow *window;
  @property (nonatomic, retain) IBOutlet UIView *background;
  
  @property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
  @property (nonatomic, retain) IBOutlet UIPageControl *pageControl;
  @property (nonatomic, retain) NSMutableArray *viewControllers;
  
  - (IBAction)changePage:(id)sender;
  
  @end  
  </code></pre>
  
  <p>AppDelegate.m</p>
  
  <pre><code>#import "AppDelegate.h"
  #import "MyViewController.h"
  
  static NSUInteger kNumberOfPages = 4;
  
  @interface AppDelegate (PrivateMethods)
  
  - (void)loadScrollViewWithPage:(int)page;
  - (void)scrollViewDidScroll:(UIScrollView *)sender;
  
  @end
  
  @implementation AppDelegate
  
  @synthesize window, scrollView, pageControl, viewControllers, background;
  
  - (void)dealloc {
  [background release];
  [viewControllers release];
  [scrollView release];
  [pageControl release];
  [window release];
  [super dealloc];
  }
  
  - (void)applicationDidFinishLaunching:(UIApplication *)application {
  
  background.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Background.png"]];
  
  // view controllers are created lazily
  // in the meantime, load the array with placeholders which will be replaced on demand
  NSMutableArray *controllers = [[NSMutableArray alloc] init];
  for (unsigned i = 0; i &lt; kNumberOfPages; i++) {
      [controllers addObject:[NSNull null]];
  }
  self.viewControllers = controllers;
  [controllers release];
  
  // a page is the width of the scroll view
  scrollView.pagingEnabled = YES;
  scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height);
  scrollView.showsHorizontalScrollIndicator = NO;
  scrollView.showsVerticalScrollIndicator = NO;
  scrollView.scrollsToTop = NO;
  scrollView.delegate = self;
  
  pageControl.numberOfPages = kNumberOfPages;
  pageControl.currentPage = 0;
  
  // pages are created on demand
  // load the visible page
  // load the page on either side to avoid flashes when the user starts scrolling
  [self loadScrollViewWithPage:0];
  [self loadScrollViewWithPage:1];
  }
  
  - (void)loadScrollViewWithPage:(int)page {
  if (page &lt; 0) return;
  if (page &gt;= kNumberOfPages) return;
  
  // replace the placeholder if necessary
  MyViewController *controller = [viewControllers objectAtIndex:page];
  if ((NSNull *)controller == [NSNull null]) {
      controller = [[MyViewController alloc] initWithPageNumber:page];
      [viewControllers replaceObjectAtIndex:page withObject:controller];
      [controller release];
  }
  
  // add the controller's view to the scroll view
  if (nil == controller.view.superview) {
      CGRect frame = CGRectMake(50, 50, 220, 330);
      frame.origin.x = 50 + (250 * page);
      frame.origin.y = 50;
      controller.view.frame = frame;
      [scrollView addSubview:controller.view];
  }
  }
  
  - (void)scrollViewDidScroll:(UIScrollView *)sender {
  // We don't want a "feedback loop" between the UIPageControl and the scroll delegate in
  // which a scroll event generated from the user hitting the page control triggers updates from
  // the delegate method. We use a boolean to disable the delegate logic when the page control is used.
  /*if (pageControlUsed) {
      // do nothing - the scroll was initiated from the page control, not the user dragging
      return;
  }*/
  
  // Switch the indicator when more than 50% of the previous/next page is visible
  CGFloat pageWidth = scrollView.frame.size.width;
  int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
  pageControl.currentPage = page;
  
  // load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
  [self loadScrollViewWithPage:page - 1];
  [self loadScrollViewWithPage:page];
  [self loadScrollViewWithPage:page + 1];
  
  // A possible optimization would be to unload the views+controllers which are no longer visible
  }
  
  // At the begin of scroll dragging, reset the boolean used when scrolls originate from the UIPageControl
  - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
  //pageControlUsed = NO;
  }
  
  // At the end of scroll animation, reset the boolean used when scrolls originate from the UIPageControl
  - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
  //pageControlUsed = NO;
  }
  
  - (IBAction)changePage:(id)sender {
  int page = pageControl.currentPage;
  
  // load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
  [self loadScrollViewWithPage:page - 1];
  [self loadScrollViewWithPage:page];
  [self loadScrollViewWithPage:page + 1];
  
  // update the scroll view to the appropriate page
  CGRect frame = scrollView.frame;
  frame.origin.x = frame.size.width * page;
  frame.origin.y = 0;
  [scrollView scrollRectToVisible:frame animated:YES];
  
  // Set the boolean used when scrolls originate from the UIPageControl. See       scrollViewDidScroll: above.
  //pageControlUsed = YES;
  }
  
  @end
  </code></pre>