UIPageViewController not showing anything

I have a PageContainerViewController ( a regular view controller with nothing in it ) that I Show Detail present from a previous view controller, which is supposed to embed a UIPageViewController in itself. The UIPageViewController is supposed to show, right now, two View Controllers, but it doesn't. The PageContainerViewController shows up as a blank white screen, and the UIPageViewController seems to be nowhere (I changed the background color to a bright green and still all I see is white).


Here's my code in PageContainerViewController (which has the UIPageViewControllerDataSource):

     var pageViewController: UIPageViewController?
    
    lazy var orderedViewControllers: [UIViewController] = {
        return [self.newVc(viewController: "vc1"),
                self.newVc(viewController: "vc2")]
    }()
    
    var currentlyShowingIndex = 0
    
    func newVc(viewController: String) -> UIViewController {
        return storyboard!.instantiateViewController(withIdentifier: viewController)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)ntroller
        
        pageViewController?.dataSource = self
        
        let arr: [UIViewController] = [orderedViewControllers.first!]
        
        pageViewController?.setViewControllers(arr, direction: .forward, animated: false, completion: nil)
        
        pageViewController?.view.frame = self.view.bounds
        
        self.addChildViewController(self.pageViewController!)
        self.view.addSubview(self.pageViewController!.view)
        pageViewController?.didMove(toParentViewController: self)
        
        pageViewController?.view.backgroundColor = UIColor.green
        
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
            return nil
        }
        
        let previousIndex = viewControllerIndex - 1
        
        guard previousIndex >= 0 else {
            return nil
        }
        
        guard orderedViewControllers.count > previousIndex else {
            return nil
        }
        
        currentlyShowingIndex -= 1
        
        return orderedViewControllers[previousIndex]
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
            return nil
        }
        
        let nextIndex = viewControllerIndex + 1
        let orderedViewControllersCount = orderedViewControllers.count
        
        guard orderedViewControllersCount != nextIndex else {
            return nil
        }
        
        guard orderedViewControllersCount > nextIndex else {
            return nil
        }
        
        currentlyShowingIndex += 1
        
        return orderedViewControllers[nextIndex]
    }
    
    func viewController(at index: Int) -> UIViewController {
        return orderedViewControllers[index]
    }
    
    func presentationIndex(for pageViewController: UIPageViewController) -> Int {
        return currentlyShowingIndex
    }
    
    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        return orderedViewControllers.count
    }

Accepted Reply

So, at least in your original set up, yopu should also conform to delegate:


class PageContainerViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate


You say:

I want to create the UIPageViewController with code, and add it to the PageContainerViewController.


You can add a view, but how do you add the viewController ?


Here is the full set up I tested:


A PageViewController, with horizontral navigation and Page Curl defined in IB


3 UIViewControllers, with storyboard ID as Page1, Page2, Page3

On each, just a label with the Page x content and a UIImageView with colored background


In PageViewController, I define a pageControl programmatically


Here is the code for the PageViewController.


import UIKit 
 
class PageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource { 
 
