Pass variable from UIPageViewController to UIViewController child - swift

I am trying to create a tutorial / intro for my app and since at this point I am not sure how many pages will it have I decided to use a UIPageViewController with a single child.
My intention is to store all the data I wanna display in an object array and to use the index of the current displayed view inside my UIPageViewController to get the object I need from that array.
override func viewWillAppear(_ animated: Bool)
{
print("PageContentVC > viewWillAppear")
ivPageContent.image = UIImage(named: DataSingleton.sharedInstance.returnTutorialVCContent()[contentPage].contentImage!)
lblPageContentTitle.text = DataSingleton.sharedInstance.returnTutorialVCContent()[contentPage].contentLabel!
lblPageContentText.text = DataSingleton.sharedInstance.returnTutorialVCContent()[contentPage].contentText!
}
For this I need to pass that index from my UIPageViewController to its child, ContentPageVC.
I have no problem in getting the index but I am having difficulties passing it to ContentPageVC.
import UIKit
class TutorialPageVC : UIPageViewController,
UIPageViewControllerDataSource,
UIPageViewControllerDelegate
{
/*
// MARK: - Variables & Constants
*/
var contentPage : Int = 0
var collectionViewPageControl : UIPageControl = UIPageControl()
lazy var pagesArray : [UIViewController] =
{
return [self.VCInstance(name: "pageContent"),
self.VCInstance(name: "pageContent"),
self.VCInstance(name: "pageContent"),
self.VCInstance(name: "pageContent"),
self.VCInstance(name: "pageContent"),
self.VCInstance(name: "pageContent"),
self.VCInstance(name: "pageContent"),
self.VCInstance(name: "pageContent"),
self.VCInstance(name: "pageContent")]
}()
private func VCInstance(name: String) -> UIViewController
{
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name)
}
/*
// MARK: - VC Status
*/
override func viewDidLoad()
{
super.viewDidLoad()
print("TutorialPageVC > viewDidLoad")
self.dataSource = self
self.delegate = self
if let firstVC = pagesArray.first
{
setViewControllers([firstVC], direction: .forward, animated: true, completion: nil)
}
let pageControl = UIPageControl.appearance()
pageControl.currentPageIndicatorTintColor = UIColor.activeColor
pageControl.pageIndicatorTintColor = UIColor.backgroundColor
self.view.backgroundColor = UIColor.clear
}
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
print("TutorialPageVC > viewDidLayoutSubviews")
for view in self.view.subviews
{
if view is UIScrollView { view.frame = UIScreen.main.bounds }
}
}
/*
// MARK: - UIPageViewController Delegates
*/
public func pageViewController(_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController) -> UIViewController?
{
print("TutorialPageVC > pageViewController > viewControllerBefore")
guard let viewControllerIndex = pagesArray.firstIndex(of: viewController) else { return nil }
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else { return pagesArray.last }
guard pagesArray.count > previousIndex else { return nil }
return pagesArray[previousIndex]
}
public func pageViewController(_ pageViewController: UIPageViewController,
viewControllerAfter viewController: UIViewController) -> UIViewController?
{
print("TutorialPageVC > pageViewController > viewControllerAfter")
guard let viewControllerIndex = pagesArray.firstIndex(of: viewController) else { return nil }
let nextIndex = viewControllerIndex + 1
guard nextIndex < pagesArray.count else { return pagesArray.first }
guard pagesArray.count > nextIndex else { return nil }
return pagesArray[nextIndex]
}
public func presentationCount(for pageViewController: UIPageViewController) -> Int
{
print("TutorialPageVC > presentationCount")
return pagesArray.count
}
public func presentationIndex(for pageViewController: UIPageViewController) -> Int
{
print("TutorialPageVC > presentationIndex")
guard let firstViewController = viewControllers?.first,
let firstViewControllerIndex = pagesArray.firstIndex(of: firstViewController) else
{
return 0
}
return firstViewControllerIndex
}
public func pageViewController(_ pageViewController: UIPageViewController,
willTransitionTo pendingViewControllers: [UIViewController])
{
print("TutorialPageVC > pageViewController > willTransitionTo")
}
public func pageViewController(_ pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool)
{
print("TutorialPageVC > pageViewController > didFinishAnimating")
if completed
{
if let currentViewController = pageViewController.viewControllers?.first,
let index = pagesArray.firstIndex(of: currentViewController)
{
contentPage = index
print(contentPage)
}
}
}
}
I've been looking for some time now for a solution and I found these:
PageViewController pass data to child controller
Passing data between UIPageViewController Child views with Swift
PageViewController - Pass variables to child views
how to pass data from UIPageViewController to child ViewController using delegates
I know this is a common issue but unfortunately none of the solutions I found worked for me and I fail to see why and what I'm missing.
Any ideas?

