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.
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.
Great example, great code comments too. kudos.
LikeLike
Actually i just follow the example from Apple =)
LikeLike
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.
LikeLike
I didn’t realize there is a small triangle. Thanks. =)
LikeLike
great tutorial. one question. how can i start from page X? I tried with pageControl.currentPage = 3; in ScrollViewWithPagingViewController but it changed nothing. cheers
LikeLike
Can u try the following in ScrollViewWithPagingViewController.m.
LikeLike
I tried. Once launched empty grey screen, and then scroll a bit gives us the first red page show up suddenly.
LikeLike
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.
LikeLike
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.
LikeLike
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.
LikeLike
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.
LikeLike
You are welcome. Let me know if you have finished your iPhone Application~ =)
LikeLike
Perfect! Thank you for saving my day.
LikeLike
you are welcome =)
LikeLike
Thanks your Great Tutorial!
LikeLike
good to know that it helps you =)
LikeLike
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
LikeLike
Hi Miles,
can you show me your code? thanks?
Kit
LikeLike
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
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 😛 I’m a begginer.
LikeLike
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
LikeLike
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
LikeLike
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
LikeLike
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
LikeLike
Great =)
LikeLike
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 ?
LikeLike
you can get the an UIImage object by the following code.
If you want to set this image to the view, you can refer to
LikeLike
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
LikeLike
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.
LikeLike
Sorry, I just figured out based on a previous comment to add the pictures in the background instead of the colours! Thanks!
But if you could help on how to release views, that would be awesome 🙂
LikeLike
Hi James,
so now you could add an image as the background of a view, this is just the same as setting colors. just refer to the following post.
iPhone – Set Background Image for UIView
if you want to release a view, just call the release method as follow
LikeLike
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?
LikeLike
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?
LikeLike
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 🙂
LikeLike
in the code, every initialized view is released after substituted the placehoder in the viewControllers.
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~~ =)
LikeLike
Then no problem! Thanks!
Much appreciate your help.
LikeLike
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
LikeLike
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.
LikeLike
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.
LikeLike
Hi James, could u solve the memory problem?
LikeLike
How would you load a different view on each page?
LikeLike
Hi Ryan,
In the example, i set each page color using the __pageControlColorList in MyViewController.m.
you can set an image instead of color for each page, you can refer to iPhone – Set Background Image for UIView to find out how to set a background image to a UIView.
Regards,
Kit
LikeLike
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
LikeLike
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.
Hope the above information could help you. =)
Reference: Stack Overflow – UIScrollView touchesBegan
LikeLike
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!
LikeLike
Hi Alex,
If you want to save the image, i think the following post could help.
iPhone – Capture Screenshot Programmatically
Make sure the self.view is your image view.
Hope this help. =D
Kit
LikeLike
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.
LikeLike
Hi Sudhir,
It seems that the UIScollView take the first responder so whenever you touch the image, the UIScrollView will get the touch event and start to scroll. In this case, you have to set the view inside the UIScollView to be the first responder.
I found a similar questions in StackOverflow, hope this could help.
LikeLike
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
LikeLike
would u consider to create a global variable to store the page number?
iPhone – Accessing the Global Variables in the AppDelegate
LikeLike
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…
LikeLike
Thanks. good to know that my blog post could help u =)
LikeLike
How do i set the scrollView to have a different View for each Page
LikeLike
Can u elaborate more?
LikeLike
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?
LikeLike
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.
LikeLike
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?
LikeLike
I suppose u have stored the imageName in MyViewController variable.
MyViewController.m
Now when u initialize each myViewControllre instance, you also pass in the imageName string.
and you can print it inside the touch event
LikeLike
OK I’ll give that a try. Thanks for the quick response!
LikeLike
I think the problems I’m having are stemming from the fact I convert the array to images in the class method:
I’ve probably gone about it all wrong – but I'm still trying to learn.
LikeLike
so did u solve the problem?
LikeLike
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!
LikeLike
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. =)
LikeLike
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!
LikeLike
The reason of using class method because the __pageControlColorList array is always the same for all myViewController instances. of course we could still use the instance method but that will create dummy arrays and consume more resources.
You can read the following post for more detail information.
StackOverflow – Objective-C: Class vs Instance Methods?
LikeLike
Brilliant I understand now. Thanks for answering all my questions, I’ll try not to bother you again.
LikeLike
Not bother at all and i didn’t help much. Have fun in objective c.
=P
LikeLike
hi sir
really amazing and wondering when i see your replies.
you are responding to each and every question.
great helping sir
thanks
LikeLike
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. =)
LikeLike
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!
LikeLike
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
LikeLike
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
LikeLike
Thanks for your code. =D
LikeLike
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.m
LikeLike
thanks very much for your code Julian~ =D
LikeLike
Hello I am still trying to figure out how to position the text label at the top of the image scrollview you can see more detail and information here http://stackoverflow.com/questions/7590074/iphone-how-to-add-text-above-and-below-the-image-in-the-scrollvew
LikeLike
you mean the label which show the page number “Page 1”, “Page 2″…
Can you edit it thru the Interface builder?
LikeLike
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.
LikeLike
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. =)
LikeLike
Oh wow, that was quick. Thank you for prompt reply, Cheers !!
LikeLike
haha~ you are welcome. =)
LikeLike
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?
LikeLike
Sorry, i didn’t get what you mean. Can you post your code here?
Remember to follow the Syntax Highlight when posting code.
LikeLike
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?
LikeLike
oic. i am not sure why this happen but i would guess if it is a SDK issue. My example was written with SDK 3.x and i think you are probably running on SDK 5.x. So some default behaviour may have been modified.
And the following reference may help to explain the issue.
StackOverflow – viewDidLoad gets called, viewWillAppear does not get called, view does not appear on screen
LikeLike
thanks for your answer, but it’s wrong how i do, call the viewwillappear in the viewdidload?
LikeLike
try this in MyViewController.m
Does it solve the problem?
LikeLike
no the viewWillAppear method it’s not called…:(
LikeLike
Then maybe u just call it in viewDidLoad as what u did. That should be fine.
LikeLike
Thanks for the example. If you want to make it work in storyboard, just change the “initWithPage” function to use [self.storyboard instantiateViewControllerWithIdentifier:@”controllerID”];
LikeLiked by 1 person
Thanks for your note. =D
LikeLike
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.
LikeLike
How about if you add the button to MyViewController.xib? could the button be shown?
LikeLike
Can you give me the exact code here please?
LikeLike
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..
LikeLike
Try this in ScrollViewWithPagingViewController.m
Assume you want to set the 4th page as default
Does it work? sorry that i dun have a mac so i couldn’t try it by myself.
LikeLike
hello, great tutorial but can you please help me with assigning a different text for each of the pages? a uitextview
thank you
LikeLike
In the viewDidLoad method in MyViewController.m. You can check the pageNumber and set different text.
LikeLike
Thank you. I’m a newbie in iOS and this helped me a lot! 🙂
LikeLike
You are welcome~ =)
LikeLike