    let lightGray = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1.0) 
    
    var pageControl = UIPageControl() 
 
    func newVc(viewControllerID: String) -> UIViewController { 
        return storyboard!.instantiateViewController(withIdentifier: viewControllerID) 
    } 
    
    lazy var orderedViewControllers: [UIViewController] = { 
        return [self.newVc(viewControllerID: "Page1"), 
                self.newVc(viewControllerID: "Page2"), 
                self.newVc(viewControllerID: "Page3")] 
    }() 
    
    var currentlyShowingIndex = 0 
    
    override func viewDidLoad() { 
        super.viewDidLoad() 
 
        let childControllers: [UIViewController] = [orderedViewControllers.first!] 
        
        self.setViewControllers(childControllers, direction: .forward, animated: false, completion: nil) 
        
        self.view.frame = self.view.bounds 
        
        self.dataSource = self 
        self.delegate = self 
        configurePageControl() 
    } 
    
 
    func configurePageControl() { 
        
        pageControl = UIPageControl(frame: CGRect(x: 0,y: UIScreen.main.bounds.maxY - 50,width: UIScreen.main.bounds.width,height: 50)) 
        self.pageControl.numberOfPages = orderedViewControllers.count 
        self.pageControl.currentPage = 0 
        self.pageControl.tintColor = UIColor.black 
        self.pageControl.pageIndicatorTintColor = lightGray // UIColor.gray 
        self.pageControl.currentPageIndicatorTintColor = UIColor.green 
          //        self.pageControl.defersCurrentPageDisplay = false     // This has no effect 
        self.view.addSubview(pageControl) 
    } 
    
    // MARK: Delegate functions 
    
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { 
 
        let pageContentViewController = pageViewController.viewControllers![0] 
        self.pageControl.currentPage = orderedViewControllers.index(of: pageContentViewController)! 
        switch self.pageControl.currentPage { 
        case 0: self.pageControl.currentPageIndicatorTintColor = .green 
        case 1: self.pageControl.currentPageIndicatorTintColor = .orange 
        case 2: self.pageControl.currentPageIndicatorTintColor = .red 
        default: self.pageControl.currentPageIndicatorTintColor = lightGray // .gray 
        } 
        
    } 
 
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { 
        
        guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else { 
            return nil 
        } 
        let previousIndex = viewControllerIndex - 1 
        guard previousIndex >= 0 else { 
            return nil 
        } 
        guard orderedViewControllers.count > previousIndex else { 
            return nil 
        } 
        currentlyShowingIndex -= 1 
        
        return orderedViewControllers[previousIndex] 
    } 
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { 
        
        guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else { 
            return nil 
        } 
        let nextIndex = viewControllerIndex + 1 
        let orderedViewControllersCount = orderedViewControllers.count 
        guard orderedViewControllersCount != nextIndex else { 
            return nil 
        } 
        guard orderedViewControllersCount > nextIndex else { 
            return nil 
        } 
        currentlyShowingIndex += 1 
        
        return orderedViewControllers[nextIndex] 
    } 
    
    func viewController(at index: Int) -> UIViewController { 
        return orderedViewControllers[index] 
    } 
    
    func presentationIndex(for pageViewController: UIPageViewController) -> Int { 
        return currentlyShowingIndex 
    } 
    
    func presentationCount(for pageViewController: UIPageViewController) -> Int { 
        return orderedViewControllers.count 
    } 
 
}

Replies

I don't understand your global set up.

You have a UIViewController, named PageContainerViewController ?

Could you show the declaration of PageContainerViewController ? Which protocols does it conform to ?


Why do you declare pageViewController ?

Why do you declare the delegate functions here and not in UIPageViewController ?


Note: for readibility, you could change the label in the func, for instance to viewControllerID

func newVc(viewControllerID: String)



I tested the following which works (transitions defined in IB)


class PageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {

    lazy var orderedViewControllers: [UIViewController] = {
        return [self.newVc(viewController: "vc1"),
                self.newVc(viewController: "vc2")]
    }()
   
    var currentlyShowingIndex = 0
   
    override func viewDidLoad() {
        super.viewDidLoad()

        self.delegate = self
        self.dataSource = self
        let arr: [UIViewController] = [orderedViewControllers.first!]
       
        self.setViewControllers(arr, direction: .forward, animated: false, completion: nil)
       
        self.view.frame = self.view.bounds
       
    }



I think the key is

self.dataSource = self

self.delegate = self

Here is my declaration for the PageContainerViewController:

class PageContainerViewController: UIViewController, UIPageViewControllerDataSource


Sorry I was being a little vague before. Let me clarify.


I have a UIViewController that I want to be the holder of a UIPageViewController. The UIViewController is called PageContainerViewController since it is the View Controller that is containing the UIPageViewController. I want to create the UIPageViewController with code, and add it to the PageContainerViewController. By the way, I've never worked with adding child view controllers before, so this is all new to me.



Anyway, I tried what you said. I got rid of the PageContainerViewController, and added a UIPageViewController to the storyboard called PageViewController. I then copied and pasted the code from above and copied and pasted the data source function overrides from my previous code. The UIPageViewControllerDelegate overrides I added are as follows:


func presentationIndex(for pageViewController: UIPageViewController) -> Int {
        return currentlyShowingIndex
    }
    
    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        return orderedViewControllers.count
    }


