Search code examples
iosswiftxcodeuipageviewcontroller

UIPageViewController - Double Tapping Dots Area Shows Partial Transition (Xcode 8, Swift 3)


Double tapping anywhere at the bottom of a page control area (where the dots are) pauses the transition from one view controller to another.

Double tap area

Example of double tapping:

Example 1 Example 2

I simplified a UIPageViewController project to demonstrate this:

Storyboard Storyboard

PageViewController.swift

import UIKit

class PageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

    var pages: [UIViewController] = [UIViewController]()

    override func viewDidLoad() {
        super.viewDidLoad()

        dataSource = self
        delegate = self

        pages.append(UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Page1"))
        pages.append(UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Page2"))

        setViewControllers([pages.first!], direction: .forward, animated: true, completion: nil)
    }

    // This allows the dots to appear on top of the views
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        for view in self.view.subviews {

            if view is UIScrollView {
                view.frame = UIScreen.main.bounds
            } else if view is UIPageControl {
                view.backgroundColor = UIColor.clear
            }
        }
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        let viewControllerIndex = pages.index(of: viewController)

        if viewControllerIndex == 1 {
            return pages[0]
        }

        return nil
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        let viewControllerIndex = pages.index(of: viewController)

        if viewControllerIndex == 0 {
            return pages[1]
        }

        return nil
    }

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

    func presentationIndex(for pageViewController: UIPageViewController) -> Int {
        return 0
    }
}

The source of the problem lies in this code:

    // This allows the dots to appear on top of the views
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        for view in self.view.subviews {

            if view is UIScrollView {
                view.frame = UIScreen.main.bounds
            } else if view is UIPageControl {
                view.backgroundColor = UIColor.clear
            }
        }

When this code is removed there is no partial transitions but you will now see a black bar which is also not desired.

blackbar

This is a common solution I have found throughout the web and Stackoverflow to get the Page Control (dots) to show on top of the views instead of within its own bar at the bottom of the screen.

So that's all I'm looking for. A solution that:

  • Shows the dots on top of the views (no black bar)
  • No problems with transitioning.

Thanks in advance for any help!


Solution

  • Jay, thanks again for helping me on this. I didn't know you could reference the UIPageControl in the UIPageViewController but I did some research and found a way. I changed the viewDidLoad to this and it worked! No hack needed:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        dataSource = self
        delegate = self
    
        setViewControllers([pages.first!], direction: .forward, animated: true, completion: nil)
    
        let pageControl = UIPageControl.appearance(whenContainedInInstancesOf: [PageViewController.self])
        pageControl.isUserInteractionEnabled = false
    }