PageViewController - Auto Slide - Swift 3 - ios

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

Related

Assert Window should be nonnil (Swift Error)

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

Adjust containerView Height based on PageviewController

I'm kinda having a trouble adjusting the ContainerView height based on the height of the pageViewController.
So on this example I have two pages : "Yellow Page","Purple Page"
Yellow page = 120px Height
Purple page = 250px Height
All I wanna work out is to adjust the height automatically as I scroll on the pageViewController embedded on the containerView.
Hope you can help, thank you!
Here's the Image.
Storyboard Image
This is my PageViewController Code.
import UIKit
class PageViewController: UIPageViewController,UIPageViewControllerDataSource, UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
let previousIndex = abs((currentIndex - 1) % pages.count)
return pages[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
let nextIndex = abs((currentIndex + 1) % pages.count)
return pages[nextIndex]
}
var pages = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.dataSource = self
let page1: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "page1");
let page2: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "page2");
pages.append(page1)
pages.append(page2)
setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return pages.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
override func viewDidLayoutSubviews() {
}
}
You can hook the height of the container and controls it from the pager like this
#IBOutlet weak var conH: NSLayoutConstraint!
//
func callMe(value:CGFloat) {
conH.constant = value
self.view.layoutIfNeeded()
}
//
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
let vc = UIApplication.shared.keyWindow?.rootViewController as! ViewController
let currentIndex = pages.index(of: previousViewControllers.first!)!
let nextIndex = abs((currentIndex + 1) % pages.count)
vc.callMe(value:nextIndex == 0 ? 100 : 200)
}
//

swift: UIPageViewController action

I have UIPageViewController and this code to flip my pages :
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
}
But I want to flip pages on button press. I should call viewControllerBefore and viewControllerAfter in my button action? Or I should write another code? How to do it?
Update
override func viewDidLoad() {
super.viewDidLoad()
createPageViewController()
setupPageControll()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func createPageViewController() {
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.insertSubview(pageViewController!.view, at: 0)
pageViewController!.didMove(toParentViewController: self)
}
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
}
New Update
new:
#IBAction func nextAction(_ sender: Any) {
let pageController = self.storyboard?.instantiateViewController(withIdentifier: "PageViewController") as! UIPageViewController
pageController.dataSource = self
pageController.delegate = self
let secondController = getContentViewController(withIndex: 1)!
let contentControllers = [secondController]
pageController.setViewControllers(contentControllers, direction: .forward, animated: true, completion: nil)
pageViewController = pageController
}
You need to use this instance method
setViewControllers(_ viewControllers: [UIViewController]?,
direction: UIPageViewController.NavigationDirection,
animated: Bool,
completion: ((Bool) -> Void)? = nil)
Like this
// self is of type UIPageViewController
self.setViewControllers([vc],direction:.forward,animated:true) { _ in
}
#IBAction func nextPageBtnPressed_TouchupInside(_ sender : UIButton ) {
if let pageViewController = self.parent as? PageViewController {
pageViewController.goToNextPage()
pageViewController.changeCurrentPageIndicator(pageViewController: pageViewController)
}
}
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)
}
}
func changeCurrentPageIndicator(pageViewController : DTCPageViewController) {
pageViewController.view.clipsToBounds = false
pageViewController.view.sizeToFit()
let pageContentViewController = pageViewController.viewControllers![0]
currentIndex = orderViewController.index(of: pageContentViewController)!
self.pageControl.currentPage = orderViewController.index(of: pageContentViewController)!
}
write goToNextPage() and changeCurrentPageIndicator() ------
In PageViewControllerClass
Or make extension
extension PageViewController

transition page with uipageviewcontroller

I have a view container containing a UIPageViewController. to him they are associated with 2 viewController of UIImageView. It works almost everything. I can not change the animations to scroll through images. the animation is always that of the page curl while I would like a simple scroll. how to do?
class MyPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pages = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.dataSource = self
let page1: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "page1")
let page2: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "page2")
pages.append(page1)
pages.append(page2)
setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
let previousIndex = abs((currentIndex - 1) % pages.count)
return pages[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
let nextIndex = abs((currentIndex + 1) % pages.count)
return pages[nextIndex]
}
private func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return pages.count
}
private func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
How are you initiating the UIPageViewController?
From code, you can specify a transitionStyle in the constructor, as seen here
From storyboard, you can specify the same transitionStyle in the storyboard itself, as shown in this SO answer

PageViewController current page index in Swift

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

Resources