Replace
return pagesArray[previousIndex]
with
let item = pagesArray[previousIndex]
item.index = previousIndex
return item
and so on for next
class VCInstance:UIViewController {
var index:Int!
}
Also
lazy var pagesArray : [UIViewController] =
with
lazy var pagesArray : [VCInstance] =

Related

UIPageViewController show the wrong page after adding third page

in my project I have a UIPageViewController that user can add new pages to it(it start with one page). every time user add new page UIPageViewController must show the current page(the user must be in the last page so he/she can add new page). after adding second page every things going fine but when user is in page2 and adding third page and scroll forward, UIPageViewController show the first page instead of showing the third page(for some reason I don't know, if user scroll backward everything working fine and UIPageViewController showing page1).
this is my codes to add new page:
class HomePageVC: UIPageViewController {
private var pages = [SomeVC]()
private var currentSomeVC: SomeVC!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
dataSource = self
delegate = self
let someVC = SomeVC()
pages.append(someVC)
if pages.count != 0 {
currentSomeVC = pages[0]
setViewControllers(pages, direction: .forward, animated: true, completion: nil)
}
if pages.count == 1 {
delegate = nil
dataSource = nil
}
}
func addNewPage() {
let someVC = SomeVC()
pages.append(someVC)
if delegate == nil, dataSource == nil {
delegate = self
dataSource = self
}
}
}
extension HomePageVC: UIPageViewControllerDelegate, UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pages.firstIndex(of: viewController as! SomeVC) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return pages.last
}
guard pages.count > previousIndex else {
return nil
}
return pages[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pages.firstIndex(of: viewController as! SomeVC) else {
return nil
}
let nextIndex = viewControllerIndex + 1
guard pages.count != nextIndex else {
return pages.first
}
guard pages.count > nextIndex else {
return nil
}
return pages[nextIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed {
guard let viewControllerIndex = pages.firstIndex(of: pageViewController.viewControllers?.first as! SomeVC), let viewController = pageViewController.viewControllers?.first as? SomeVC else {
return
}
currentSomeVC = viewController
}
}
}
Try replacing your addNewPage() method with the following:
func addNewPage() {
let someVC = SomeVC()
pages.append(someVC)
if delegate == nil, dataSource == nil {
delegate = self
dataSource = self
}
setViewControllers([pages[pages.count-1]], direction: .forward, animated: true, completion: nil)
}
You're adding new viewControllers to your pageViewController but not setting you pageViewController to newly created one.
Hope It'll work for you.
Reload UIPageViewController,
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if !completed { return }
DispatchQueue.main.async() {
self.dataSource = nil
self.dataSource = self
}
}

swift 4 UIPageViewControler

