UIPageViewController - changing dots won't work - ios

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]
}
}

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.

How to Capture click event on Page Control swift

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.

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.

Pre-load all UIViewControllers in UIPageViewControllers (Swift)

I currently have 3 UIViewControllers in the storyboard and I connected them into one UIPageViewController with scrollView. However, I'm having some issues initializing the app on the center UIViewController due to not preloading all the UIViewControllers in the PageViewController. I'm using the scrollview contentOffset to show the center ViewController when the user first open the app viewDidLoad.
I have attached the code I'm using here:
var currentIndex = 0
var mainScrollView = UIScrollView()
lazy var ViewControllerArray: [UIViewController] = {
return [self.ViewControllerInstance(name: "RightVC"),
self.ViewControllerInstance(name: "CenterVC"),
self.ViewControllerInstance(name: "LeftVC")]
}()
private func ViewControllerInstance(name: String) -> UIViewController{
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name)
}
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
self.delegate = self
if let CenterViewController = ViewControllerArray.first {
setViewControllers([CenterViewController] , direction: .reverse, animated: false, completion: nil)
}
}
override func viewDidAppear(_ animated: Bool) {
mainScrollView = view.subviews.filter { $0 is UIScrollView }.first as! UIScrollView
mainScrollView.delegate = self
mainScrollView.contentOffset.x = self.view.bounds.size.width * 2
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let ViewControllerIndex = ViewControllerArray.index(of: viewController) else {
return nil
}
let PreviousIndex = ViewControllerIndex - 1
currentIndex = ViewControllerIndex
guard PreviousIndex >= 0 else {
return nil
}
guard ViewControllerArray.count > PreviousIndex else {
return nil
}
return ViewControllerArray[PreviousIndex]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let ViewControllerIndex = ViewControllerArray.index(of: viewController) else {
return nil
}
let NextIndex = ViewControllerIndex + 1
currentIndex = ViewControllerIndex
guard NextIndex < ViewControllerArray.count else {
return nil
}
guard ViewControllerArray.count > NextIndex else {
return nil
}
return ViewControllerArray[NextIndex]
}
// Control bounce # DidScroll & EndDragging
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let lastPosition = scrollView.contentOffset.x
if (currentIndex == ViewControllerArray.count - 1) && (lastPosition > scrollView.frame.width) {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
} else if currentIndex == 0 && lastPosition < scrollView.frame.width {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
}
}
// ^^
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let lastPosition = scrollView.contentOffset.x
if (currentIndex == ViewControllerArray.count - 1) && (lastPosition > scrollView.frame.width) {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
} else if currentIndex == 0 && lastPosition < scrollView.frame.width {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
}
}
UPDATE:
I used a variable IntialOpen as a bool for the system to understand whether the app was opened before and I also added If/else statement inside the public functions.
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let ViewControllerIndex = ViewControllerArray.index(of: viewController) else {
return nil
}
let PreviousIndex = ViewControllerIndex - 1
if !initialOpen {
currentIndex = ViewControllerIndex
}else{
currentIndex = ViewControllerIndex + 1
initialOpen = false
}
guard PreviousIndex >= 0 else {
return nil
}
guard ViewControllerArray.count > PreviousIndex else {
return nil
}
return ViewControllerArray[PreviousIndex]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let ViewControllerIndex = ViewControllerArray.index(of: viewController) else {
return nil
}
let NextIndex = ViewControllerIndex + 1
if !initialOpen {
currentIndex = ViewControllerIndex
}else{
currentIndex = ViewControllerIndex + 1
initialOpen = false
}
guard NextIndex < ViewControllerArray.count else {
return nil
}
guard ViewControllerArray.count > NextIndex else {
return nil
}
return ViewControllerArray[NextIndex]
}
This worked by pretty much forcing it to open on the second page if its the first time the app has started.

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