How to Capture click event on Page Control swift - ios

I follow a tutorial on how to create UIPageControl and when I click on the page indicator it do not change the page
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
// User is on the first view controller and swiped left to loop to
// the last view controller.
guard previousIndex >= 0 else {
return orderedViewControllers.last
// Uncommment the line below, remove the line above if you don't want the page control to loop.
// return nil
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
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
// User is on the last view controller and swiped right to loop to
// the first view controller.
guard orderedViewControllersCount != nextIndex else {
return orderedViewControllers.first
// Uncommment the line below, remove the line above if you don't want the page control to loop.
// return nil
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
func configurePageControl() {
// The total number of pages that are available is based on how many available colors we have.
pageControl = UIPageControl(frame: CGRect(x: 0,y: UIScreen.main.bounds.maxY - 40,width: UIScreen.main.bounds.width,height: 40))
self.pageControl.numberOfPages = orderedViewControllers.count
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.gray
self.pageControl.pageIndicatorTintColor = UIColor.gray
self.pageControl.currentPageIndicatorTintColor = UIColor.white
self.view.addSubview(pageControl)
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
let pageContentViewController = pageViewController.viewControllers![0]
self.pageControl.currentPage = orderedViewControllers.index(of: pageContentViewController)!
}

In configurePageControl() add pageControl.addTarget
func configurePageControl() {
// The total number of pages that are available is based on how many available colors we have.
pageControl = UIPageControl(frame: CGRect(x: 0,y: UIScreen.main.bounds.maxY - 40,width: UIScreen.main.bounds.width,height: 40))
//add target for dots
pageControl.addTarget(self, action: #selector(self.pageControlSelectionAction(_:)), for: .touchUpInside)
self.pageControl.numberOfPages = orderedViewControllers.count
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.gray
self.pageControl.pageIndicatorTintColor = UIColor.gray
self.pageControl.currentPageIndicatorTintColor = UIColor.white
self.view.addSubview(pageControl)
}
Then add the method to change page when a dot is selected.
#objc func pageControlSelectionAction(_ sender: UIPageControl) {
//move page to wanted page
let page: Int? = sender.currentPage
self.pageViewController?.setViewControllers([[orderedViewControllers[page!]]], direction: .forword, animated: true, completion: nil)
}

In my case(Xcode 13), I received previous currentPage.
So use dispatchQueue asyncAfter
#objc func pageControlSelectionAction(_ sender: UIPageControl) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
let page: Int? = sender.currentPage
//scroll
}
}
Also I just tested 'DispatchQueue.main.async' was fine.

Related

Disable UIPageViewController Scroll

