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!!!
Related
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.
I have a small application with some detail controller. Detail controller is a web view put inside scroll view:
Here is a code of detail view controller:
- (void)loadScrollViewWithPage:(int)page
{
if (page < 0)
return;
if (page >= detailTotalJobs)
return;
// replace the placeholder if necessary
SearchJobDetailWebViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{
NSLog(#"Vytvaram web controller: %d", page + 1);
if (detailJobIndex == page) {
controller = [[SearchJobDetailWebViewController alloc] initWithNibName:#"SearchJobDetailWebViewController" bundle:nil page:page url:detailExportUrl];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
} else if (detailJobIndex + 1 == page) {
controller = [[SearchJobDetailWebViewController alloc] initWithNibName:#"SearchJobDetailWebViewController" bundle:nil page:page url:detailNextExportUrl];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
} else {
[self performSelector:#selector(downloadData:) withObject:[NSNumber numberWithInt:page] afterDelay:0.1];
//[self downloadData:[NSNumber numberWithInt:page]];
controller = [[SearchJobDetailWebViewController alloc] initWithNibName:#"SearchJobDetailWebViewController" bundle:nil page:page url:nextArray.exportUrl];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
}
// add the controller's view to the scroll view
if (controller.view.superview == nil)
{
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
//NSDictionary *numberItem = [self.contentList objectAtIndex:page];
//controller.numberImage.image = [UIImage imageNamed:[numberItem valueForKey:ImageKey]];
//controller.myLabel.text = #"Test";
}
}
- (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;
NSString *detailTitleText = [NSString stringWithFormat:#"%d / %d", page + 1, detailTotalJobs];
[self setTitle:detailTitleText];
//[detailTitleText release];
// 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
}
Code inspired by Apple page control.
Problem is here:
if (detailJobIndex == page) {
controller = [[SearchJobDetailWebViewController alloc] initWithNibName:#"SearchJobDetailWebViewController" bundle:nil page:page url:detailExportUrl];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
} else if (detailJobIndex + 1 == page) {
controller = [[SearchJobDetailWebViewController alloc] initWithNibName:#"SearchJobDetailWebViewController" bundle:nil page:page url:detailNextExportUrl];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
} else {
[self performSelector:#selector(downloadData:) withObject:[NSNumber numberWithInt:page] afterDelay:0.1];
//[self downloadData:[NSNumber numberWithInt:page]];
controller = [[SearchJobDetailWebViewController alloc] initWithNibName:#"SearchJobDetailWebViewController" bundle:nil page:page url:nextArray.exportUrl];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
First part and second part of if work correctly, because I have data for first web view and for second web view.
But last part of if have a problem. when I scroll from second web view to third web view, scroll view show blank page. Next pages work correctly.
Where is problem, please ?
Just put a breakpoint in debug mode on the braces.
Is controller nil ?
Check your custom init method.
Does it return nil if there is a problem ?
[self = super initWithNibName..................];
if (!self) return nil;
// some inits
return self;
If this is done and controller is nil, check your URL, and try it into a web browser. Does it showsup ?
How do you fill your webView ? Is there an error returned when filling it ? No encoding problem returned ?
Well, some code would help, but these indications are the most common problems you can have.
i want to remove currencyViews (tableViews) that are not more needed. i can change dynamically them, and if i had 6 currencyViews and now are 4 , those 2 (not showed) still exist. how can i remove them ?
- (void)loadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= numberOfCurrencyViews) return;
// replace the placeholder if necessary
CurrencyViewController *controller = [self.currencyControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller = [[CurrencyViewController alloc] initWithPageNumber:page];
controller.delegate = self;
[self.currencyControllers 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];
}
}
You can remove a subview from a view using something to the extent of:
[controller.view removeFromSuperview];
[myScrollView.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
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!
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.