How does UIScrollView work with StoryBoard and show different UIViewControllers? - ios5

I have a StoryBoard. In that there is a UIViewController - on that I have put UIScrollView and UIPageControl. And on each page (say I have 3) I am trying to load a UIViewController (that shows different labels and table values).
Problem - I am able to load everything up but the label and table is not visible. Thou when I try to change the background color for each page - it does gets changed. I have researched various posts and code but nothing seems to work / show UIScrollView with StoryBoard (with UIViewController displaying labels and table).
I am thou able to do this with loading a separate XIB file but then the whole segue gets interrupted.
Please see my code below.
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
//ScrollView and PageControl
// Create view controllers placeholder array
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (int i = 0; i < numberOfPages; i++)
{
[controllers addObject:[NSNull null]];
}
self.viewControllers = controllers;
// Set Scroll View
scrollView.pagingEnabled = YES;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * numberOfPages, scrollView.frame.size.height);
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.scrollsToTop = NO;
scrollView.delegate = self;
// Set Page Control
pageControl.numberOfPages = numberOfPages;
pageControl.currentPage = 0;
// Load the visible and next page
[self loadScrollViewWithPage:0];
[self loadScrollViewWithPage:1];
}
- (void)scrollViewDidScroll:(UIScrollView *)sender
{
if (pageControlUsed)
{
// To know if scroll is valid - from Page Control
return;
}
// Changes Page Control indicator
CGFloat pageWidth = scrollView.frame.size.width;
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
pageControl.currentPage = page;
// Load the visible, previous and next pages
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
pageControlUsed = NO;
}
- (void)loadScrollViewWithPage:(int)page
{
if (page < 0) return;
if (page >= numberOfPages) return;
// Load new Controller
ReportsDetailViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{
controller = [[ReportsDetailViewController alloc] init];
controller.pageNumber = [NSNumber numberWithInt:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
}
if (nil == controller.view.superview)
{
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}

Try to create your ViewControllers like this:
YourViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"YourViewController"];
You can set the identifier of your view controller in the attribute inspector.
I had the same problem today and this did the trick for me.

Related

UIScrollView and UIViews

I want to use a UIScrollView to display multiple UIViews as the user scrolls through the UIScrollView control. I'm not worried about showing Pagination just yet.
I already managed to implement some of it, but is not working the way I want it to.
Currently:
I have 3 ViewControllers with different nib files. The root view controller is the one with the UIScrollView, and it to load the rest of the view controllers.
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < kNumberOfPages; i++) {
[controllers addObject:[NSNull null]];
}
self.viewControllers = controllers;
[controllers release];
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;
[self loadScrollViewWithPage:0];
- (void)loadScrollViewWithPage:(int)page {
if (page == 0) {
PageTwo *controller2 = [viewControllers objectAtIndex:page];
if ((NSNull *)controller2 == [NSNull null]) {
controller2 = [[PageTwo alloc] initWithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller2];
[controller2 release];
}
// add the controller's view to the scroll view
if (nil == controller2.view.superview) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller2.view.frame = frame;
[scrollView addSubview:controller2.view];
}
}
if (page == 1) {
// replace the placeholder if necessary
NewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller = [[NewController 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 = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
That's all fine... However, what I want it to do is to load the root nib first. But I'm not sure how to go about this. I know I have to increase the number of pages to 3, but when I initialize the controllers I don't know how to tell it that page == 0 should be the current view.
Any ideas?
Update:
Sigh, I overlooked something.. Didn't notice that if don't specify a page at level 0 it just shows the current view hah!
Silly me.
I overlooked something.. Didn't notice that if don't specify a page at level 0 it just shows the current view hah!

Possible optimization to unload the views+controllers which are no longer visible in UIScrollView and PageControl

I have this page control code same from the apple sample. Here i have a subview (controller.view) which contains a ImageView. Now problem is with memory management. All works fine. But when i scroll 5-10 pages. RAM gets filled.
I tried to release the view+controller but did not find any proper place/way that work. I want to release the views which are not currently visible. (except current,previous & next view)
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < kNumberOfPages; i++) {
[controllers addObject:[NSNull null]];
}
self.viewControllers = controllers;
[controllers release];
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;
[self loadScrollViewWithPage:0];
[self loadScrollViewWithPage:1];
}
- (void)loadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= kNumberOfPages) return;
PageControlExampleViewControl *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller = [[PageControlExampleViewControl alloc] initWithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
if (nil == controller.view.superview) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
- (void)scrollViewDidScroll:(UIScrollView *)sender {
if (pageControlUsed) {
return;
}
CGFloat pageWidth = scrollView.frame.size.width;
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
pageControl.currentPage = page;
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
pageControlUsed = NO;
}
- (IBAction)changePage:(id)sender {
int page = pageControl.currentPage;
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];
pageControlUsed = YES;
}
I tried for several hours the code of Deepmist. It did the job but I received memory warnings and my app crashed every 25-30 pages scrolled (I am using big images in the pages). In Instruments I noticed a very large use of memory: even if the unnecessary views were removed time by time from superview and the relative viewControllers were replaced with NSNulls, Instruments showed that real memory increased on every pagescroll of 4-5MB!
Searching on the web I found that this is a common problem. If you also have this problem, you should try the following checks:
1) in each view, be sure to use imageWithContentsOfFile instead of imageNamed. As documented imageNamed cache images and increase memory size.
2) in the Deepmist code, after:
[controller.view removeFromSuperview];
you also have to set to nil the view:
controller.view=nil;
This trick solved the memory consumption which now is stable for the only three views loaded (current, current-1 and current+1 to avoid flashing in the pagescroll).
Hope this helps!
I didn't test this but I imagine you could write a method that does the opposite of loading like so:
- (void)unloadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= kNumberOfPages) return;
PageControlExampleViewControl *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller != [NSNull null]) {
if (nil != controller.view.superview)
[controller.view removeFromSuperview];
[viewControllers replaceObjectAtIndex:page withObject:[NSNull null]];
}
}
Then add some code to your didScroll method like so:
[self unloadScrollViewWithPage:page - 2];
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
[self unloadScrollViewWithPage:page + 2];
in loadScrollViewWithPage method, just before if (nil == controller.view.superview), cycle trough all views and remove them all except current -1, current and current + 1 but only if method was called with current index view. Also, don't forget to replace those view controllers in viewControllers array with NSNulls.
try this VSScrollview , it reuses its views like UITableview reuses its cell.

