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
Related
For some reason I'm having trouble displaying a view from a secondary view controller in my application.
My main view controller calls another view controller that's responsible for loading a pdf view.
The code in MainViewController looks like this:
- (int)openPDF
{
[self loadSettingsWithDefaults];
RDPDFViewController *m_pdf;
if( m_pdf == nil )
{
m_pdf = [[RDPDFViewController alloc] initWithNibName:#"RDPDFViewController"bundle:nil];
}
int result = [m_pdf PDFOpen:#"/Users/steve/test.pdf" withPassword:#""];
if(result == 1)
{
m_pdf.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:m_pdf animated:YES];
}
return result;
}
This code is located in the second view. This is the method referred to above in MainViewController.
- (int)PDFOpen:(NSString *)path withPassword:(NSString *)pwd {
[self PDFClose];
PDF_ERR err = 0;
m_doc = [[PDFDoc alloc] init]; err = [m_doc open:path :pwd]; switch( err )
{
case err_ok: break;
case err_password: return 2;
break; default: return 0;
}
CGRect rect = [[UIScreen mainScreen]bounds];
//GEAR
if (![self isPortrait] && rect.size.width < rect.size.height) { float height = rect.size.height;
rect.size.height = rect.size.width;
rect.size.width = height;
}
//END
if(SYS_VERSION>=7.0) {
m_view = [[PDFView alloc] initWithFrame:CGRectMake(0, 0, rect.size.width, rect.size.height)]; }
else
{
m_view = [[PDFView alloc] initWithFrame:CGRectMake(0, 0, rect.size.width, rect.size.height-20-44)];
}
[m_view vOpen :m_doc :(id<PDFViewDelegate>)self];
pagecount = [m_doc pageCount];
[self.view addSubview:m_view];
return 1; }
When the code executes, I know the view object is being created as I can see debug output coming from it in the console. However it will not display the new view in the iPhone simulator.
Feel free to provide feedback--just remember this is my first StackOverflow post! I'll be happy to provide more info as needed.
My main view controller was set to be the root view controller for my application. I had to set the navigation controller to be the root view controller, and then add the other view controllers after that.
After that I told navController to pop to the root view controller.
navController = [[UINavigationController alloc] initWithRootViewController:mainViewController];
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
[navController setNavigationBarHidden:YES];
[navController popToRootViewControllerAnimated:YES];
I am using UIPageViewController to show images full screen, the UIViewController which is added to UIPageController as a sub view / child has the images being showed using ImageView.
Problem is the images arent comming fullscreen, instead the pagecontrol view's donts are appearing at the bottom and that space is completely wasted. Please check the image attached.
Here is the code
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self view] bounds]];
NewsItemViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
[self.pageController didMoveToParentViewController:self];
Here NewsItemViewController is UIViewController showing images and some text and The MainViewController implements UIPageViewControllerDataSource protocol and necessary methods in MainViewController.
I believe there has to be a way to do show the things in full screen.
*** Also the MainViewController is a part of a storyboard if that matters.
Finally got the solution myself I just hide the page control from UIViewPageController and then extended the size of the UIPageViewController to cover up the gap left due to absense of page control.
NSArray *subviews = self.pageController.view.subviews;
UIPageControl *thisControl = nil;
for (int i=0; i<[subviews count]; i++) {
if ([[subviews objectAtIndex:i] isKindOfClass:[UIPageControl class]]) {
thisControl = (UIPageControl *)[subviews objectAtIndex:i];
}
}
thisControl.hidden = true;
self.pageController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height+40);
Curious. The docs say:
If both of the methods in “Supporting a Page Indicator” are
implemented and the page view controller’s transition style is
UIPageViewControllerTransitionStyleScroll, a page indicator is
visible.
Those two methods are:
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
}
Are you implementing these two data source methods? If so, perhaps if you remove them you won't have to manually remove the page control (dots)? A quick test would be to change the
UIPageViewControllerTransitionStyleScroll
to
UIPageViewControllerTransitionStylePageCurl
and see if the page control indicator dots go away. (After commenting out your hide method, of course.)
UIPageViewController dots are only shown when you have implemented following method:
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController;
check in you code. and also returning back zero in this method will hide the dots (UIPageControl)
I am adding for swift 2.2 compatible code
for view in self.view.subviews {
if view.isKindOfClass(UIScrollView) {
view.frame = UIScreen.mainScreen().bounds
} else if view.isKindOfClass(UIPageControl) {
view.backgroundColor = UIColor.clearColor()
}
}
This is the Swift 4 compatible solution, embedded in viewDidLayoutSubviews
for view in self.view.subviews {
if view.isKind(of:UIScrollView.self) {
view.frame = UIScreen.main.bounds
} else if view.isKind(of:UIPageControl.self) {
view.backgroundColor = UIColor.clear
}
}
Swift version :). Return Zero for below implementation inside UIPageViewController subclass.
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int { return 0 }
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int { return 0 }
Set your pager controller as so
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self view] bounds]];
And implement,this method should return any value greater than 1
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
// The selected item reflected in the page indicator.
return 2;
}
Now the gap at the bottom space is removed and no page control shown :)
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 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];
}
I'm a junior and I have been really struggling to get this to work, would really appreciate some help.
I am trying to build an App where you can scroll through different WebViews by selecting an item from a table in a popover (while the popover remains in place). Unfortunately, I can't get my scrollView to move when I select an item from the popover view.
I have set up a UIScrollView with page control in a main UIViewController. The scrollView is populated with (pageViewController) UIWebViews. The main UIViewController has a nav bar with a button, when the button is clicked it creates a Popover View. When I select an item from the table in the popover view I want my UIScrollView to scroll to a certain position (equates to a selected page), but the scrollView does not move.
The method below takes the value selected from the popover and uses the page number to determine where the scrollView should scroll to, but it doesn't work because at this point my scrollView is Null.
I can supply more code if needed.
main UIViewController.m code:
- (void)setDetailItem:(id)newDetailItem {
if (detailItem != newDetailItem) {
[detailItem release];
detailItem = [newDetailItem retain];
NSString *pgnum = [[NSString alloc] initWithString:[detailItem description]];
int pg;
if(pgnum == #"Page 1"){
pg =1;
}
if(pgnum == #"Page 2"){
pg =2;
}
if(pgnum == #"Page 3"){
pg =3;
}
[pgnum release];
pageControl.currentPage = pg;
[self loadScrollViewWithPage:pg - 1];
[self loadScrollViewWithPage:pg];
[self loadScrollViewWithPage:pg + 1];
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pg;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];
pageControlUsed = YES;
}
if (popoverController != nil) {
[popoverController dismissPopoverAnimated:YES];
}
}
This is how I triggered the popOverController
The infoButton is on the navbar.
The scrollView is NOT NULL in the infoButton
- (void)infoButton:(id)sender {
NSLog(#"Entering: infoButton");
// [self inspectView:self.view level:#""];
if (self.popoverController == nil) {
PopoverViewController *popoverViewController =
[[PopoverViewController alloc]
initWithNibName:#"PopoverViewController"
bundle:[NSBundle mainBundle]];
popoverViewController.navigationItem.title = #"Navigation";
UINavigationController *navController =
[[UINavigationController alloc]
initWithRootViewController:popoverViewController];
UIPopoverController *popover =
[[UIPopoverController alloc]
initWithContentViewController:navController];
popover.delegate = self;
[popoverViewController release];
[navController release];
self.popoverController = popover;
[popover release];
//NSLog(#"User pressed the info button");
}
[self.popoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; }
UIPopover, being neither the most obvious class to use memory-management wise, nor the most stable, does not require you to release it before you present it. In fact, doing so causes it to throw a fatal exception! Popovers should be released after they are removed from the screen, and/or in -dealloc.