I Have Three ViewControllers
a, b , c I Want to initialized first viewController as (b) then swipe left to viewController(a) and swipe right for viewController(c)
how to make it by UIPageViewController
You will need to have 3 ViewControllers with identifiers Page1,Page2 & Page3 in the Main.storyboard.
import UIKit
class PageViewController: UIPageViewController
{
fileprivate lazy var pages: [UIViewController] = {
return [
self.getViewController(withIdentifier: "Page1"),
self.getViewController(withIdentifier: "Page2"),
self.getViewController(withIdentifier: "Page3")
]
}()
fileprivate func getViewController(withIdentifier identifier: String) -> UIViewController
{
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: identifier)
}
override func viewDidLoad()
{
super.viewDidLoad()
self.dataSource = self
self.delegate = self
if let firstVC = pages.first
{
setViewControllers([firstVC], direction: .forward, animated: true, completion: nil)
}
}
}
extension PageViewController: UIPageViewControllerDataSource
{
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = pages.index(of: viewController) else { return nil }
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else { return pages.last }
guard pages.count > previousIndex else { return nil }
return pages[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?
{
guard let viewControllerIndex = pages.index(of: viewController) else { return nil }
let nextIndex = viewControllerIndex + 1
guard nextIndex < pages.count else { return pages.first }
guard pages.count > nextIndex else { return nil }
return pages[nextIndex]
}
}
extension PageViewController: UIPageViewControllerDelegate { }

How to stop after view scroll in page controller at particular view

I integrated pageviewcontroller using storyboard. i have 4 view. how to stop after scroll at third view. how to disable after scoll?
This my code. how to stop after scroll in ThirdViewController.
import UIKit
class PageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
var pageControl = UIPageControl()
// MARK: UIPageViewControllerDataSource
lazy var orderedViewControllers: [UIViewController] = {
return [self.newVc(viewController: "sbRed"),self.newVc(viewController: "sbBlue"),self.newVc(viewController: "sbOrange"),self.newVc(viewController: "sbGray")]
}()
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
self.delegate = self
// 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)
}
configurePageControl()
// Do any additional setup after loading the view.
}
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 - 50,width: UIScreen.main.bounds.width,height: 50))
self.pageControl.numberOfPages = orderedViewControllers.count
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.black
self.pageControl.pageIndicatorTintColor = UIColor.white
self.pageControl.currentPageIndicatorTintColor = UIColor.black
self.view.addSubview(pageControl)
}
func newVc(viewController: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: viewController)
}
// MARK: Delegate methords
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
let pageContentViewController = pageViewController.viewControllers![0]
self.pageControl.currentPage = orderedViewControllers.index(of: pageContentViewController)!
}
// MARK: Data source functions.
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]
}
}
import UIKit
class ThirdViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You need to use UiPagerViewDatasource methods :
a) func pageViewController(UIPageViewController, viewControllerBefore: UIViewController)
b) func pageViewController(UIPageViewController, viewControllerAfter: UIViewController)
Example:
func pageViewController(UIPageViewController, viewControllerBefore: UIViewController){
if viewController == controllerA {
return controllerB;
}
else if viewController == controllerB {
return controllerC;
}
else ifviewController == controllerC {
return nil;
}}
and
func pageViewController(UIPageViewController, viewControllerAfter: UIViewController){
if (viewController == controllerC) {
return controllerB;
}
else if(viewController == controllerB) {
return controllerA;
}
else {
return nil;
}}
Sets the view controllers to be displayed you need to call :
pager.setViewControllers([selectingViewController]?, direction: UIPageViewControllerNavigationDirectionForward, animated: false, completion:nil)

have a UINavigationBar title for each page in PageViewController

