iPhone – UIScrollView with Paging Example

I have downloaded the PageControl example from developer.apple.com but i don’t know the reason why i cannot open the .xib in Interface Builder. Anyway, i cannot make use of this example for my purpose as i don’t how to remove the UIPageControl without using the Interface Builder.

So i follow the example to create a simple UIScrollView with paging feature by scrolling the screen in horizontal direction.

GitHub:ScrollViewWithPaging – Xcode project

The project contains 3 classes:

  • ScrollViewWithPagingAppDelegate – The AppDelegate of the project
  • ScrollViewWithPagingViewController – The view controller of ScrollViewWithPagingView.xib. It has a NSMutableArray which contains all the MyViewController instances. Each MyViewController instance is one page in the UIScrollView.
  • MyViewController – The a simple view controller which only shows the page number in MyViewController.xib with different background colors.

Build a run the project

It should run well in in iPhone SDK 3.x.

Done =)

Reference: iPhone OS Reference Library – PageControl

Update @ 2012-08-29: If you want to make it work on storyboard, please refer to this comment.

About these ads

97 thoughts on “iPhone – UIScrollView with Paging Example

  1. Kevin

    You can not open the xib file in the apple’s sample code is because the xib file has been localized. You can click the small triangle beside the xib file’s name, in the pull down list, you could see an en xib file, double click it then you can open the xib file.

    Reply
  2. bicbac

    great tutorial. one question. how can i start from page X? I tried with pageControl.currentPage = 3; in ScrollViewWithPagingViewController but it changed nothing. cheers

    Reply
    1. ykyuen Post author

      Can u try the following in ScrollViewWithPagingViewController.m.

      - (void)viewDidLoad {
          ...
          pageControl.numberOfPages = kNumberOfPages;
          pageControl.currentPage = 3;
      
          // 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:2];
          [self loadScrollViewWithPage:3];
          [self loadScrollViewWithPage:4];
      }
      
      Reply
      1. bicbac

        I tried. Once launched empty grey screen, and then scroll a bit gives us the first red page show up suddenly.

      2. ykyuen Post author

        Sorry the i dun have a Mac machine right now so i cannot try the code by myself. but i think the following code should work.

        - (void)viewDidLoad {
            ...
            pageControl.numberOfPages = kNumberOfPages;
            pageControl.currentPage = 3;
        
            // 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:2];
            [self loadScrollViewWithPage:3];
            [self loadScrollViewWithPage:4];
        
            // move the scrollview to page 3
            CGRect frame = scrollView.frame;
            frame.origin.x = frame.size.width * 3;
            frame.origin.y = 0;
            [scrollView scrollRectToVisible:frame animated:NO];
        }
        
      3. bicbac

        Actually if we comment out the line ‘pageControl.currentPage = 3; ‘ the code still works fine. I guess the line determining first page is located at somewhere else.

      4. ykyuen Post author

        assume each page has width = 100:
        if i have 7 pages, the scrollview will be 700 wide and which page is shown depends on the scrollview.frame.origin.x.

        say i want to go to page 3 (here i mean array index = 3), i need to set the frame.origin.x = frame.size.width * 3 which equals to 300.
        The result is the page with label “Page 4″ is shown.

        if you didn’t set the pageControl.currentPage = 3 in viewDidLoad, then the frame.origin.x will equal to 0 by default which is a grey page as the first page has not yet preloaded.

      5. bicbac

        I made it working using ‘contentOffset’. came back to tell so someone might get help. your advice “frame.origin.x = frame.size.width * 3″ will be working as well. many thanks.

  3. Miles

    Thanks for the great tutorial. I couldn’t work out how to determine what page is on the screen when a button is pressed. I tried using if pageControl.currentpage == 1 NSLog(@”page 1″); for each page but always just got “page 1″ in the log.

    Thanks

    Reply
  4. Miles

    I am making a game where on the start page you can select the mode by scrolling through the pages to your choice of theme.

    StartViewController.h

    - (IBAction)start:(id)sender {
           if (pageControl.currentPage == 0) {
    		NSLog(@"Page 1 on screen");
    	}
    	
    	else if (pageControl.currentPage == 1) {
    		NSLog(@"Page 2 on screen");
    	}
    	
    	else if (pageControl.currentPage == 2) {
    		NSLog(@"Page 3 on screen");
    	}
    
    }
    
    - (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;
    	
    	
    }
    

    What is happening with this code is every time you tap start the console presents “Page 1 on screen.

    I can not tell what page is on the screen by which one loaded because the pages beside the one on the screen are also loaded to prevent flashes.

    Thanks in advance and sorry if this is a stupid question :P I’m a begginer.

    Reply
    1. ykyuen Post author

      Hi Miles,

      did you implement the scrollViewDidScroll()? i am not very sure as i do not have a mac to try now but as i go through the code again, the changePage() is not needed in my example. This is because it is called only when the page control exists in the layout for user to change the page without scrolling. but as you see in my example, i have removed the page control in the layout. i just keep it in the code for storing the current page when the view is scrolled.

      my example only allows changing pages by scrolling, when user scroll the page horizontally, the scrollViewDidScroll() is triggered the the pageControl.current page is set.

      feel free to ask if you have any problems, there is no stupid questions in my world and every question is good for stimulating human’s mind to think. =)

      Kit

      Reply
      1. Miles

        Hi Kit

        I have implemented scrollViewDidScroll() as well as using loadScrollViewWithPage() , scrollViewWillBeginDragging() and scrollViewDidEndDecelerating(). I assumed changePage was important as it updated pageControl.currentPage.

        Should I use the int “page” to determine what page is on screen or am I just using pageControl.currentPage incorectlly.

        Cheers,
        Miles

      2. ykyuen Post author

        Hi Miles,

        the scrollViewDidScroll() function will also set the pageControl.currentPage. so i think you should be able to get the current page from pageControl.currentPage.

        i have the following suggestions.
        1. You can add logs on both scrollViewDidScroll() and changePage() and run your program. Then you will know if changePage() is needed or not.

        2. Add logs to print the scrollView.contentOffset.x and pageWidth. these 2 values determine the int page which is then assigned to pageControl.currentPage.

        I am sorry that i dun have a Mac to try the code now. let me know what the result after u have tried them.

        Regards,
        Kit

      3. Miles

        Hey

        I used the scrollView.contentOffset.x to determine the postion which worked just how I had wanted.

        Thanks so much for your help.

        Miles

  5. carlo

    hi,
    i’m working on my first app and i need to implement a paging feature. problem is i need to have images inside the views can you tell me how to get images from the resources folder in the xcode project inside the individual views ?

    Reply
  6. James @ La Mode Outré

    Hi ykyuen,

    thanks for this – I’ve managed to get it working in a tabbarcontroller – can’t believe how easy it was! Just copied the files across and set the secondview of my tab to the ScrollViewWithPagingViewController.xib and it works! Amazing. Have spent all week trying to work out how to get this with a tabbarcontroller.

    However, and this is probably going to sound super thick – how to I now make it so I can scroll through images now instead of the coloured screens? Do I load the images into the *controllers NSMutableArray? and if so what is the code? I tried [controllers addObject:@"documentname.xxx"]; in place of [controllers addObject:[NSNull null]]; but it crashed.

    Do I need to create a viewcontroller for each image? Sorry, totally lost here.

    Thanks!

    James

    Reply
  7. James @ La Mode Outré

    and sorry… how would I add the optimization to release views not in use – I’m going to have about 80 images, so don’t want them all loaded. 3 at a time will be fine.

    Thank you, I appreciate any help you can give.

    Reply
      1. James @ La Mode Outré

        Thank you :)
        Sorry, I am a coding newcomer – so in ScrollViewWithPagingViewController.m do I just add the [aView release]; like:

        - (void)dealloc {
        [viewControllers release];
        [aView release];
        [scrollView release];
        [pageControl release];
        [super dealloc];
        }

        ?

        Also, and please don’t feel obliged to answer as this is already awesome help – is there a way to turn off vertical scrolling altogether? i.e. turn off any gesture that allows the user to scroll up and down, even to the bounce that currently occurs?

      2. ykyuen Post author

        U are welcome. =)

        memory management is really a headache to me. could u show me the code how you init the aView object? but actually if the application u are writing is not memory demanding. just make use of the autorelease method instead.

        If you want to turn off the vertical scrolling, just set a fixed scrollable height using the setContentSize (UIScrollView Class Reference). the following example fixed the scrollable width so it cannot be scrolled horizontally.
        Stack Overflow – how to disable vertical scrolling of scrollview iphone?

  8. James @ La Mode Outré

    Hey ykyuen,

    actually re: the images I am loading, I have used all the files in your project verbatim for controlling the scrollview, the only modification now is that I have replaced the colours with images in MyViewController.m as follows:

    + (UIColor *)pageControlColorWithIndex:(NSUInteger)index {
    if (__pageControlColorList == nil) {
    __pageControlColorList = [[NSArray alloc] initWithObjects:[[UIColor alloc] initWithPatternImage:[UIImage imageNamed:@"quote.jpg"]], nil];
    }

    The trouble is that I will have about 80 images and will be repeating the [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:@"xxxxxxx.jpg"]] as above for each image, so I want to release those that are not needed immediately by scrollview e.g. the views either side of the current one.

    So is there an easy way to release those, perhaps in ScrollViewWithPagingViewController.m? Happy to use the autorelease method, but I don’t know how to do it. Basically any suggestions on the consideration:
    “// A possible optimization would be to unload the views+controllers which are no longer visible”

    Any help appreciated :)

    Reply
    1. ykyuen Post author

      in the code, every initialized view is released after substituted the placehoder in the viewControllers.

      ...
      // 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];
      }
      ...
      

       

      So i think you dun need to release it in somewhere else. did u meet any problem?

      if you are new to Cocoa framework, i strongly suggest you going thru some tutorials. you could find some @ iPhone – Start Developing Your First iPhone Application

      Enjoy the Cocoa~~ =)

      Reply
      1. James @ La Mode Outré

        Hey ykyuen,

        I’ve just managed to test my app on iPad – it’s crashing due to memory running out and the images aren’t releasing from memory – any suggestions? Maybe the controllers are being replaced, but they aren’t being released from the memory.
        Any help appreciated.
        James

      2. ykyuen Post author

        sorry, i didn’t pay attention the the MyViewController. preloading 80 images is too much for the device. I suggest using NSDictionary for storing the viewing images as well as the adjacent images instead of using NSArray to store all of them.

        Then whenever the scrollview finish scrolling (try using the scrollViewDidEndDragging: method),
        release the that NSDictionary and init a new one with new key and new images.

        hope the above approach works and you can refer to Objective-C – NSDictionary Example and see how it works.

  9. James @ La Mode Outré

    Sorry should stop commenting so much – but super easy to stop the vertical scrolling:

    in ScrollViewWithPagingViewController.m, just changed the line:

    scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height);

    to

    scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, 411);

    I’m using a tabbarcontroller so my pictures are 320×411.
    Thank you – this is just great. I’ve spent the last week and a half trying to find how to do this and can’t believe how difficult it is to find something that works :)

    However if the answer comes easily, still appreciate any help on the releasing images from memory.

    Reply
  10. Alex

    Great tutorial! Would you be able to assist further please?! I have successfully loaded images to the scrollview as I require. What I’d like to do is instigate the images upon being tapped to reveal a toolbar with a save facility, ie to save the scrolled image to camera roll. Then, when swipe to further images the toolbar to disappear again.
    Any help greatly appreciated! Thanks again, Alex

    Reply
    1. ykyuen Post author

      Hi Alex,

      Again, i haven’t tried that before. i hope the following suggestion could help.

      First you could create a new project and try implementing the touchesBegan:withEvent: in a simple uiview. This event is triggered when even there is a touch on the uiview. you can find more details in the following links

      So when you get it ready, you can try to apply it to the UIScrollview. But in this case, you have to set the view controller be the first responder as follow.

      - (BOOL)canBecomeFirstResponder {
        return YES;
      }
      
      - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        // Add the following line in viewDidAppear
        [self becomeFirstResponder];
        ...
      

      Hope the above information could help you. =)

      Reference: Stack Overflow – UIScrollView touchesBegan

      Reply
      1. Alex

        Great response, many thanks.

        Do you think it possible to save the images in this project by tapping the image, rather than instigating a toolbar?

        I’m struggling to establish how to do so!!

        Many thanks again!

  11. Sudhir

    Hi,

    Great example but I am not able to figure one thing. Hope you can help me with it.

    Question: I have 10 views with each view displaying a image of width 1500 and height 1500. I was able to set the scroll view width and height to be 1500 each respectively.

    The problem that i am facing is that I am not able to scroll and view the image i.e. when I scroll my view is shifted to either the next page or view is set to the initial coordinates.
    Can you please let me know how can I view the scroll and view the image and stop at a particular position. (required functionality is that when the scrolling stops, the user can touch any part of the image to tag his/her name).

    Thanks in advance.

    Reply
  12. Matt

    I was wondering if you could help me out with a problem. I’ve decided to add buttons to the view which is displayed on each page and I wan’t to be able to detect which button was clicked on which page so I can play a specific sound.

    I’ve worked out how to detect the button clicked but if I try to access the ‘pageNumber’ variable from within my “- (IBAction)buttonClicked:(id)sender” method, then it is always null for the first page or crashes the app for any other page.

    Any ideas?
    Thanks

    Reply
  13. Dinesh Kumar

    ykyuen,Definitely I should say…You are not just giving tutorials…but also helping when ever people struck and ask u for help…mostly many wont do….Great man…plz continue…

    Reply
  14. squarefrog

    Thanks so much for this article. This is the only scrollview/paging tutorial I’ve seen that actually works with iOS 4 & 5.

    I have a question though, and I’m not really sure where to start solving it, so I thought I’d ask. I’ve implemented your code, so that my app loads in a number of images from an array of filenames. I’ve also stuck in the touchesBegan method, so I know when I user taps an image.

    What I’d like to know is can you think of anyway to get the name of the image that is tapped? Can I get the frame location and run a switch statement to choose the right array instance and NSLog that? Or is there a more graceful way of doing what I wish to achieve?

    Reply
    1. ykyuen Post author

      I hope the following suggestion could work. =P

      By default, the UIScrollView in my example will be the first responder to the tap action. So i guess you have to change to first responder to the MyViewController. For more information. take a look to the following links.

      If it is done, the MyViewController will response to your tap action. Just add a varaible to store the image name just like the pageNumberLabel in the example and you can manipulate it for the tab event.

      Reply
      1. squarefrog

        OK I’m struggling to get to grips with this. Here’s what I’ve got in my MyViewController.m

        - (void)viewDidLoad
        {
        [super viewDidLoad];
        // Some code removed.

        [self becomeFirstResponder];
        }

        - (BOOL)canBecomeFirstResponder {
        return YES;
        }

        -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        UITouch *touch = [touches anyObject];
        NSLog(@”object was %@”, touch);
        }

        But how do I read the variable? Say for example during my pageControlColorWithIndex method I set the following;

        NSString *imageLabel = imageName;

        How do I pull that out from the touches method?

      2. ykyuen Post author

        I suppose u have stored the imageName in MyViewController variable.
        MyViewController.m

        ...
        @synthesize pageNumberLabel;
        @synthesize imageName;
        
        // Creates the color list the first time this method is invoked. Returns one color object from the list.
        + (UIColor *)pageControlColorWithIndex:(NSUInteger)index {
            if (__pageControlColorList == nil) {
                __pageControlColorList = [[NSArray alloc] initWithObjects:[UIColor redColor], [UIColor greenColor], [UIColor magentaColor],
                                          [UIColor blueColor], [UIColor orangeColor], [UIColor brownColor], [UIColor grayColor], nil];
            }
        
            // Mod the index by the list length to ensure access remains in bounds.
            return [__pageControlColorList objectAtIndex:index % [__pageControlColorList count]];
        }
        
        // Load the view nib and initialize the pageNumber ivar.
        - (id)initWithPageNumber:(int)page (NSString *)imgName {
            if (self = [super initWithNibName:@"MyViewController" bundle:nil]) {
                pageNumber = page;
                self.imageName = imgName
            }
            return self;
        }
        
        - (void)dealloc {
            [pageNumberLabel release];
            [super dealloc];
        }
        
        // Set the label and background color when the view has finished loading.
        - (void)viewDidLoad {
            self.pageNumberLabel.text = [NSString stringWithFormat:@"Page %d", pageNumber + 1];
            self.view.backgroundColor = [MyViewController pageControlColorWithIndex:pageNumber];
        }
        ...
        

         

        Now when u initialize each myViewControllre instance, you also pass in the imageName string.
        and you can print it inside the touch event

        NSLog(self.imageName);
        
  15. squarefrog

    I think the problems I’m having are stemming from the fact I convert the array to images in the class method:

    + (UIImage *)pageControlColorWithIndex:(NSUInteger)index {
        
    
        if (__pageControlColorList == nil) {
            __pageControlColorList = [[NSMutableArray alloc] init];
            // Get selected category
            UltimateRageAppDelegate *dataCenter = (UltimateRageAppDelegate *) [[UIApplication sharedApplication] delegate];
            NSString *faceCategory = dataCenter.data;  
            
            // Load faceLists.plist
            NSString *path = [[NSBundle mainBundle] pathForResource:@"faceLists" ofType:@"plist"];
            
            // Load plist into a new dictionary
            NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
            
            // Drill down to next level
            NSArray *faceSelection = [dict objectForKey:faceCategory];
    
            // Get the count of the array
            NSInteger faceCount = [faceSelection count];
            
            int i;
            for (i = 0; i < faceCount; i++) {
                NSString *objectName = [faceSelection objectAtIndex:i];
                NSString *imageLocation = [objectName stringByAppendingString:@".png"];
                          
                [__pageControlColorList addObject: [UIImage imageNamed:imageLocation]];
    
            }
            
        }
            
        // Mod the index by the list length to ensure access remains in bounds.
        return [__pageControlColorList objectAtIndex:index % [__pageControlColorList count]];
    }
    

    I’ve probably gone about it all wrong – but I'm still trying to learn.

    Reply
      1. squarefrog

        No I can’t quite figure it out. I’m reading through apples introduction to objective c to learn more about class and instance methods. I think I’ve got too far into it without actually understanding what I’m doing. I just edited your code to fit my purpose, but now I’m not sure how I can proceed with the class method.

        Should I try and split it up maybe? Method one creates an array with image names, and returns the value, then from this i can set the label, and convert the image name into a UIImage?

        Thanks for trying to help out – I am grateful!

      2. ykyuen Post author

        It seems that you are not only trying to store the image name. Anyway, just split the goal into small tasks and diagnose/complete them one by one.

        i also learn objective-c by trial and error and i am still learning too. =P

        Wish you could solve your problem soon. =)

  16. squarefrog

    I’m sure I will, i think breaking it up into small parts will help. I have one last question if you don’t mind? Is there any reason why the method is

    + (UIColor *)pageControlColorWithIndex:(NSUInteger)index

    class, or static, instead of

    - (UIColor *)pageControlColorWithIndex:(NSUInteger)index

    Instance method? My friend codes a lot in C# I believe and he couldn’t think of a reason why I should do anything as class methods!

    Reply
      1. ykyuen Post author

        Not bother at all and i didn’t help much. Have fun in objective c.
        =P

  17. manoj

    hi sir
    really amazing and wondering when i see your replies.
    you are responding to each and every question.
    great helping sir
    thanks

    Reply
    1. ykyuen Post author

      You are very welcome. Actually i can’t answer all the questions, some of them are out of my knowledge scope and i just provide what i found by Google. =P

      Thanks for your message. =)

      Reply
  18. Felipe Bautista

    Hi Ykyuen:

    it’s work for me very well this tuto, but I want to add and index, I’m already did it but i want to know how show a specific image (on of the scroll) when click a button, but i want it in the same scroll and still working with previous and next pages.

    Thank you!

    Reply
    1. ykyuen Post author

      Hi Felipe,

      I think it would be quite complicated to add an index listing on the for the scrollview. i suggest you could list all images with UITableView and show the scrollview when user click a specific image. if the user want to return to the index list, just click the back button.

      Kit

      Reply
  19. Julian

    Great tutorial. if anyone want’s the show the pageContoller on screen…

    Add this to the header file…
    @property (nonatomic, retain) IBOutlet UIPageControl *pageControl;
    …so it appears in the xib

    Don’t forget to synthesize it
    @synthesize scrollView, pageControl, viewControllers;

    Link the IBoutlet to the xib

    Add a Page Control to the View, and the bit that got me for ages, turn off auto-scaling and auto-sizing and auto-anchoring for the page control if it doesn’t appear! See http://stackoverflow.com/questions/1676418/uipagecontrol-render-quirk-with-uiscrollview

    Reply
  20. Julian

    Hi ykyuen

    No, Thank you! And hello Squarefrog – love your Holga tips… very useful, your notes on this post helped me solve the following…

    I’ve got Apple’s zooming code to work with your paging scroller, and used a custom pageControl class to get coloured page dots. You’ll need to hunt these down to get the missing files. Here’s my code, hope it saves someone the hours it took me to get it to work ; )

    Note none of this is mine, I just hobbled it together, thank you ykyuen, squarefrog, Apple and dizbits

    .MyViewController.h

    //
    //  MyViewController.h
    //  ScrollViewWithPaging
    //
    //  Created by Yuen Ying Kit on 18/05/2010.
    //  Url: http://ykyuen.wordpress.com/2010/05/22/iphone-uiscrollview-with-paging-example/
    //
    
    #import 
    #import "TapDetectingImageView.h"
    
    @interface MyViewController : UIViewController  {
        int pageNumber;
    	IBOutlet UIImageView *theImageView;
    	UIScrollView *imageScrollView; //Added for zooming
    }
    
    @property (nonatomic, retain) IBOutlet UIImageView *theImageView;
    @property (nonatomic, retain) IBOutlet UIScrollView *imageScrollView; //Added for zooming
    
    - (id)initWithPageNumber:(int)page;
    
    @end
    

     

    .MyViewController.m

    //
    //  MyViewController.m
    //  ScrollViewWithPaging
    //
    //  Created by Yuen Ying Kit on 18/05/2010.
    //  Url: http://ykyuen.wordpress.com/2010/05/22/iphone-uiscrollview-with-paging-example/
    //
    // Some code removed/amended by Julian Baker to show page dot by
    // http://code.google.com/p/dizbits/source/browse/trunk/?r=14#trunk%2FPageControl%2FClasses
    // and use series of images
    //
    // Code from Apple's ScrollViewSuite/1_TapToZoom RootViewController.m added
    // Copyright (C) 2010 Apple Inc. All Rights Reserved.
    //
    
    #import "MyViewController.h"
    
    static NSArray *__pageControlColorList = nil;
    
    // Added for zooming 
    #define ZOOM_VIEW_TAG 100
    #define ZOOM_STEP 1.5
    
    @interface MyViewController (UtilityMethods)
    - (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center;
    @end
    // Added for zooming end
    
    
    
    @implementation MyViewController
    
    @synthesize theImageView;
    @synthesize imageScrollView; // Added for zooming
    
    
    /* 
     Creates list the first time this method is invoked. 
     Returns one color object from the list.
    */
    //+ (UIColor *)pageControlColorWithIndex:(NSUInteger)index {
    + (NSString *)pageControlColorWithIndex:(NSUInteger)index {
        if (__pageControlColorList == nil) {
            __pageControlColorList = [[NSArray alloc] initWithObjects:
    								  @"Approach", 
    								  @"Approach2", 
    								  @"Approach2", 
    								  @"Approach2", 
    								  @"Approach2", 
    								  @"Approach2", 
    								  @"Approach2", 
    								  nil];
        }
    
        // Mod the index by the list length to ensure access remains in bounds.
        return [__pageControlColorList objectAtIndex:index % [__pageControlColorList count]];
    }
    
    // Load the view nib and initialize the pageNumber ivar.
    - (id)initWithPageNumber:(int)page {
        if (self = [super initWithNibName:@"MyViewController" bundle:nil]) {
           pageNumber = page;
        }
        return self;
    }
    
    
    
    // Functions added for zooming
    - (void)loadView {
        [super loadView];
        
        // set up main scroll view
        [imageScrollView setBackgroundColor:[UIColor whiteColor]];
        [imageScrollView setDelegate:self];
        [imageScrollView setBouncesZoom:YES];
        
        // add touch-sensitive image view to the scroll view
        [theImageView setTag:ZOOM_VIEW_TAG];
        [theImageView setUserInteractionEnabled:YES];
        [imageScrollView setContentSize:[theImageView frame].size];
        
        // add gesture recognizers to the image view
        UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
        UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
        UITapGestureRecognizer *twoFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerTap:)];
        
        [doubleTap setNumberOfTapsRequired:2];
        [twoFingerTap setNumberOfTouchesRequired:2];
        
        [theImageView addGestureRecognizer:singleTap];
        [theImageView addGestureRecognizer:doubleTap];
        [theImageView addGestureRecognizer:twoFingerTap];
        
        [singleTap release];
        [doubleTap release];
        [twoFingerTap release];
        
        // calculate minimum scale to perfectly fit image width, and begin at that scale
        float minimumScale = [imageScrollView frame].size.width  / [theImageView frame].size.width;
        [imageScrollView setMinimumZoomScale:minimumScale];
        [imageScrollView setZoomScale:minimumScale];
    }
    
    
    - (void)viewDidUnload {
    	self.imageScrollView = nil;
    	self.theImageView = nil;
    }
    // Functions added for zooming end
    
    
    
    - (void)viewDidLoad {
    	
    	[super viewDidLoad];
    	[self becomeFirstResponder];
    	
        //pageNumberLabel.text = [NSString stringWithFormat:@"Page %d", pageNumber + 1];
        //self.view.backgroundColor = [MyViewController pageControlColorWithIndex:pageNumber];
    	
    	// UIImage *newImage = [UIImage imageWithContentsOfFile:[--IMG-- ofType:@"png"]];
    	// [NSBundle mainBundle] pathForResource:[MyViewController pageControlColorWithIndex:pageNumber]
    	
    	UIImage *newImage = [UIImage imageWithContentsOfFile:[
    	[NSBundle mainBundle] pathForResource:[MyViewController pageControlColorWithIndex:pageNumber] 
    												  ofType:@"png"]];
    
    	[theImageView setImage:newImage];
    }
    
    
    
    - (BOOL)canBecomeFirstResponder {
    	return YES;
    }
    
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    	UITouch *touch = [touches anyObject];
    	NSLog(@"object was %@", touch);
    }
    
    - (void)dealloc {
    	[theImageView release];
    	[imageScrollView release]; // Added for zooming
        [super dealloc];
    }
    
    
    
    
    /************************************** NOTE **************************************/
    /* The following delegate method works around a known bug in zoomToRect:animated: */
    /* In the next release after 3.0 this workaround will no longer be necessary      */
    /**********************************************************************************/
    - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
        [scrollView setZoomScale:scale+0.01 animated:NO];
        [scrollView setZoomScale:scale animated:NO];
    }
    
     #pragma mark UIScrollViewDelegate methods
     
     - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    	return [imageScrollView viewWithTag:ZOOM_VIEW_TAG];
    }
    
     #pragma mark TapDetectingImageViewDelegate methods
    
    - (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer {
        NSLog(@"Soembody touched me %@", gestureRecognizer);
    	// single tap does nothing for now
    }
    
    - (void)handleDoubleTap:(UIGestureRecognizer *)gestureRecognizer {
        // double tap zooms in
    	float newScale = [imageScrollView zoomScale] * ZOOM_STEP;
        CGRect zoomRect = [self zoomRectForScale:newScale withCenter:[gestureRecognizer locationInView:gestureRecognizer.view]];
        [imageScrollView zoomToRect:zoomRect animated:YES];
    
        NSLog(@"Soembody touched me twice %@", gestureRecognizer);
    	NSLog(@"zoomScale! %f", [imageScrollView zoomScale] );
    	NSLog(@"ZOOM_STEP on! %f", ZOOM_STEP );
    	NSLog(@"Float on! %f", newScale );
    	NSLog(@"Fail! %@", NSStringFromCGRect(zoomRect) );
    }
    
    - (void)handleTwoFingerTap:(UIGestureRecognizer *)gestureRecognizer {
        // two-finger tap zooms out
        NSLog(@"Soembody double touched me %@", gestureRecognizer);
        float newScale = [imageScrollView zoomScale] / ZOOM_STEP;
        CGRect zoomRect = [self zoomRectForScale:newScale withCenter:[gestureRecognizer locationInView:gestureRecognizer.view]];
        [imageScrollView zoomToRect:zoomRect animated:YES];
    }
    
    #pragma mark Utility methods
    
    - (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center {
        
        CGRect zoomRect;
        
        // the zoom rect is in the content view's coordinates. 
        //    At a zoom scale of 1.0, it would be the size of the imageScrollView's bounds.
        //    As the zoom scale decreases, so more content is visible, the size of the rect grows.
        zoomRect.size.height = [imageScrollView frame].size.height / scale;
        zoomRect.size.width  = [imageScrollView frame].size.width  / scale;
        
        // choose an origin so as to get the right center.
        zoomRect.origin.x    = center.x - (zoomRect.size.width  / 2.0);
        zoomRect.origin.y    = center.y - (zoomRect.size.height / 2.0);
        
        return zoomRect;
    }
    
    @end
    
    Reply
    1. ykyuen Post author

      you mean the label which show the page number “Page 1″, “Page 2″…

      Can you edit it thru the Interface builder?

      Reply
  21. Nachiket

    Hi, Thanks for the great tutorial. I’m using it in my app. What I wanted to ask is, Its a modification of Apple example of page control. So do I have to retain the Apple license on top of my code? Do Apple reject if I don’t?

    Just a curiosity, I don’t have any problem in retaining their license.

    Reply
    1. ykyuen Post author

      I think you don’t need to keep the license statement on top of the code. Just remove it and that should not affect your app submission. =)

      Reply
  22. Piero

    Hello, great example really, but in MyViewController class don’t enter in the viewwillappear method, enter in the viewdidload method, but if i change for example in the view did load method the navigation bar title, the title don’t change…how i can do?

    Reply
      1. Piero

        it’s the same of your code, i have created a new project using your code, but in MyViewController, after the viewDidload method, don’t call the viewWillappear method, so i have insert at the end of the viewdidload this: [self viewWillAppear:YES] , now it works, but you know why don’t call it and i have to call it manually?

      2. Piero

        thanks for your answer, but it’s wrong how i do, call the viewwillappear in the viewdidload?

      3. ykyuen Post author

        try this in MyViewController.m

        // Set the label and background color when the view has finished loading.
        - (void)viewDidLoad {
            [super viewDidLoad];
            pageNumberLabel.text = [NSString stringWithFormat:@"Page %d", pageNumber + 1];
            self.view.backgroundColor = [MyViewController pageControlColorWithIndex:pageNumber];
        }
        

        Does it solve the problem?

  23. Pingback: swiping content to move to next piece on iphone | PHP Developer Resource

  24. nontrivial

    Thanks for the example. If you want to make it work in storyboard, just change the “initWithPage” function to use [self.storyboard instantiateViewControllerWithIdentifier:@"controllerID"];

    Reply
      1. Tikam Chandrakar

        Hello, Great example to understand the logic and flow. But I have one issues in this example. When I am trying to add button or image in ScrollViewWithPagingViewController.xib and connected to IBOutlet “IBOutlet UIButton and UIImageView” , It seems not showing me the IBOutlet connection in this ScrollViewWithPagingViewController.xib interface builder .
        Can you please provide me any solution for that . then we can add button or connect to action method as per required.

  25. Sami

    Great example .. thanks for it ..
    I’d like to make the scroll view to start with a certain page (not the first..) , I used [self loadScrollViewWithPage:4]; in viewDidLoad but no effect . is there any way to achieve this ??

    thanks..

    Reply
    1. ykyuen Post author

      Try this in ScrollViewWithPagingViewController.m
      Assume you want to set the 4th page as default

      - (void)viewDidLoad {
          [super viewDidLoad];
      
          // 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 < 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 = 3;
      
          // 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:2];
          [self loadScrollViewWithPage:3];
          [self loadScrollViewWithPage:4];
      }
      

       

      Does it work? sorry that i dun have a mac so i couldn’t try it by myself.

      Reply
  26. Deem

    hello, great tutorial but can you please help me with assigning a different text for each of the pages? a uitextview
    thank you

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s