Paging viewcontrollers in Landscape mode

I have this code, modified version of Apple's PageScrollView sample. Here the differnce is that m using ViewControllers instead of UIView.
MyClass.h
#interface MyClass : UIViewController {
UIScrollView *scrollView;
UIPageControl *pageControl;
NSMutableArray *viewControllers;
BOOL pageControlUsed;
}
#property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
#property (nonatomic, retain) IBOutlet UIPageControl *pageControl;
#property (nonatomic, retain) NSMutableArray *viewControllers;
- (IBAction)changePage:(id)sender;
#end
MyClass.m
#import "MyClass.h"
#import "MyViewController.h"
#import "MyViewControllerZero.h"
#import "MyViewControllerOne.h"
#import "MyViewControllerTwo.h"
static NSUInteger kNumberOfPages = 3;
#interface MyClass (PrivateMethods)
- (void)loadScrollViewWithPage:(int)page;
- (void)scrollViewDidScroll:(UIScrollView *)sender;
#end
#implementation MyClass
#synthesize scrollView, pageControl, viewControllers;
/*
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Custom initialization
}
return self;
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
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 = 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 < 0) return;
// if (page >= kNumberOfPages) return;
if(page==0)
{
MyViewControllerZero *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{ controller = [[MyViewControllerZero alloc] init];
[viewControllers replaceObjectAtIndex:page withObject:controller];
if (nil == controller.view.superview) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
}
if(page==1)
{
MyViewControllerOne *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{ controller = [[MyViewControllerOne alloc] init];
[viewControllers replaceObjectAtIndex:page withObject:controller];
if (nil == controller.view.superview) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
}
if(page==2)
{
MyViewControllerTwo *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{ controller = [[MyViewControllerTwo alloc] init];
[viewControllers replaceObjectAtIndex:page withObject:controller];
if (nil == controller.view.superview) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
}
}
- (void)scrollViewDidScroll:(UIScrollView *)sender {
if (pageControlUsed) {
return;
}
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
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return YES;
}
- (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;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[viewControllers release];
[scrollView release];
[pageControl release];
[super dealloc];
}
#end
The Above code works absolutely fine in the potrait mode. But when i change the orientation,the whole paging gets screwed up.. :((
please help me to resolve this problem..
Apple didn't design UIViewController to work at any point in a view hierarchy. The views that you're adding to your UIScrollView are managed by view controllers, but I think you'll find that the view controllers for those views don't receive rotation events.
You could try to manually forward all events from the scroll view's controller to the child view controllers, but I think you'll find that to be tedious and error prone. I'd suggest instead that you simply use a single view controller for the scroll view and its child views.

Page Scroll View Help, different views controllers each page?

Ok, so I am trying to a pagescrollView with two views each with a different view controller so I can work on each different view. I got apple's sample code and it seems that they created a lot of different view "lazily as they said" how can this code be altered so that I can have the first page be one view controller and the second page be another view controller?
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// 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 = 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 < 0) return;
if (page >= 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 = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
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 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;
}
Thanks if anyone knows how to do this.
To load the pages with different view controllers all you have to change is this segment of code
MyViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{ controller = [[MyViewController alloc] initWithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
to initialize each view controller to the one you want instead of the same one each time, so the code would look something like
if(page==0)
{
MyViewControllerZero *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{ controller = [[MyViewControllerZero alloc] initWithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
}
if(page==1)
{
MyViewControllerOne *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{ controller = [[MyViewControllerOne alloc] initWithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
}
//and so on ...
This is the Correct Code!!!
- (void)loadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= kNumberOfPages) return;
// replace the placeholder if necessary
if (page == 0){
MyViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller = [[MyViewController alloc] initWithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
}
if (nil == controller.view.superview) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
if (page == 1){
PageOne *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller = [[PageOne alloc] initWithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
}
if (nil == controller.view.superview) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
// add the controller's view to the scroll view
}
}
THX For the first answewr she open my mind!!!

How to set the default page on the right side instead on the left (reverse) when launching App that used UIPageControl of UIScrollView?

My Case:
I have original Page Control app from Apple. I would like to modify the launch of the app on right side on page control at the bottom of UIScrollView?
By default, when launch - it is on the first page which is on the left. Say for example, i have 7 pages. So, I want the first page appear on the last page which is on the 7th page and second page page on the 6th page and so on. In short, the reverse of the default.
I know there have to be one code that do this trick. I'm not sure what it is.
Please help. Thanks!
Below is description of Page Control app from Apple:
PageControl
This application demonstrates use of UIScrollView's paging functionality to use horizontal scrolling as a mechanism for navigating between different pages of content. Each page is managed by its own view controller which is loaded only when it is needed. A UIPageControl is displayed at the bottom of the window as an alternative interface for moving between pages
The Code in AppDelegate.m
#import "AppDelegate.h"
#import "MyViewController.h"
static NSUInteger kNumberOfPages = 7;
#interface AppDelegate (PrivateMethods)
- (void)loadScrollViewWithPage:(int)page;
- (void)scrollViewDidScroll:(UIScrollView *)sender;
#end
#implementation AppDelegate
#synthesize window, scrollView, pageControl, viewControllers;
- (void)dealloc {
[viewControllers release];
[scrollView release];
[pageControl release];
[window release];
[super dealloc];
}
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// 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 = 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 < 0) return;
if (page >= 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 = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
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 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
Taking the PageControl sample from Apple, here's the code that would do this. I tested it and it works fine, except for noticing a jump of the page control when you launch the app from the initial dot to the last one, but I don't know how to solve that. Let me know if this doesn't work for you.
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// 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;
// First, set the number of pages
pageControl.numberOfPages = kNumberOfPages;
// Then, tell the page control that you're on the last page (numPages - 1, since we're 0-indexed)
pageControl.currentPage = kNumberOfPages - 1;
// Now, we have to tell the frame that we're at the end of the frame
// So we set the content offset to be the last page
scrollView.contentOffset = CGPointMake(scrollView.frame.size.width * (kNumberOfPages - 1), 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];
// Now, instead of loading the original page, we load the last two pages
[self loadScrollViewWithPage:kNumberOfPages - 1];
[self loadScrollViewWithPage:kNumberOfPages - 2];
}
Well you can just set your scrollViews offset to be the last page and so then it will start at t he last page and you have to scroll to the right instead of the left...use UIScrollViews - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated method.