I have UIPageViewController with 3 child viewControllers. Is it possible to disable/prevent a user from scrolling to one specific viewController (i.e. User can scroll from view controller B to A, but cannot from B to C - until I later toggle permission allowing user to go from B to C).
Any guidance would be greatly appreciated!
I've been able to disable the scroll via UIPageViewController scrollView, but can't isolate disabling to a specific viewController.
func togglePageControllerScroll(shouldScroll: Bool) {
for view in view.subviews {
if let scrollView = view as? UIScrollView {
scrollView.isScrollEnabled = shouldScroll
break
}
}
}
I've also tried putting a check in UIPageViewController delegate functions viewControllerBefore and viewControllerAfter where I would return nil if the user tried scrolling from B to C, but it seems like doing so also returns nil for viewController A...
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = childViewControllers.firstIndex(of: viewController) else { return nil }
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else { return nil }
guard childViewControllers.count > previousIndex else { return nil }
guard let previousViewController = childViewControllers[safe: previousIndex] else { return nil }
if viewController is B && previousViewController is A {
return nil
} else {
return previousViewController
}
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = childViewControllers.firstIndex(of: viewController) else { return nil }
let nextIndex = viewControllerIndex + 1
guard childViewControllers.count != nextIndex else {
return nil
}
guard childViewControllers.count > nextIndex else { return nil }
guard let nextViewController = childViewControllers[safe: nextIndex] else { return nil }
if viewController is B && nextViewController is C {
return nil
} else {
return nextViewController
}
}
One approach - which, I think, is pretty straight-forward and would be a reasonable solution...
Add a maxPages property to your page view controller. Then, in viewControllerAfter, do this:
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pageViewControllers.firstIndex(of: viewController) else { return nil }
let nextIndex = viewControllerIndex + 1
// if we are at the Last controller,
// OR
// we are at the "maxPages" controller
// return nil
guard nextIndex < pageViewControllers.count, nextIndex < maxPages else { return nil }
return pageViewControllers[nextIndex]
}
Start maxPages at 2, then in the class that is controlling your page view controller, when you want to allow the user to go to the 3rd page, change the maxPages property to 3.
Here is a quick example...
We'll start with a simple "page" view controller - a label centered vertically:
class ExamplePageVC: UIViewController {
let theLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .white
v.textAlignment = .center
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(theLabel)
NSLayoutConstraint.activate([
theLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
theLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
theLabel.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9),
])
}
}
Then, a view controller to hold our page view controller, along with a switch to Allow/Not-Allow scrolling to the 3rd page:
class PagesViewController: UIViewController {
var myPVC: MyPageViewController!
override func viewDidLoad() {
super.viewDidLoad()
// a label and toggle switch to enable/disable the 3rd page
let label = UILabel()
label.text = "Allow 3rd Page?"
let sw = UISwitch()
sw.isOn = false
sw.addTarget(self, action: #selector(switchChanged(_:)), for: .valueChanged)
let stack = UIStackView(arrangedSubviews: [label, sw])
stack.axis = .horizontal
stack.spacing = 8
// a UIView to hold the page view controller
let pvcContainer = UIView()
pvcContainer.backgroundColor = .gray
[stack, pvcContainer].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
pvcContainer.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
pvcContainer.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
pvcContainer.centerYAnchor.constraint(equalTo: g.centerYAnchor),
pvcContainer.heightAnchor.constraint(equalTo: pvcContainer.widthAnchor, multiplier: 0.75),
stack.bottomAnchor.constraint(equalTo: pvcContainer.topAnchor, constant: -20.0),
stack.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
let pvc = MyPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
addChild(pvc)
pvc.view.translatesAutoresizingMaskIntoConstraints = false
pvcContainer.addSubview(pvc.view)
NSLayoutConstraint.activate([
pvc.view.topAnchor.constraint(equalTo: pvcContainer.topAnchor),
pvc.view.leadingAnchor.constraint(equalTo: pvcContainer.leadingAnchor),
pvc.view.trailingAnchor.constraint(equalTo: pvcContainer.trailingAnchor),
pvc.view.bottomAnchor.constraint(equalTo: pvcContainer.bottomAnchor),
])
pvc.didMove(toParent: self)
myPVC = pvc
}
#objc func switchChanged(_ sender: UISwitch) {
let n: Int = sender.isOn ? 3 : 2
myPVC.maxPages = n
}
}
and our custom Page View Controller:
class MyPageViewController: UIPageViewController {
var maxPages: Int = 2 {
didSet {
// get the current page index
var n = currentIndex
// set controllers, keeping the current page in view
// unless, we are decreasing the count and a higher-index page is showing
if n > maxPages - 1 {
n = maxPages - 1
}
setViewControllers([pageViewControllers[n]], direction: .forward, animated: false, completion: nil)
}
}
// so we can get the index of the current page
var currentIndex: Int {
guard let vc = viewControllers?.first else { return 0 }
return pageViewControllers.firstIndex(of: vc) ?? 0
}
let colors: [UIColor] = [
.systemRed,
.systemGreen,
.systemBlue,
]
var pageViewControllers: [UIViewController] = [UIViewController]()
override init(transitionStyle style: UIPageViewController.TransitionStyle, navigationOrientation: UIPageViewController.NavigationOrientation, options: [UIPageViewController.OptionsKey : Any]? = nil) {
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
// instantiate all "pages"
for i in 0..<colors.count {
let vc = ExamplePageVC()
vc.theLabel.text = "Page: \(i)"
vc.view.backgroundColor = colors[i]
pageViewControllers.append(vc)
}
setViewControllers([pageViewControllers[0]], direction: .forward, animated: false, completion: nil)
}
}
extension MyPageViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pageViewControllers.firstIndex(of: viewController) else { return nil }
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else { return nil }
return pageViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pageViewControllers.firstIndex(of: viewController) else { return nil }
let nextIndex = viewControllerIndex + 1
// if we are at the Last controller,
// OR
// we are at the "maxPages" controller
// return nil
guard nextIndex < pageViewControllers.count, nextIndex < maxPages else { return nil }
return pageViewControllers[nextIndex]
}
}
It will look like this:
Arrays are zero-based, so we've set the labels to match the array index.
When the switch is Off, we can only scroll back-and-forth between Page 0 and Page 1.
When the switch is On, we can scroll on to the 3rd page - Page 2:
Notice that we have a little bit of code inside the didSet {} block of our maxPages var. When we set maxPages to a new value, we need to "re-set" the view controllers array of our Page View Controller, while keeping the current page visible.