I have set up my pageViewController (embedded in a NavigationController) with an array of ViewControllers to use as the pages (seen below):
`
class PageViewController: UIPageViewController {
var arrayIndex: Int = 0
var navigationBar = UINavigationBar()
var pageControl = UIPageControl.self
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .Forward,
animated: true,
completion: nil)
}
}
// set up & order all view controllers
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.newDayViewController("monday"),
self.newDayViewController("tuesday"),
self.newDayViewController("wednesday"),
self.newDayViewController("thursday")//,
self.newDayViewController("friday"),
self.newDayViewController("saturday"),
self.newDayViewController("sunday")
]
}()
// instantiate view controllers above that contain 'day' (easiest method to differentiate from other controllers)
private func newDayViewController(day: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewControllerWithIdentifier("\(day)")
}
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if (!completed) {
return
}
arrayIndex = Int(pageViewController.viewControllers!.first!.view.restorationIdentifier!)!
switch arrayIndex {
case 0:
navigationBar.topItem!.title = "Monday"
break
case 1:
navigationBar.topItem!.title = "Tuesday"
break
case 2:
navigationBar.topItem!.title = "Wednesday"
break
case 3:
navigationBar.topItem!.title = "Thursday"
default:
navigationBar.topItem!.title = "Timetable"
}
}
}
extension PageViewController: UIPageViewControllerDataSource {
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return orderedViewControllers.last
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
return orderedViewControllers[previousIndex]
}
func pageViewController(pageViewController: UIPageViewController,
viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count
//MARK: - determine which viewController the user is on
guard orderedViewControllersCount != nextIndex else {
return orderedViewControllers.first
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return orderedViewControllers.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first,
firstViewControllerIndex = orderedViewControllers.indexOf(firstViewController) else {
return 0
}
return firstViewControllerIndex
}
}
`
I am wondering how I could have it so that when the user is swiping through the pageViews, the UINavigationBar's title changes to the title of the view they are looking at ? e.g : When the user is looking at the mondayViewController, the title is monday and they can swipe to tuesday and the title will change etc.
Any suggestions are welcome
Thanks!
There are some errors in your code, first of all there isnt a UIPageViewControllerDelegate, so the delegates method will never be called.
Then, I dont know if your PageViewController really have a UINavigationController. To ensure that you can embed it by selecting your PageViewController and make the same thing as maded in picture:
so you can see in your storyboard both UINavigationController and UIPageViewController like this:
After you have all your day UIViewController created with each storyboard id, for example for monday:
you can modify you code like the code below:
import UIKit
class PageViewController: UIPageViewController,UIPageViewControllerDelegate {
var arrayIndex: Int = 0
var pageControl = UIPageControl.self
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.newDayViewController("monday"),
self.newDayViewController("tuesday"),
self.newDayViewController("wednesday"),
self.newDayViewController("thursday"),
self.newDayViewController("friday"),
self.newDayViewController("saturday"),
self.newDayViewController("sunday")
]
}()
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
delegate = self
if let firstViewController = orderedViewControllers.first {
self.navigationController!.navigationBar.topItem!.title = "monday"
setViewControllers([firstViewController],
direction: .Forward,
animated: true,
completion: nil)
}
}
private func newDayViewController(day: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewControllerWithIdentifier("\(day)")
}
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if (!completed) {
return
}
if let firstViewController = viewControllers?.first,
let arrayIndex = orderedViewControllers.indexOf(firstViewController) {
switch arrayIndex {
case 0:
self.navigationController!.navigationBar.topItem!.title = "Monday"
break
case 1:
self.navigationController!.navigationBar.topItem!.title = "Tuesday"
break
case 2:
self.navigationController!.navigationBar.topItem!.title = "Wednesday"
break
case 3:
self.navigationController!.navigationBar.topItem!.title = "Thursday"
default:
self.navigationController!.navigationBar.topItem!.title = "Timetable"
}
}
}
}
extension PageViewController: UIPageViewControllerDataSource {
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return orderedViewControllers.last
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
return orderedViewControllers[previousIndex]
}
func pageViewController(pageViewController: UIPageViewController,
viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count
//MARK: - determine which viewController the user is o
guard orderedViewControllersCount != nextIndex else {
return orderedViewControllers.first
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return orderedViewControllers.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first,
firstViewControllerIndex = orderedViewControllers.indexOf(firstViewController) else {
return 0
}
return firstViewControllerIndex
}
}

How to Enable scroll in PageViewController after disable delay in button highlighted in SWIFT 2.0

I just have trouble with button highlighted delay in UIPageViewController.
I can solve now by setting property "delaysContentTouches" = false to subview.
But now i can't scroll page anymore. i search forum and found "gestureRecognizers" which might help. but i don't know how to set.
could you help suggest me?
import UIKit
class PageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pages = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.dataSource = self
//declare all pages
let page1: UIViewController! = storyboard?.instantiateViewControllerWithIdentifier("c01")
let page2: UIViewController! = storyboard?.instantiateViewControllerWithIdentifier("c02")
let page3: UIViewController! = storyboard?.instantiateViewControllerWithIdentifier("c03")
pages.append(page1)
pages.append(page2)
pages.append(page3)
setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
override func viewDidAppear(animated: Bool) {
for view in self.view.subviews {
if view is UIScrollView {
(view as? UIScrollView)!.delaysContentTouches = false
}
}
}
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 pages.last
}
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 pages.first
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return pages[nextIndex]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I can do it now.
It is not possible for uipageviewcontroller.
i have to move to uiscrollviewcontroller
then subclass it and override function touchesShouldCancelInContentView for all my UIButton to be true.
now i can tap button without delay and can still hold my finger to scroll.