I am trying to jump to a specific page on a button press, I have made a ViewController which contains a ContainerView of the PageViewController, so that I can have permanent buttons on top. From there i do:
@IBAction func jump(_ sender: Any) {
PageViewController().jump()
}
and here is the Jump function in the PageViewController
func jump() {
var lastCoinNumber:Int = Manager.shared.coins.index(of: last!)!
lastNumber = lastCoinNumber
if let jumpView = viewControllerAtIndex(lastNumber, storyboard: self.storyboard!) {
self.setViewControllers([jumpView], direction: .forward, animated: false, completion: nil)
}
}
For some reason, I always get an error at the ViewControllerAtIndex part of the jump function:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value . even if I manually choose an index it does the same.
if let firstViewController = viewControllerAtIndex(0, storyboard: self.storyboard!) {
self.setViewControllers([firstViewController], direction: .forward, animated: false, completion: nil)
}
Above is how I set up the ViewControllers initialy in viewDidLoad()
, and that works fine, if I am essentially doing the same thing, why doesn't it work? Here is how I index the pages and instantiate the viewcontrollers, they use the same single ViewController so a new instance of it is created for each page, otherwise I would have created an array of viewControllers.
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if !completed { return }
DispatchQueue.main.async() {
self.dataSource = nil
self.dataSource = self
self.pageControl.numberOfPages = (Manager.shared.coins.count)
}
}
func viewControllerAtIndex(_ index: Int, storyboard: UIStoryboard) -> TemplateViewController? {
if Manager.shared.coins.count == 0 || index >= Manager.shared.coins.count {
return nil
}
let templateViewController = storyboard.instantiateViewController(withIdentifier: "templateController") as! TemplateViewController
templateViewController.dataObject = Manager.shared.coins[index]
return templateViewController
}
func indexOfViewController(_ viewController: TemplateViewController) -> Int {
self.pageControl.currentPage = Manager.shared.coins.index(of: viewController.dataObject)!
return Manager.shared.coins.index(of: viewController.dataObject)!
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
var index = self.indexOfViewController(viewController as! TemplateViewController)
if (index == 0) {
return nil
}
index -= 1
return self.viewControllerAtIndex(index, storyboard: viewController.storyboard!)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
var index = self.indexOfViewController(viewController as! TemplateViewController)
if index == -1 {
return nil
}
index += 1
if index == Manager.shared.coins.count {
return nil
}
return self.viewControllerAtIndex(index, storyboard: viewController.storyboard!)
}
I hope this is enough information, thank you.
Edit:
Coin is a string array, I declare this:
var last = Manager.shared.coins.last
var lastNumber: Int = 0
and then the jump() takes care of the rest. So I am getting the last object, finding its index, and then doing setViewControllers with the index of the last number, which should correspond with viewControllerAtIndex, because that uses the same array to index and load the pages.
for some reason, when I try to do: var lastCoinNumber:Int = Manager.shared.coins.index(of: last) It forces me to use ! because: "Value of optional type 'Array.Index?' (aka 'Optional') not unwrapped; did you mean to use '!' or '?'?"
I'm not sure what I'm doing wrong in that regard, here is how coins is declared:
var coins = [""]
Edit: Code in full, PageViewController:
import UIKit
var pageView = PageViewController()
class PageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
//page Control dots
var pageControl = UIPageControl()
var last = Manager.shared.coins.last!
var lastNumber: Int = 0
var viewControllerArray = [UIViewController()]
func configurePageControl() {
// The total number of pages that are available is based on ManagerCoins names array
pageControl = UIPageControl(frame: CGRect(x: 0,y:
UIScreen.main.bounds.maxY - 50,width: UIScreen.main.bounds.width,height:
50))
self.pageControl.numberOfPages = (Manager.shared.coins.count)
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.black
self.pageControl.pageIndicatorTintColor = UIColor.gray
self.pageControl.currentPageIndicatorTintColor = UIColor.white
self.view.addSubview(pageControl)
}
// MARK: Delegate functions
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
}
func viewControllerAtIndex(_ index: Int, storyboard: UIStoryboard) -> TemplateViewController? {
if Manager.shared.coins.count == 0 || index >= Manager.shared.coins.count {
return nil
}
let templateViewController = storyboard.instantiateViewController(withIdentifier: "templateController") as! TemplateViewController
templateViewController.dataObject = Manager.shared.coins[index]
return templateViewController
}
func indexOfViewController(_ viewController: TemplateViewController) -> Int {
self.pageControl.currentPage = Manager.shared.coins.index(of: viewController.dataObject)!
return Manager.shared.coins.index(of: viewController.dataObject)!
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
var index = self.indexOfViewController(viewController as! TemplateViewController)
if (index == 0) {
return nil
}
index -= 1
return self.viewControllerAtIndex(index, storyboard: viewController.storyboard!)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
var index = self.indexOfViewController(viewController as! TemplateViewController)
if index == -1 {
return nil
}
index += 1
if index == Manager.shared.coins.count {
return nil
}
return self.viewControllerAtIndex(index, storyboard: viewController.storyboard!)
}
func jump() {
var lastCoinNumber:Int = Manager.shared.coins.index(of: last!)!
lastNumber = lastCoinNumber
if let jumpView = viewControllerAtIndex(lastCoinNumber, storyboard: self.storyboard!){
self.setViewControllers([jumpView], direction: .forward, animated: false, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
configurePageControl()
self.dataSource = self
if let firstViewController = viewControllerAtIndex(0, storyboard: self.storyboard!){
self.setViewControllers([firstViewController], direction: .forward, animated: false, completion: nil)
}
}
}
Edit:
After much research, This seems to be the correct way to jump a page, by calling setViewControllers, the first comment on this answer seems to have the same problem, it works fine for setting the view on viewDidLoad() but trying to programmatically navigate to a page based on the index falls to an error. https://stackoverflow.com/a/24335008/10058239