UIPageViewController - changing dots won't work

I'm using a UIPageViewController to swipe between two ViewControllers. I tried to display indicator dots, which worked (see addDots). But somehow the dots will only change on next swipe, but not when I swipe to the previous view.
Can anybody tell me where I've made a mistake?
Here's my code:
import UIKit
class LeasingTutorialViewController: UIPageViewController, UIPageViewControllerDataSource {
var pageControl = UIPageControl()
lazy var viewControllerList:[UIViewController] = {
let sb = UIStoryboard(name: "Main", bundle: nil)
let vc1 = sb.instantiateViewController(withIdentifier: "leasingPageOne")
let vc2 = sb.instantiateViewController(withIdentifier: "leasingPageTwo")
return [vc1, vc2]
}()
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
addDots()
if let firstViewController = viewControllerList.first as? LeasingPageOneViewController {
self.setViewControllers([firstViewController], direction: .forward, animated: true, completion: nil)
}
}
func addDots() {
let color = UIColor(red: 24/255, green: 90/255, blue: 189/255, alpha: 1)
pageControl = UIPageControl(frame: CGRect(x: 0,y: UIScreen.main.bounds.maxY - 100,width: UIScreen.main.bounds.width,height: 50))
pageControl.numberOfPages = viewControllerList.count
pageControl.currentPage = 0
pageControl.tintColor = color
pageControl.pageIndicatorTintColor = UIColor.lightGray
pageControl.currentPageIndicatorTintColor = color
self.view.addSubview(pageControl)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let vcIndex = viewControllerList.firstIndex(of: viewController) else { return nil }
let previousIndex = vcIndex - 1
guard previousIndex >= 0 else { return nil }
guard viewControllerList.count > previousIndex else { return nil }
self.pageControl.currentPage = previousIndex
return viewControllerList[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let vcIndex = viewControllerList.firstIndex(of: viewController) else { return nil }
let nextIndex = vcIndex + 1
guard viewControllerList.count != nextIndex else { return nil }
guard viewControllerList.count > nextIndex else { return nil }
self.pageControl.currentPage = nextIndex
return viewControllerList[nextIndex]
}
}

Fast scroll UIPageViewController prevents viewcontroller from updating

