When PageController trying to present it's content I'm encountering following message in console:
"[Assert] Window should be nonnil here unless a subclass is mistakenly sending this to a child when no window can be found".
App is not crashing it's just not showing content it's supposed to show in PageController. Code seems fine to me:
class RulesPageViewController: UIPageViewController {
var rulesDelegate: RulesPageVeiwControllerDelegate?
var pageHeadings = ["1st page","2nd","3rd"]
var currentIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
self.delegate = self
if let startingViewController = contentViewController(at: 0)
{
self.setViewControllers([startingViewController], direction: .forward, animated: false, completion: nil)
}
}
func contentViewController(at index: Int)-> PageContentViewController?
{
if(index < 0 || index >= pageHeadings.count)
{
return nil
}
if let pageContentViewController = UIStoryboard(name: "GameRules", bundle: nil).instantiateViewController(withIdentifier: "PageContentViewController") as? PageContentViewController{
pageContentViewController.index = index
return pageContentViewController
}
return nil
}
}
extension RulesPageViewController: UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed {
if let contentViewController = pageViewController.viewControllers?.first as? PageContentViewController
{
currentIndex = contentViewController.index
rulesDelegate?.didUpdatePageIndex(currentIndex: currentIndex)
}
}
}
}
extension RulesPageViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
var index = (viewController as! PageContentViewController).index
index -= 1
return contentViewController(at: index)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
var index = (viewController as! PageContentViewController).index
index += 1
return contentViewController(at: index)
}
}
Problem was solved: PageContentViewController was conforming to wrong class
This question already has answers here:
Refresh UIPageViewController - reorder pages and add new pages
(4 answers)
Closed 4 years ago.
I am looking to how to reload the UI page controller.
On a table VC I have added UIHeaderView and embedded the UIPage VC.
Page VC is loading images dynamically from firebase.
First time it is loading the right set of images but when on the table VC I selecting other cel the Page VC need to refresh its data.
How to achieve this functionality
Here is the coed I have written for PAGE VC
//-----PAGE VC CODE------
import UIKit
import Firebase
protocol ProductImagesPageVCDelegate: class
{
func setupPageController(numberOfPages: Int)
func turnPageController(to index: Int)
}
class ProductImagesPageVC: UIPageViewController {
var product: Product!
weak var pageViewControllerDelegate: ProductImagesPageVCDelegate?
struct StoryBoard {
static let productImageVC = "ProductImageVC"
}
lazy var controllers: [UIViewController] = {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var controllers = [UIViewController]()
if let imageLinks = self.product.imageLinks
{
for imageLink in imageLinks
{
let productImageVC = storyboard.instantiateViewController(withIdentifier: StoryBoard.productImageVC)
controllers.append(productImageVC)
}
}
self.pageViewControllerDelegate?.setupPageController(numberOfPages: controllers.count)
return controllers
}()
override func viewDidLoad() {
super.viewDidLoad()
// if #available(iOS 11.0, *) {
// contentInsetAdjustmentBehavior = .never
// } else {
// automaticallyAdjustsScrollViewInsets = false
// }
automaticallyAdjustsScrollViewInsets = false
dataSource = self
delegate = self
self.turnToPage(index: 0)
}
func turnToPage(index: Int)
{
let controller = controllers[index]
var direction = UIPageViewControllerNavigationDirection.forward
if let currentVC = viewControllers?.first
{
guard let currentIndex = controllers.index(of: currentVC) else {return}
if currentIndex > index
{
direction = .reverse
}
}
self.configuewDisplaying(viewController: controller)
setViewControllers([controller], direction: direction, animated: true, completion: nil)
}
func configuewDisplaying(viewController: UIViewController)
{
for (index, vc) in controllers.enumerated()
{
if viewController === vc {
if let productImageVC = viewController as? ProductImageVC
{
productImageVC.imageLink = self.product.imageLinks?[index]
self.pageViewControllerDelegate?.turnPageController(to: index)
}
}
}
}
}
extension ProductImagesPageVC: UIPageViewControllerDataSource
{
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if let index = controllers.index(of: viewController)
{
if index < controllers.count - 1
{
return controllers[index + 1]
}
}
return controllers.first
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if let index = controllers.index(of: viewController)
{
if index > 0
{
return controllers[index - 1]
}
}
return controllers.last
}
}
extension ProductImagesPageVC: UIPageViewControllerDelegate
{
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
self.configuewDisplaying(viewController: pendingViewControllers.first as! ProductImageVC)
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if !completed
{
self.configuewDisplaying(viewController: previousViewControllers.first as! ProductImageVC)
}
}
}
Try this-
let viewControllers: [UIViewControllers] = [UIViewController()]
if let pageViewController = parentViewController as? UIPageViewController {
pageViewController.setViewControllers(viewControllers, direction: .Forward, animated: true, completion: nil)}
I want to create UIPageViewController with spine mid location. I read several tutorials with UIPageViewController but in this tutorials used spine min or max location. And I can not create UIPageViewController with spine mid location.
I have this function to create UIPageViewController with spine mid location min or max location:
func createPageViewController() {
// Instantiate the PageViewController
let pageController = self.storyboard?.instantiateViewController(withIdentifier: "PageViewController") as! UIPageViewController
pageController.dataSource = self
pageController.delegate = self
if images.count > 0{
let contentController = getContentViewController(withIndex: 0)!
let contentControllers = [contentController]
pageController.setViewControllers(contentControllers, direction: UIPageViewControllerNavigationDirection.forward, animated: true, completion: nil)
}
pageViewController = pageController
self.addChildViewController(pageViewController!)
self.view.addSubview(pageViewController!.view)
pageViewController!.didMove(toParentViewController: self)
}
I tried to change it for this:
let contentController = getContentViewController(withIndex: 0)!
let contentController1 = getContentViewController(withIndex: 1)!
let contentControllers = [contentController, contentController1]
But in this case my images not showing in pages. It is not help. What am I doing wrong? How to create UIPageViewController with spine mid location?
Update
import UIKit
class PageViewController: UIViewController {
#IBOutlet weak var PageControl: UIPageControl!
var pageViewController: UIPageViewController?
var images = ["book1page1.png","book1","book1","book1page2.png","book1page1.png","book1page2.png"]
var pendingIndex: Int?
override func viewDidLoad() {
super.viewDidLoad()
createPageViewController()
setupPageControll()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func createPageViewController() {
// Instantiate the PageViewController
let pageController = self.storyboard?.instantiateViewController(withIdentifier: "PageViewController") as! UIPageViewController
pageController.dataSource = self
pageController.delegate = self
if images.count > 0{
let firstController = getContentViewController(withIndex: 0)!
let contentControllers = [firstController]
pageController.setViewControllers(contentControllers, direction: UIPageViewControllerNavigationDirection.forward, animated: true, completion: nil)
}
pageViewController = pageController
self.addChildViewController(pageViewController!)
//self.view.addSubview(pageViewController!.view)
self.view.insertSubview(pageViewController!.view, at: 0)
pageViewController!.didMove(toParentViewController: self)
}
//Setup Pagination Icons and count
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return images.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
return 0
}
func setupPageControll(){
let apperance = UIPageControl.appearance()
apperance.pageIndicatorTintColor = UIColor.gray
apperance.currentPageIndicatorTintColor = UIColor.white
apperance.backgroundColor = UIColor.clear
}
func currentControllerIndex() -> Int{
let pageItemController = self.currentConroller()
if let controller = pageItemController as? ContentViewController {
return controller.itemIndex
}
return -1
}
///////////////////////////////////////////////
func currentConroller() -> UIViewController?{
if (self.pageViewController?.viewControllers?.count)! > 0{
return self.pageViewController?.viewControllers![0]
}
return nil
}
func getContentViewController(withIndex index: Int) -> ContentViewController? {
if index < images.count{
let contentVC = self.storyboard?.instantiateViewController(withIdentifier: "ContentViewController") as! ContentViewController
contentVC.itemIndex = index
contentVC.imageName = images[index]
return contentVC
}
return nil
}
}
extension PageViewController: UIPageViewControllerDataSource, UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
pendingIndex = (pendingViewControllers.first as! ContentViewController).itemIndex
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed {
let currentIndex = pendingIndex
if let index = currentIndex {
self.PageControl.currentPage = index
}
}
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let contentVC = viewController as! ContentViewController
if contentVC.itemIndex > 0 {
return getContentViewController(withIndex: contentVC.itemIndex - 1)
}
return nil
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let contentVC = viewController as! ContentViewController
if contentVC.itemIndex + 1 < images.count {
return getContentViewController(withIndex: contentVC.itemIndex + 1)
}
return nil
}
}
Select your PageViewController on story board and set. Spine Location none to Mid. Also Check double sided.
Modify your code as below. (Changes done on your own code)
import UIKit
class PageViewController: UIViewController {
#IBOutlet weak var PageControl: UIPageControl!
var pageViewController: UIPageViewController?
var images = ["01","02","03","04"]
var pendingIndex: Int?
override func viewDidLoad() {
super.viewDidLoad()
createPageViewController()
setupPageControll()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func createPageViewController() {
let pageController = self.storyboard?.instantiateViewController(withIdentifier: "PageViewController") as! UIPageViewController
pageController.isDoubleSided = true
pageController.dataSource = self
pageController.delegate = self
if images.count > 1{
let firstController = getContentViewController(withIndex: 0)!
let secondController = getContentViewController(withIndex: 1)!
let contentControllers = [firstController,secondController]
pageController.setViewControllers(contentControllers, direction: UIPageViewControllerNavigationDirection.forward, animated: true, completion: nil)
}
pageViewController = pageController
self.addChildViewController(pageViewController!)
self.view.insertSubview(pageViewController!.view, at: 0)
pageViewController!.didMove(toParentViewController: self)
}
//Setup Pagination Icons and count
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return images.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
return 0
}
func setupPageControll(){
let apperance = UIPageControl.appearance()
apperance.pageIndicatorTintColor = UIColor.gray
apperance.currentPageIndicatorTintColor = UIColor.white
apperance.backgroundColor = UIColor.clear
}
func currentControllerIndex() -> Int{
let pageItemController = self.currentConroller()
if let controller = pageItemController as? ContentView {
return controller.itemIndex
}
return -1
}
///////////////////////////////////////////////
func currentConroller() -> UIViewController?{
if (self.pageViewController?.viewControllers?.count)! > 0{
return self.pageViewController?.viewControllers![0]
}
return nil
}
func getContentViewController(withIndex index: Int) -> ContentView? {
if index < images.count{
let contentVC = self.storyboard?.instantiateViewController(withIdentifier: "ContentViewController") as! ContentView
contentVC.itemIndex = index
//contentVC.imageName.image = self.images[index]
return contentVC
}
return nil
}
}
extension PageViewController: UIPageViewControllerDataSource, UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
pendingIndex = (pendingViewControllers.first as! ContentView).itemIndex
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let contentVC = viewController as! ContentView
if contentVC.itemIndex > 0 {
return getContentViewController(withIndex: contentVC.itemIndex - 1)
}
return nil
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let contentVC = viewController as! ContentView
if contentVC.itemIndex + 1 < images.count {
return getContentViewController(withIndex: contentVC.itemIndex + 1)
}
return nil
}
}
Github Full Project.
I am trying to auto-slide the page view controller pages and the page indicator simultaneously. There are 5 pages in my page view controller. The flow works fine from pic 1-5, but I am unable to go back from 5th to 1st image and restart the auto sliding.
Below is my code:
import UIKit
class PageSliderViewController: UIViewController , UIPageViewControllerDataSource, UIPageViewControllerDelegate {
#IBOutlet weak var pageControl: UIPageControl!
var pageContainer: UIPageViewController!
// The pages it contains
var pages = [UIViewController]()
// Track the current index
var currentIndex: Int?
private var pendingIndex: Int?
override func viewDidLoad() {
super.viewDidLoad()
// Setup the pages
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let page1: UIViewController! = storyboard.instantiateViewController(withIdentifier: "Page1")
let page2: UIViewController! = storyboard.instantiateViewController(withIdentifier: "Page2")
let page3: UIViewController! = storyboard.instantiateViewController(withIdentifier: "Page3")
let page4: UIViewController! = storyboard.instantiateViewController(withIdentifier: "Page4")
let page5: UIViewController! = storyboard.instantiateViewController(withIdentifier: "Page5")
pages.append(page1)
pages.append(page2)
pages.append(page3)
pages.append(page4)
pages.append(page5)
// Create the page container
pageContainer = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
pageContainer.delegate = self
pageContainer.dataSource = self
pageContainer.setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil)
currentIndex = 0
// Add it to the view
view.addSubview(pageContainer.view)
// Configure our custom pageControl
self.view.bringSubview(toFront: self.loginButton)
self.view.bringSubview(toFront: self.signUpButton)
self.view.bringSubview(toFront: self.pageControl)
pageControl.numberOfPages = pages.count
pageControl.currentPage = 0
Timer.scheduledTimer(timeInterval: 5,
target: self,
selector: #selector(self.next(_:)),
userInfo: nil,
repeats: true)
}
func next(_ timer: Timer) {
pageContainer.goToNextPage()
currentIndex = currentIndex! + 1
if currentIndex == pageControl.numberOfPages{
currentIndex = 0
}
pageControl.currentPage = currentIndex!
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - UIPageViewController delegates
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
if currentIndex == 0 {
return nil
}
let previousIndex = abs((currentIndex - 1) % pages.count)
return pages[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
if currentIndex == pages.count-1 {
return nil
}
let nextIndex = abs((currentIndex + 1) % pages.count)
return pages[nextIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
pendingIndex = pages.index(of: pendingViewControllers.first!)
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed {
currentIndex = pendingIndex
if let index = currentIndex {
pageControl.currentPage = index
}
}
}
}
extension UIPageViewController {
func goToNextPage(animated: Bool = true, completion: ((Bool) -> Void)? = nil) {
if let currentViewController = viewControllers?[0] {
if let nextPage = dataSource?.pageViewController(self, viewControllerAfter: currentViewController) {
setViewControllers([nextPage], direction: .forward, animated: animated, completion: completion)
}
}
}
}
Note that through this code the page control goes from 5th to 1st dot but the page is not changing. There is some minor mistake but unable to figure it out. Any help would be greatly appreciated!
I haven't tried to run this code, but my guess is:
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
if currentIndex == pages.count-1 {
return nil
}
let nextIndex = abs((currentIndex + 1) % pages.count)
return pages[nextIndex]
}
Arrays are zero-based, so if you are currently looking at page5, you are looking at pages[4] ... and pages.count - 1 equals 4. So, your code is saying:
if the current page is page5
return nil
So your func goToNextPage() extension calls for viewControllerAfter and gets nil in return.
You most likely want to change viewControllerAfter to be:
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
// you don't need this, because you are using % modulo operator
// to keep "nextIndex" within the array bounds
//if currentIndex == pages.count-1 {
// return nil
//}
let nextIndex = abs((currentIndex + 1) % pages.count)
return pages[nextIndex]
}
I want to get current index of a pageViewController, I don't know how I get the visible pages index.
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool,previousViewControllers: [UIViewController],transitionCompleted completed: Bool)
{
// How to get it?
}
You can use didFinishAnimating, and set tags to viewcontrollers. try this
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool)
{
if (!completed)
{
return
}
self.pageControl.currentPageIndex = pageViewController.viewControllers!.first!.view.tag //Page Index
}
Add this code to your UIPageViewController.
var pages = [UIViewController]()
var currentIndex: Int {
guard let vc = viewControllers?.first else { return 0 }
return pages.firstIndex(of: vc) ?? 0
}
In Swift 3
Override didFinishAnimating function of UIPageViewControllerDelegate like this:
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed {
if let currentViewController = pageViewController.viewControllers![0] as? WalkthroughContentViewController {
pageControl.currentPage = currentViewController.index
}
}
}
where WalkthroughContentViewController is the UIViewController presented by UIPageViewController
Remember to keep an index variable inside the WalkthroughContentViewController. Also, in the viewDidLoad method set:
delegate = self
Swift 3: full programmatic PageViewController example to get/set the current page index without view tagging:
enum PageViewType:String {
case green = "greenView"
case blue = "blueView"
case red = "redView"
}
class MyPageViewController: UIPageViewController {
private (set) lazy var orderedViewControllers:[UIViewController] = {
return [self.newPageView(.green),
self.newPageView(.blue),
self.newPageView(.red)
]
}
var currentIndex:Int {
get {
return orderedViewControllers.index(of: self.viewControllers!.first!)!
}
set {
guard newValue >= 0,
newValue < orderedViewControllers.count else {
return
}
let vc = orderedViewControllers[newValue]
let direction:UIPageViewControllerNavigationDirection = newValue > currentIndex ? .forward : .reverse
self.setViewControllers([vc], direction: direction, animated: true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .forward,
animated: true,
completion: nil)
}
}
private func newPageView(_ viewType:PageViewType) -> UIViewController {
let vc = self.storyboard?.instantiateViewController(withIdentifier: viewType.rawValue)
return vc
}
}
UIPageViewController DataSource implementation:
extension MyPageViewController:UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let previousIndex = currentIndex - 1
guard previousIndex >= 0 else {
return nil
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
return orderedViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let nextIndex = currentIndex + 1
guard orderedViewControllers.count != nextIndex else {
return nil
}
guard orderedViewControllers.count > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return orderedViewControllers.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return currentIndex
}
}
First, have your UIPageViewController implement the UIPageViewControllerDataSource method presentationIndex(for pageViewController: UIPageViewController) -> Int to return the index for each of the PageViewController's ViewControllers.
Then in your delegate, access the datasource through the passed-in PageViewController:
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard let dataSource = pageViewController.dataSource,
let currentIndex = dataSource.presentationIndex?(for: pageViewController) else { preconditionFailure() }
}
Dont forget to set pageviewcontroller's delegate.
func createPageViewController() {
// Create page view controller
pageViewController = storyboard?.instantiateViewController(withIdentifier: "PageViewController") as? UIPageViewController
pageViewController?.delegate = self
pageViewController?.dataSource = self
let startingViewController: ChildViewController = viewControllerAtIndex(index: 0)!
let viewControllers: Array = [startingViewController]
pageViewController?.setViewControllers(viewControllers, direction: .forward, animated: true, completion: nil)
self.addChildViewController(pageViewController!)
self.view.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: self.view.bounds.size.height)
self.view.addSubview((pageViewController?.view)!)
self.pageViewController?.didMove(toParentViewController: self)
}
func viewControllerAtIndex(index: Int) -> ChildViewController? {
// return nil here, if there won't be any page in pageviewcontroller
// Create a new view controller and pass suitable data.
let pageContentViewController: ChildViewController = storyboard?.instantiateViewController(withIdentifier: "ChildViewController") as! ChildViewController
pageContentViewController.pageIndex = index
return pageContentViewController
}
//Also add viewControllerAfter and viewControllerBefore methods
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
self.pendingIndex = (pendingViewControllers.first as! ChildViewController).pageIndex
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed {
self.currentIndex = self.pendingIndex!
//Perform your task here
}
}
You can use next method:
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard
completed,
let viewControllerIndex = tutorialViews.index(of: pageViewController.viewControllers!.first!) else
return
}
self.currentIndex = viewControllerIndex
}
Where tutorialViews is an array of pages (ViewControllers).
And initialize currentIndex like that:
var currentIndex = 0 {
didSet {
self.updateContentForPage(withIndex: currentIndex)
}
}
Try it..
func pageViewController(pageViewController: UIPageViewController,didFinishAnimating finished: Bool,previousViewControllers: [UIViewController],transitionCompleted completed: Bool){
guard completed else { return }
self.pageControl.currentPage = pageViewController.viewControllers!.first!.view.tag
}
Swift 4 version
Extending the ViewController class with these protocols (UIPageViewControllerDelegate, UIPageViewControllerDataSource) and adding the following functions helped me to make my page control work correctly.
class ViewController : UIPageViewControllerDelegate,UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
if let viewController = pendingViewControllers[0] as? DataViewController {
self.lastPendingViewControllerIndex = viewController.index!
}
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed {
pageControl.currentPage = self.lastPendingViewControllerIndex
if lastPendingViewControllerIndex == 4 {
self.getstartedButton.isHidden=false
} else {
self.getstartedButton.isHidden=true
}
}
}
}
Create a method within, for example, class -> WalkthroughPageViewController), such that:
/* The method takes in a page index and creates the next content view controller. If the controller can
be created, we call the built-in setViewControllers method and navigate to the next view controller.*/
func forward(index: Int) {
if let nextViewController = contentViewController(at: index + 1) {
setViewControllers([nextViewController], direction: .forward, animated: true, completion: nil)
}
}
And in the class that controls said UIPageController, which will be a controller view (class -> WalkthroughContentViewController) and that contains a following button that passes to the next page and updates the property "pageControl.currentPage" (which is itself the UIpageControl), implements:
class WalkthroughContentViewController: UIViewController {
#IBOutlet var headingLabel: UILabel!
#IBOutlet var contentLabel: UILabel!
#IBOutlet var forwardButton: UIButton!
#IBOutlet var pageControl: UIPageControl!
#IBOutlet var contentImageView: UIImageView!
var index = 0
var heading = ""
var content = ""
var imageFile = ""
override func viewDidLoad() {
super.viewDidLoad()
headingLabel.text = heading
contentLabel.text = content
contentImageView.image = UIImage(named: imageFile)
// Update the 'currentPage' property of the page control.
pageControl.currentPage = index
if case 0...1 = index {
forwardButton.setTitle("NEXT", for : .normal)
} else if case 2 = index {
forwardButton.setTitle("DONE", for : .normal)
}
}
// MARK: - Actions
#IBAction func nextButtonTapped(sender: UIButton) {
switch index {
case 0...1: /* Get the parent controller & call to the "forward" method from the 'WalkthroughPageViewController'. */
let pageViewController = parent as! WalkthroughPageViewController
pageViewController.forward(index: index)
case 2: /* Dismiss the page view controller and show the main screen of the app*/
dismiss(animated: true, completion: nil)
default: break
}
}
}
Just check apple's docs (https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit) -> Section 3 -> Step 5
You can get the current index like this
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed,
let visibleViewController = pageViewController.viewControllers?.first,
let index = parent.controllers.firstIndex(of: visibleViewController)
{
parent.currentPage = index
}
}
or if you are keeping viewControllers in an array you can do this
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed,
let visibleViewController = pageViewController.viewControllers?.first,
let index = orderedViewControllers.firstIndex(of: visibleViewController)
{
statisticsPageDelegate?.statisticsPageViewController(statisticsPageViewController: self, didUpdatePageIndex: index)
}
}
This is the full code
class StatisticsPageViewController: UIPageViewController,UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var currentIndex: Int?
var statisticsPageDelegate: StatisticsPageViewControllerDelegate?
private var pendingIndex: Int?
required init?(coder aDecoder: NSCoder) {
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
delegate = self
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .forward,
animated: true,
completion: nil)
}
statisticsPageDelegate?.statisticsPageViewController(
statisticsPageViewController: self,
didUpdatePageCount: orderedViewControllers.count
)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
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
}
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
}
return orderedViewControllers[nextIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed,
let visibleViewController = pageViewController.viewControllers?.first,
let index = orderedViewControllers.firstIndex(of: visibleViewController)
{
statisticsPageDelegate?.statisticsPageViewController(statisticsPageViewController: self, didUpdatePageIndex: index)
}
}
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.newStatisticsViewController(identifier: Identifiers.PROGRAM_VIEW_CONTROLLER),
self.newStatisticsViewController(identifier: Identifiers.LOGIN_VIEW_CONTROLLER)]
}()
private func newStatisticsViewController(identifier: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewController(withIdentifier: identifier)
}
}
protocol StatisticsPageViewControllerDelegate: class {
func statisticsPageViewController(statisticsPageViewController:
StatisticsPageViewController, didUpdatePageCount count: Int)
func statisticsPageViewController(statisticsPageViewController:
StatisticsPageViewController, didUpdatePageIndex index: Int)
}
Use viewDidAppear that mark page as visible and viewDidDisappear that mark page as invisible
Item ViewController:
class PageItemViewController: UIViewController {
private(set) var isVisible: Bool = false
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
isVisible = true
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
isVisible = false
}
}
Get index of the visible page
class PageViewController: UIPageViewController, UIPageViewControllerDelegate {
var items: [PageItemViewController] = [] {
didSet {
if let last = items.last {
setViewControllers([last], direction: .forward, animated: true, completion: nil)
}
}
}
// ...
public func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if finished {
guard let currentIndex = items.firstIndex(where: { $0.isVisible }) else { return }
print(currentIndex)
}
}
}