I have a problem with memory management. A have a scrollview and every page in it loads from array of view controllers.
I load scrolview page calling the folowing method:
- (void)loadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= kNumberOfPages) return;
BancaTableViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller=[[BancaTableViewController alloc] initWithPageNumber:page];
controller.banks=banks;
[controllersetDelegate:self];
[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];
}
}
This is my unload view controller method which unloads all controllers except the controller of curent page but it doesn't seem to work because the memory keep increasing.
- (void)unloadScrollViewWithPage:(int)page {
for (unsigned i = 0; i < kNumberOfPages; i++) {
if(i!=page){
[viewControllers replaceObjectAtIndex:i withObject:[NSNull null]];
}
}
}
How to write the unloadviewcontroller correctly?
You're adding a hard pointer to scrollView, but you never remove the object. (i.e. you keep adding subViews to scrollView and never remove them.
[scrollView addSubview:controller.view];
Try this...
Add a tag to your controller view when you add them to the scrollView, then remove it before you load a new controller. Check syntax - from memory - not tested
if ((NSNull *)controller == [NSNull null]) {
controller=[[BancaTableViewController alloc] initWithPageNumber:page];
controller.banks=banks;
controller.tag = 3;
[controllersetDelegate:self];
[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 removeFromView:[scrollView viewWithTag:3]];
[scrollView addSubview:controller.view];
}
Related
I have been trying for a while to get UIScrollView to get working. Ive come so far that it does indeed scroll but not to the ViewController i want to. And i have no idea how to do it so some help from here would be nice.
This is how the code looks like for ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *controllers = [[NSMutableArray alloc] init];
[controllers addObject:[NSNull null]];
[controllers addObject:[NSNull null]];
self.viewControllers = controllers;
self.scrollView.pagingEnabled = YES;
self.scrollView.contentSize =
CGSizeMake(CGRectGetWidth(self.scrollView.frame) * 2, CGRectGetHeight(self.scrollView.frame));
self.scrollView.showsHorizontalScrollIndicator = YES;
self.scrollView.delegate = self;
self.pageControl.numberOfPages = 2;
self.pageControl.currentPage = 0;
[self loadScrollViewWithPage:0];
[self loadScrollViewWithPage:1];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)loadScrollViewWithPage:(NSUInteger)page
{
if (page >= 2)
return;
// replace the placeholder if necessary
ViewController2 *controller = [self.viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{
controller = [[ViewController2 alloc] init];
[self.viewControllers replaceObjectAtIndex:page withObject:controller];
}
// add the controller's view to the scroll view
if (controller.view.superview == nil)
{
CGRect frame = self.scrollView.frame;
frame.origin.x = CGRectGetWidth(frame) * page;
frame.origin.y = 0;
controller.view.frame = frame;
[self addChildViewController:controller];
[self.scrollView addSubview:controller.view];
[controller didMoveToParentViewController:self];
}
}
// at the end of scroll animation, reset the boolean used when scrolls originate from the UIPageControl
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
// switch the indicator when more than 50% of the previous/next page is visible
CGFloat pageWidth = CGRectGetWidth(self.scrollView.frame);
NSUInteger page = floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.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
}
- (void)gotoPage:(BOOL)animated
{
NSInteger page = self.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 bounds = self.scrollView.bounds;
bounds.origin.x = CGRectGetWidth(bounds) * page;
bounds.origin.y = 0;
[self.scrollView scrollRectToVisible:bounds animated:animated];
}
- (IBAction)changePage:(id)sender
{
[self gotoPage:YES]; // YES = animate
}
So what happens is the first view gets loaded. And i can scroll but it scrolls to an empty view. What i would like to do is that it scrolls to ViewController2
How can i implement for it to do so? (horizontally)
EDIT1, trying to implement what matt suggested
- (void)viewDidLoad {
[super viewDidLoad];
self.vcIdentifiers = #[#"PageContentViewController", #"PageContentViewController1"];
// Create page view controller
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController"];
self.pageViewController.dataSource = self;
UIViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 30);
[self addChildViewController:_pageViewController];
[self.view addSubview:_pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (PageContentViewController *)viewControllerAtIndex:(NSUInteger)index
{
if (([self.vcIdentifiers count] == 0) || (index >= self.vcIdentifiers.count)) {
return nil;
}
// Create a new view controller and pass suitable data.
PageContentViewController *pageContentViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageContentViewController"];
pageContentViewController.titleText = self.pageTitles[index];
pageContentViewController.pageIndex = index;
return pageContentViewController;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = ((PageContentViewController*) viewController).pageIndex;
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = ((PageContentViewController*) viewController).pageIndex;
if (index == NSNotFound) {
return nil;
}
index++;
if (index == [self.pageTitles count]) {
return nil;
}
return [self viewControllerAtIndex:index];
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
//return [self.pageTitles count];
return 2;
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
return 0;
}
I am thinking of using the storyboard id's that my viewcontrollers have, svaed in VCidentifiers.
What i dont get is how to instantiate the viewcontrollers in the method viewControllerAtIndex. The way it is written now, it will only create PageContentViewController, when it should for index 0 create PageContentViewController and for index 1 PageContent2Controller.
What am i missing?
A scroll view is for scrolling between existing views, not for dynamically loading views from view controllers. You can use view controllers in this way, and we used to have to do it sometimes, but (as you've discovered) it is difficult.
I recommend that you drop your scroll view and use UIPageViewController in a scroll style instead. It solves all the problem for you when what you want you to do is scroll between view controllers' views.
I have added view controllers according to my array count and set the content size of the horizontal scroll view.now i want some text to be displayed on each view.
for this i have taken a for loop but i'm not able to add text views over the views.
this is my code:
-(void)loadScrollView
{
scrollView.contentSize = CGSizeMake(0, scrollView.frame.size.height);
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < [_arrUrlLinks count]; i++) {
[controllers addObject:[NSNull null]];
}
// MyViewController1 *controller;
self.viewControllers = controllers;
// self.viewControllers =_txtView;
count=1;
// a page is the width of the scroll view
scrollView.pagingEnabled = YES;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [_arrUrlLinks count], scrollView.frame.size.height);
scrollView.showsHorizontalScrollIndicator =YES;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.scrollsToTop = NO;
scrollView.delegate = self;
pageControl.numberOfPages = [_arrUrlLinks count];
pageControl.currentPage = 0;
}
- (void)loadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= [_arrUrlLinks count])
return;
controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
NSString *deviceType = [UIDevice currentDevice].model;
if([deviceType isEqualToString:#"iPhone"])
{
controller = [[MyViewController1 alloc] initWithNibName:#"MyViewController1" bundle:nil];
}
else{
controller = [[MyViewController1 alloc] initWithNibName:#"MyViewController1_ipad" bundle:nil];
}
[controller initWithPageNumber: page];
NSLog(#"loadscrollviewwithpage");
[viewControllers replaceObjectAtIndex:page withObject:controller];
}
// 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;
NSLog(#"111111%#",resultDicArray);
NSLog(#"0000000%#",body);
NSLog(#"header======.>>>>..%#",headerstr);
}
[scrollView addSubview:controller.view];
[self downLoadData:page];
}
- (void)unloadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= [_arrUrlLinks count]) return;
controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller != [NSNull null]) {
if (nil != controller.view.superview)
[controller.view removeFromSuperview];
[viewControllers replaceObjectAtIndex:page withObject:[NSNull null]];
NSLog(#"Unloadscrollviewwithpage");
}
}
- (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 unloadScrollViewWithPage:page - 2];
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
[self unloadScrollViewWithPage:page + 2];
count=page+1;
// [self newCountTitleSet];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadScrollView];
[self loadScrollViewWithPage:0];
[self loadScrollViewWithPage:1];
}
i have successfully added my controllers over the scroll view now how to add text view over the controllers.
Try this,
....
if (nil == controller.view.superview) {
.....
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
//Add textview as subview to controller
UITextView *textView = [[UITextView alloc] init];
textView.text = #"hello";
textView.frame = //set frame here
//set tag to textView if needed
[controller.view addSubView:textView];
[scrollView addSubview:controller.view];
NSLog(#"loadscrollviewwithpage................>>>>>>>>>>>>");
}
....
For example you want to 10 diffrent images or textview you could use this logic:
- (void)viewDidLoad {
[super viewDidLoad];
UIScrollView *scr=[[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
scr.tag = 1;
scr.autoresizingMask=UIViewAutoresizingNone;
[self.view addSubview:scr];
[self setupScrollView:scr];
UIPageControl *pgCtr = [[UIPageControl alloc] initWithFrame:CGRectMake(0, 264, 480, 36)];
[pgCtr setTag:12];
pgCtr.numberOfPages=10;
pgCtr.autoresizingMask=UIViewAutoresizingNone;
[self.view addSubview:pgCtr];
}
- (void)setupScrollView:(UIScrollView*)scrMain {
// we have 10 images here.
// we will add all images into a scrollView & set the appropriate size.
for (int i=1; i<10; i++) {
// create image
UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:#"sti%02i.jpeg",i]];
// create imageView
UIImageView *imgV = [[UIImageView alloc] initWithFrame:CGRectMake((i-1)*scrMain.frame.size.width, 0, scrMain.frame.size.width, scrMain.frame.size.height)];
// set scale to fill
imgV.contentMode=UIViewContentModeScaleToFill;
// set image
[imgV setImage:image];
// apply tag to access in future
imgV.tag=i+1;
// add to scrollView
[scrMain addSubview:imgV];
UITextView*txtview =[[UITextView alloc]initWithFrame:CGRectMake(10,50,200,200)];
[txtview setDelegate:self];
[txtview setReturnKeyType:UIReturnKeyDone];
[txtview setTag:1];
[txtview setText:[yourArray ObjectAtIndex:i]];
[txtview setCornerRadius:5];
[scrMain addSubview:txtview];
}
// set the content size to 10 image width
[scrMain setContentSize:CGSizeMake(scrMain.frame.size.width*10, scrMain.frame.size.height)];
}
I'm loading images on the UIImageView through a string and i want horizontal scrolling to view the images on a UIImageView.
In my xib file I have a scroll view over which there is a image view. I'm using this code but my page is not scrolling and only the first image in the string is loaded.Can anyone please help.
Code:
-(void)viewDidLoad
{
count=1;
// imagesName = [[NSMutableArray alloc]initWithObjects :#"election_band_base.jpg", #"ElectionBoxAPPA-Hindi(1).jpg", #"photos.png", #"business.png", #"health.png", nil];
imagesName1 = [NSString stringWithFormat :#"election_band_base.jpg", #"ElectionBoxAPPA-Hindi(1).jpg", #"photos.png", #"business.png", #"health.png", nil];
[imagesName addObject:imagesName1];
items = [[NSMutableArray alloc]init];
// [_imageView1.image setImage=imagesName1];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// _imageView1.image=[UIImage animatedImageWithImages:imagesName duration:0];
_imageView1.image=[UIImage imageNamed:imagesName1 ];
[self loadScrollView];
}
-(void)loadScrollView
{
scrollView.contentSize = CGSizeMake(0, scrollView.frame.size.height);
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < [imagesName count]; i++) {
[controllers addObject:[NSNull null]];
}
self.viewControllers = controllers;
count=1;
// a page is the width of the scroll view
scrollView.pagingEnabled = YES;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [imagesName count], scrollView.frame.size.height);
//scrollView.contentSize = CGSizeMake(900,80);
scrollView.showsHorizontalScrollIndicator =YES;
scrollView.showsVerticalScrollIndicator = YES;
scrollView.scrollsToTop = NO;
scrollView.delegate = self;
pageControl.numberOfPages = [imagesName count];
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 >= [imagesName count])
return;
// replace the placeholder if necessary
controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
NSString *deviceType = [UIDevice currentDevice].model;
if([deviceType isEqualToString:#"iPhone"])
{
controller = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
}
else{
controller = [[MyViewController alloc] initWithNibName:#"MyViewController_ipad" bundle:nil];
}
[controller initWithPageNumber:page];
[controller setArrData:imagesName];
[viewControllers replaceObjectAtIndex:page withObject:controller];
}
// 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)unloadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= [imagesName count]) return;
controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller != [NSNull null]) {
if (nil != controller.view.superview)
[controller.view removeFromSuperview];
[viewControllers replaceObjectAtIndex:page withObject:[NSNull null]];
}
}
- (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;
// NSLog(#"current page %d",page);
// load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
[self unloadScrollViewWithPage:page - 2];
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
[self unloadScrollViewWithPage:page + 2];
count=page+1;
// A possible optimization would be to unload the views+controllers which are no longer visible
}
// At the begin of scroll dragging, reset the boolean used when scrolls originate from the UIPageControl
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollViewLoc
{
CGFloat pageWidth = scrollViewLoc.frame.size.width;
CGPoint translation = [scrollViewLoc.panGestureRecognizer translationInView:scrollViewLoc.superview];
int page = floor((scrollViewLoc.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
}
// 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;
}
So, you want to built the gallery view:
For this implement the following steps:
Add the UIScrollView to the view of your view controller.
In viewDidLoad method: Load the name of the images in the "ImageArray". (I assume all your images have names as "img1.png", "img2.png", "img3.png", ....)
ImageArray=[[NSMutableArray alloc]init];
for (int i=0; i<19; i++) {
NSString *imgtext=[[NSString alloc]initWithFormat:#"img%d",i+1];
[ImageArray addObject:imgtext];
}
In the viewWillAppear method, add the following code:
for (int i = 0; i < ImageArray.count; i++) {
CGRect frame;
frame.origin.x = self.scrollview.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollview.frame.size;
UIView *subview = [[UIView alloc] initWithFrame:frame];
UIImage *image = [UIImage imageNamed: [NSString stringWithFormat:#"%#.png",[ImageArray objectAtIndex:i]]];
UIImageView *imageView = [[UIImageView alloc] initWithImage: image];
[imageView setFrame:CGRectMake(0, 0, frame.size.width,frame.size.height )];
[subview addSubview:imageView];
[self.scrollview addSubview:subview];
}
self.scrollview.contentSize = CGSizeMake(self.scrollview.frame.size.width * ImageArray.count, self.scrollview.frame.size.height);
self.scrollview.contentOffset=CGPointMake (self.scrollview.frame.size.width, 0);
Hope it helps.
I have made a scroll view in my app in which I have added multiple images with horizontal scrolling. Below is the function which may help you..
- (void)makeFriendsScrollView{
int friendsCount = 10;
float xPos = 10;
for (int i = 0; i < friendsCount; i++) {
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(xPos,25 , 54, 54)];
imgView.image = [UIImage imageNamed:#"user_default.png"];
[imgView.layer setCornerRadius:27.0];
imgView.clipsToBounds = YES;
[scrollViewFriends addSubview:imgView];
xPos = xPos + 64;
}
scrollViewFriends.contentSize = CGSizeMake(friendsCount*65, 90);
[scrollViewFriends.layer setBorderColor:[UIColor lightGrayColor].CGColor];
[scrollViewFriends.layer setBorderWidth:0.5f];
}
i simply did this in view controller check it out...and change according to your requirement..try this in viewWillAppear method
self.arraname=[NSArray arrayWithObjects:#"1.jpg",#"2.jpg",#"3.jpg", nil];
// int friendsCount = 10;
float xPos = 160;
float x1=0;
float y=60;
for (int i = 0; i < [self.arraname count]; i++)
{
x1=xPos+(260*i);
_imgView = [[UIImageView alloc] initWithFrame:CGRectMake(x1, y, 54, 54)];
_imgView.image = [UIImage imageNamed:[self.arraname objectAtIndex:i]];
[_imgView.layer setCornerRadius:27.0];
_imgView.clipsToBounds = YES;
[self.scroll addSubview:_imgView];
}
NSLog(#"%f",x1);
self.scroll.contentSize=CGSizeMake(x1+200, 0);
self.scroll.showsHorizontalScrollIndicator = YES;
self.scroll.showsVerticalScrollIndicator=NO;
self.scroll.pagingEnabled = YES;
I am pretty new to iOS development and I stumbled upon several issues for which I couldn't easily find any answers yet:
General Setup: I'm using a ScrollView with PageControl inside a TabBarApplication
Is it possible to have the PageControl within the same area as the content of the pages? For me it always gets hidden by the SrollView's Views, but due to display space being rare I really need it on the same height as the actual content.
I've fooled around in some Sandbox-Project and whenever I first started to implement a button into the View of a ScrollView-Page the Pages of the ScrollView wouldn't show immediately anymore, but only after the first scroll attempt. I'd post some code about that but its basically only autogenerated from IB.
This is a general Question about possibilities again: The main design of the Project should be a TabBarApplication with a NavigationController letting you go deeper into sub-menues like it is pretty common. Now in one of the Tabs there should be the PageControl, in which you can then again go into sub-menues by pushing Views on a NavigationController stack . Is this possible?
Some Code for 2.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < kNumberOfPages; i++) {
[controllers addObject:[NSNull null]]; // [TaskPageViewController new]];
}
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;
}
- (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)loadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= kNumberOfPages) return;
// replace the placeholder if necessary
TaskPageViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller = [[TaskPageViewController alloc] init]; //WithPageNumber: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
}
You can have two view hierarchies for this:
Have the page control inside scrollview with origin fixed at contentOffset property
Have the page control in the superview of scrollView, but at a higher index (i.e. floating above it)
This depends on where you put the code of adding the subviews. Is it in the delegate method of scrollView? viewDidLoad? Somewhere else? Some code might help.
Not sure why you'd need to have a page control when it's a drill-down navigation. Pages are for navigating same level items.
I'm trying to implement a Page Control to show some pages with the following storyboard:
As you can see, I've the main view with a scroll view (ViewController3) and another view (PageViewController) that represents the one that I want to push on my scroll view.
On viewDidLoad of ViewController3 I call the following method:
- (void)loadScrollViewWithPage:(int)page
{
if (page < 0)
return;
if (page >= NUMBER_OF_PAGES)
return;
PageViewController *currentViewController = [viewControllers objectAtIndex:page];
if ((NSNull *)currentViewController == [NSNull null])
{
currentViewController = [[PageViewController alloc] init];
currentViewController.pageNumber = [NSString stringWithFormat:#"%d", page];
[viewControllers replaceObjectAtIndex:page withObject:currentViewController];
}
// add the controller's view to the scroll view
if (currentViewController.view.superview == nil)
{
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
currentViewController.view.frame = frame;
currentViewController.view.backgroundColor = [UIColor yellowColor];
[self.scrollView addSubview:currentViewController.view];
self.scrollView.backgroundColor = [UIColor redColor];
NSDictionary *numberItem = [contentList objectAtIndex:page];
currentViewController.numberImage.image = [UIImage imageNamed:[numberItem valueForKey:IMAGE_KEY]];
currentViewController.numberTitle.text = [numberItem valueForKey:NAME_KEY];
}
}
If I run the project, views with yellow background are displayed with no labels, nor image views.
As you can see, I set this background color programmatically. From my understanding, this means that the new view is correctly added to ViewController3, but it is not linked to the one configured on the storyboard.
Do you have any suggestions on how to solve this issue?
Thanks in advance,
yassa
You have to instantiate PageViewController instance using [UIStoryboard instantiateViewControllerWithIdentifier:(NSString *)identifier].
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIStoryboard_Class/Reference/Reference.html