I have a UIPageviewcontroller which got two controllers inside. As you swipe to the next, I use the viewController argument to set the appropriate delegate. But I experience that if you swipe too fast, the function viewControllerAfter isn't updating the viewController correctly. The initially swipe should update the index of the viewcontroller from 0 to 1, but doesn't do so if you swipe too fast.
import UIKit
class WizardPageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
lazy var orderedViewControllers: [UIViewController] = {
return [self.newVc(viewController: "intro"),
self.newVc(viewController: "welcome")]
}()
var pageControl = UIPageControl()
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
self.delegate = self
configurePageControl()
// This sets up the first view that will show up on our page control
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .forward,
animated: true,
completion: nil)
}
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
let pageContentViewController = pageViewController.viewControllers![0]
self.pageControl.currentPage = orderedViewControllers.index(of: pageContentViewController)!
}
func newVc(viewController: String) -> UIViewController {
return UIStoryboard(name: "Wizard", bundle: nil).instantiateViewController(withIdentifier: viewController)
}
func configurePageControl() {
// The total number of pages that are available is based on how many available colors we have.
pageControl = UIPageControl(frame: CGRect(x: 0,y: UIScreen.main.bounds.maxY - 225,width: UIScreen.main.bounds.width,height: 50))
self.pageControl.numberOfPages = orderedViewControllers.count
self.pageControl.currentPage = 0
pageControl.isEnabled = false
//self.pageControl.tintColor = UIColor.black
self.pageControl.pageIndicatorTintColor = UIColor.gray
self.pageControl.currentPageIndicatorTintColor = UIColor(red:0.647, green:0.192, blue:0.216, alpha:1.00)
self.view.addSubview(pageControl)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
print(orderedViewControllers.index(of: viewController))
let previousIndex = viewControllerIndex - 1
// User is on the first view controller and swiped left to loop to
// the last view controller.
guard previousIndex >= 0 else {
return nil
// Uncommment the line below, remove the line above if you don't want the page control to loop.
// return nil
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
return orderedViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
print(orderedViewControllers.index(of: viewController)) // Returns 0 is I swipe too fast, otherwise 1
if let vc = orderedViewControllers[viewControllerIndex] as? WelcomeViewController {
vc.delegate = self
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count
// User is on the last view controller and swiped right to loop to
// the first view controller.
guard orderedViewControllersCount != nextIndex else {
return nil
// Uncommment the line below, remove the line above if you don't want the page control to loop.
// return nil
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
}
I've encountered exactly the same problem. It essentially boils down to UIPageViewController (_UIQueuingScrollView to be exact) not updating its view hierarchy correctly.
I noticed UIPageViewController is clever enough not to add/remove the content view if the page remains the same (even if it's in wrong place in the view hierarchy it somehow manages to cope with it so it looks good to the user). That's why I added a PageTrackingView to observe the content view being added (or not) to UIPageViewController view hierarchy. By tracking if the change happens I can calculate the current index by flipping it. So currently the workaround is good enough for 2 pages only.
class PageTrackingView: UIView {
var pageIndexable: PageIndexable?
override func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)
if var pageIndexable = pageIndexable {
let newIndex = (pageIndexable.internalIndex == 0) ? 1 : 0
pageIndexable.internalIndex = newIndex
}
//Reset pageIndexable so that the index flip is fired only once
pageIndexable = nil
}
}
protocol PageIndexable {
var internalIndex: Int { get set }
}
class PageViewController: UIPageViewController,
UIPageViewControllerDataSource,
UIPageViewControllerDelegate,
PageIndexable {
private var supportedViewControllers = [UIViewController]()
internal var internalIndex: Int = 0 {
didSet {
if supportedViewControllers.indices.contains(internalIndex) {
//Do something with the actual internalIndex value
} else {
assertionFailure()
}
}
init(transitionStyle style: UIPageViewControllerTransitionStyle = .scroll,
navigationOrientation: UIPageViewControllerNavigationOrientation = .horizontal,
options: [String: Any]? = nil,
viewControllers: [T]) {
supportedViewControllers = viewControllers
if !supportedViewControllers.isEmpty {
let viewController = supportedViewControllers[0]
let trackingView = PageTrackingView(frame: viewController.view.frame)
viewController.view.frame = viewController.view.bounds
trackingView.addSubview(viewController.view)
viewController.view = trackingView
}
super.init(transitionStyle: style,
navigationOrientation: navigationOrientation,
options: options)
}
func pageViewController(_ pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool) {
if !supportedViewControllers.isEmpty, let trackingView = supportedViewControllers[0].view as? PageTrackingView {
trackingView.pageIndexable = self
}
}
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
datasource = self
}
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let index = arrayVCs.index(of: viewController) else {
return nil
}
if index == 0 {
return nil
}
let prevIndex = abs((index - 1) % arrayVCs.count)
return arrayVCs[prevIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let index = arrayVCs.index(of: viewController) else {
return nil
}
if index == arrayVCs.count - 1 {
return nil
}
let nextIndex = abs((index + 1) % arrayVCs.count)
return arrayVCs[nextIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if let viewController = pageViewController.viewControllers?[0] {
guard let index = arrayVCs.index(of: viewController) else {
return
}
self.segment.selectedSegmentIndex = index
}
}
Try this. It's work for me.

UIPageViewController page location dots appear overlapped?

I am having this issue where the dots in my PageViewController are appearing with a dot in-between two dots. When I save the pages and load them, then the pages appear with normal dots, the appropriate number of dots, and normal placement. Here is an image of what I am talking about.
Hierarchy debugger shows this image. Obviously the dots are appearing multiple times, behind one another.
Here is the code that I use for the dots:
func configurePageControl() {
pageControl = UIPageControl(frame: CGRect(x: 0,y: UIScreen.main.bounds.maxY - 50,width: UIScreen.main.bounds.width,height: 50))
self.pageControl.numberOfPages = storyPageViewControllers.count
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.black
self.pageControl.alpha = 0.5
self.pageControl.pageIndicatorTintColor = UIColor.white
self.pageControl.currentPageIndicatorTintColor = UIColor.black
self.view.addSubview(pageControl)
}
configurePageControl() is called in the viewDidAppear. I tried moving it to the viewDidLoad, but it only showed one dot and didn't show more as I made new pages.
Here is my extension on the UIPageViewController class if you want to see how my pages are created:
extension BookPageViewController: UIPageViewControllerDelegate, UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = storyPageViewControllers.index(of: viewController as! TemplateViewController) else { return nil }
if viewControllerIndex == 0 { return nil } // Page won't scroll below first page.
let previousIndex = viewControllerIndex - 1
guard storyPageViewControllers.count > previousIndex else { return nil }
currentPage = Double(previousIndex)
return storyPageViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = storyPageViewControllers.index(of: viewController as! TemplateViewController) else { return nil }
let nextIndex = viewControllerIndex + 1
let storyPageViewControllersCount = storyPageViewControllers.count
guard storyPageViewControllersCount != nextIndex else { return nil }
guard storyPageViewControllersCount > nextIndex else { return nil }
currentPage = Double(nextIndex)
return storyPageViewControllers[nextIndex]
}
// MARK: Delegate function
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
let pageContentViewController = pageViewController.viewControllers![0]
self.pageControl.currentPage = storyPageViewControllers.index(of: pageContentViewController as! TemplateViewController)!
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return storyPageViewControllers.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first,
let firstViewControllerIndex = storyPageViewControllers.index(of: firstViewController as! TemplateViewController) else
{ return 0 }
return firstViewControllerIndex
}
}
1- Definitely don't call configurePageControl() on viewDidappear. Is wrong, can give u bugs and weird user experience.
2- U r calling configurePageControl() 3 times. By chance do you have 3 viewController on your pageViewController?
Solutions:
A: I would create the pageViewController in Storyboard and just update the dots number just after u finish setting all ViewControllers.
B: Keep all as is BUT:
1- Move configurePageControl() to ViewDidLoad()
2- Leave it like this, with initial dots to 0
(tip 1: avoid using "self" when is not necessary)
(tip 2: avoid using frames, use constraints)
func configurePageControl() {
pageControl = UIPageControl(frame: CGRect(x: 0,y:
UIScreen.main.bounds.maxY - 50,width:
UIScreen.main.bounds.width,height: 50))
pageControl.numberOfPages = 0
pageControl.currentPage = 0
pageControl.tintColor = UIColor.black
pageControl.alpha = 0.5
pageControl.pageIndicatorTintColor = UIColor.white
pageControl.currentPageIndicatorTintColor = UIColor.black
view.addSubview(pageControl)
}
3- Just after u finish setting all viewControllers, update the pageViewControllers dots number
pageControl.numberOfPages = storyPageViewControllers.count
Notice it should work if placed here, but is not the right place. U can do it just for testing.
func presentationCountForPageViewController(pageViewController:
UIPageViewController) -> Int {
pageControl.numberOfPages = storyPageViewControllers.count
return storyPageViewControllers.count
}
UIPageViewController already has a page control; all you have to do is show it (by implementing the two data source methods that do that). You shouldn't be adding another page control to it. The included page control already syncs itself to the page view controller. You're doing far more work than you should be doing.

Data source methods of UIPageViewController are not called

I have the following code in my iOS app:
class BannerTableViewCell: UITableViewCell, UIPageViewControllerDataSource {
private var pageViewController: UIPageViewController!
private var pages: [UIViewController] = []
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = UIColor.clearColor()
self.backgroundView?.backgroundColor = UIColor.clearColor()
self.contentView.backgroundColor = UIColor.clearColor()
self.selectionStyle = UITableViewCellSelectionStyle.None
pageViewController = UIPageViewController();
self.pageViewController.dataSource = self
pageViewController.view.frame = CGRect(x: 0, y: 0, width: UIScreen.mainScreen().bounds.width, height: 130)
self.addSubview(pageViewController.view)
//Example data
let v1 = UIViewController()
v1.view.frame = CGRect(x: 0, y: 0, width: UIScreen.mainScreen().bounds.width, height: 130);
v1.view.backgroundColor = UIColor.blueColor()
let v2 = UIViewController()
v2.view.frame = CGRect(x: 0, y: 0, width: UIScreen.mainScreen().bounds.width, height: 130);
v2.view.backgroundColor = UIColor.greenColor()
pages.append(v1)
pages.append(v2)
}
func pageViewController(pageViewController: UIPageViewController,
viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pages.indexOf(viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return nil
}
guard pages.count > previousIndex else {
return nil
}
return pages[previousIndex]
}
func pageViewController(pageViewController: UIPageViewController,
viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pages.indexOf(viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = pages.count
guard orderedViewControllersCount != nextIndex else {
return nil
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return pages[nextIndex]
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return pages.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
I instantiate this cell in the table view, however page view controller in this cell is always empty, and the data source methods of pageViewController are not called. Do you have any idea why they are not called?
You should use the documented initializer to instantiate the UIPageViewController:
public init(transitionStyle style: UIPageViewControllerTransitionStyle, navigationOrientation: UIPageViewControllerNavigationOrientation, options: [String : AnyObject]?)
Also the ViewControllers you create at the end of awakeFromNib can be placed into the pageViewController right away.
pageViewController.setViewControllers([v1, v2], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
A page indicator will be visible if both methods are implemented, transition style is 'UIPageViewControllerTransitionStyleScroll', and navigation orientation is 'UIPageViewControllerNavigationOrientationHorizontal'.
Both methods are called in response to a 'setViewControllers:...' call, but the presentation index is updated automatically in the case of gesture-driven navigation.
It could also be because the Page View Controller is not your Root View Controller.
You could add the following code in your segue:
let x = (UIApplication.shared.delegate as! AppDelegate).window!
x.rootViewController = yourViewController()

Resources