Now, when I run the app and click the button I have that runs a Show Detail segue straight to the PageViewController, I don't see any animation, but see a black screen after a delay of the time the animation would normally take.


I feel like there is a problem pertaining to the Show Detail segue here

So, at least in your original set up, yopu should also conform to delegate:


class PageContainerViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate


You say:

I want to create the UIPageViewController with code, and add it to the PageContainerViewController.


You can add a view, but how do you add the viewController ?


Here is the full set up I tested:


A PageViewController, with horizontral navigation and Page Curl defined in IB


3 UIViewControllers, with storyboard ID as Page1, Page2, Page3

On each, just a label with the Page x content and a UIImageView with colored background


In PageViewController, I define a pageControl programmatically


Here is the code for the PageViewController.


import UIKit 
 
class PageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource { 
 
    let lightGray = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1.0) 
    
    var pageControl = UIPageControl() 
 
    func newVc(viewControllerID: String) -> UIViewController { 
        return storyboard!.instantiateViewController(withIdentifier: viewControllerID) 
    } 
    
    lazy var orderedViewControllers: [UIViewController] = { 
        return [self.newVc(viewControllerID: "Page1"), 
                self.newVc(viewControllerID: "Page2"), 
                self.newVc(viewControllerID: "Page3")] 
    }() 
    
    var currentlyShowingIndex = 0 
    
    override func viewDidLoad() { 
        super.viewDidLoad() 
 
        let childControllers: [UIViewController] = [orderedViewControllers.first!] 
        
        self.setViewControllers(childControllers, direction: .forward, animated: false, completion: nil) 
        
        self.view.frame = self.view.bounds 
        
        self.dataSource = self 
        self.delegate = self 
        configurePageControl() 
    } 
    
 
    func configurePageControl() { 
        
        pageControl = UIPageControl(frame: CGRect(x: 0,y: UIScreen.main.bounds.maxY - 50,width: UIScreen.main.bounds.width,height: 50)) 
        self.pageControl.numberOfPages = orderedViewControllers.count 
        self.pageControl.currentPage = 0 
        self.pageControl.tintColor = UIColor.black 
        self.pageControl.pageIndicatorTintColor = lightGray // UIColor.gray 
        self.pageControl.currentPageIndicatorTintColor = UIColor.green 
          //        self.pageControl.defersCurrentPageDisplay = false     // This has no effect 
        self.view.addSubview(pageControl) 
    } 
    
    // MARK: Delegate functions 
    
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { 
 
        let pageContentViewController = pageViewController.viewControllers![0] 
        self.pageControl.currentPage = orderedViewControllers.index(of: pageContentViewController)! 
        switch self.pageControl.currentPage { 
        case 0: self.pageControl.currentPageIndicatorTintColor = .green 
        case 1: self.pageControl.currentPageIndicatorTintColor = .orange 
        case 2: self.pageControl.currentPageIndicatorTintColor = .red 
        default: self.pageControl.currentPageIndicatorTintColor = lightGray // .gray 
        } 
        
    } 
 
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { 
        
        guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else { 
            return nil 
        } 
        let previousIndex = viewControllerIndex - 1 
        guard previousIndex >= 0 else { 
            return nil 
        } 
        guard orderedViewControllers.count > previousIndex else { 
            return nil 
        } 
        currentlyShowingIndex -= 1 
        
        return orderedViewControllers[previousIndex] 
    } 
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { 
        
        guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else { 
            return nil 
        } 
        let nextIndex = viewControllerIndex + 1 
        let orderedViewControllersCount = orderedViewControllers.count 
        guard orderedViewControllersCount != nextIndex else { 
            return nil 
        } 
        guard orderedViewControllersCount > nextIndex else { 
            return nil 
        } 
        currentlyShowingIndex += 1 
        
        return orderedViewControllers[nextIndex] 
    } 
    
    func viewController(at index: Int) -> UIViewController { 
        return orderedViewControllers[index] 
    } 
    
    func presentationIndex(for pageViewController: UIPageViewController) -> Int { 
        return currentlyShowingIndex 
    } 
    
    func presentationCount(for pageViewController: UIPageViewController) -> Int { 
        return orderedViewControllers.count 
    } 
 
}