I have correctly set up a UIPageViewController.
I would like to check the itemIndex of each views and display content accordingly. Here's what the code looks like:
var itemIndex: Int = 0 {
didSet {
if itemIndex == 1 {
println("Character is Lela!")
}
if itemIndex == 0 {
println("Character is John!")
}
}
}
What I tried
I run the app
Console Output: "Character is John!"
I swipe forward (to the right)
Console Output: "Character is Lela!"
I swipe back (to the left)
There is no output!
I swipe forward
There is no output! Again!
What is this due to? Here's the full code:
import UIKit
var currentIndex: Int = 0
var nextIndex: Int = 0
class ProView: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pageViewController: UIPageViewController?
let characterImages = ["character1", "character2", "character1", "character2", "character1", "character2", "character1", "character2"]
override func viewDidLoad() {
super.viewDidLoad()
createPageViewController()
setupPageControl()
character = 1
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// Forward, check if this IS NOT the last controller
func pageViewController(pageViewController: UIPageViewController,
viewControllerAfterViewController ProView: UIViewController) -> UIViewController? {
let itemController = ProView as PageItemController
// Check if there is another view
if itemController.itemIndex+1 < characterImages.count {
return getItemController(itemController.itemIndex+1)
}
return nil
}
// Check if this IS NOT the first controller
func pageViewController(pageViewController: UIPageViewController,
viewControllerBeforeViewController ProView: UIViewController) -> UIViewController? {
let itemController = ProView as PageItemController
if itemController.itemIndex < 0 {
return getItemController(itemController.itemIndex-1)
}
return nil
}
private func getItemController(itemIndex: Int) -> PageItemController? {
if itemIndex < characterImages.count {
let pageItemController = self.storyboard!.instantiateViewControllerWithIdentifier("ItemController") as PageItemController
pageItemController.itemIndex = itemIndex
pageItemController.imageName = characterImages[itemIndex]
return pageItemController
}
return nil
}
func createPageViewController() {
let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("PageController") as UIPageViewController
pageController.dataSource = self
pageController.delegate = self
if characterImages.count > 0 {
let firstController = getItemController(0)!
let startingViewControllers: NSArray = [firstController]
pageController.setViewControllers(startingViewControllers, direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
pageViewController = pageController
addChildViewController(pageViewController!)
self.view.addSubview(pageViewController!.view)
pageViewController?.didMoveToParentViewController(self)
}
func setupPageControl() {
let appearance = UIPageControl.appearance()
appearance.pageIndicatorTintColor = UIColor.grayColor()
appearance.currentPageIndicatorTintColor = UIColor.whiteColor()
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return characterImages.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
// BETA
func pageViewController(PageItemController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers pageViewController: [AnyObject],
transitionCompleted completed: Bool)
{
if (!completed)
{
// You do nothing because whatever page you thought
// the book was on before the gesture started is still the correct page
return;
}
// This is where you would know the page number changed and handle it appropriately
}
}
class PageItemController: UIViewController {
#IBOutlet weak var imageCharacterChoose: UIImageView!
var itemIndex: Int = 0 {
didSet {
if itemIndex == 1 {
println("Character is Lela!")
}
if itemIndex == 0 {
println("Character is John!")
}
}}
var imageName: String = "" {
didSet {
if let imageView = imageCharacterChoose {imageCharacterChoose.image = UIImage(named: imageName)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
imageCharacterChoose!.image = UIImage(named: imageName)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
didSet and willSet will only get called when you change the value of the corresponding variable, so in your case you should somewhere in your code do itemIndex = 1, or some other value to trigger the functions.
Related
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] =
I have 2 UIViewControllers as items of UIPageViewController. It works well for slide, but i want to move from pageOne to pageTwo with button's action. How i can do that?
Here is my code and storyboard preview:
Contents of Pager.swift :
import UIKit
class Pager: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource, UIScrollViewDelegate
{
var viewsArray: [UIViewController] = []
var pageOne: UIViewController = UIViewController()
var pageTwo: UIViewController = UIViewController()
var currentIndex = 0
var previousIndex = 0
var nextIndex = 0
override func viewDidLoad()
{
super.viewDidLoad()
self.delegate = self
self.dataSource = self
pageOne = (storyboard?.instantiateViewController(withIdentifier: "pageOne"))!
pageTwo = (storyboard?.instantiateViewController(withIdentifier: "pageTwo"))!
viewsArray.append(pageOne)
viewsArray.append(pageTwo)
setViewControllers([pageOne], direction: .forward, animated: true, completion: nil)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?
{
currentIndex = viewsArray.index(of: viewController)!
if currentIndex == 1
{
return nil
}
else
{
nextIndex = abs((currentIndex + 1) % viewsArray.count)
return viewsArray[nextIndex]
}
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController?
{
currentIndex = viewsArray.index(of: viewController)!
if currentIndex == 0
{
return nil
}
else
{
previousIndex = abs((currentIndex - 1) % viewsArray.count)
return viewsArray[previousIndex]
}
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
{
return viewsArray.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
{
return 0
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
}
Contents of PageOne.swift :
import UIKit
class PageOne: UIViewController
{
var pager = Pager()
override func viewDidLoad()
{
super.viewDidLoad()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
#IBAction func forwardButton(_ sender: Any)
{
pager.setViewControllers([pager.pageOne], direction: .forward, animated: true, completion: nil)
}
}
You need to write below line
pageController?.setViewControllers([YOUR_VIEW_CONTROLLER], direction: direction, animated: true, completion: nil)
give proper direction either forward or reverse based on which controller you are at
hope this will help you
You need to pass pager to your both viewcontrollers
pageOne.pager = self
pageTwo.pager = self
How can I get the 'real' current index of the active page? I have tried every solution on stack overflow with no luck. So vc.pageIndex gives me the wrong value back. How can I solve this?
My code:
import UIKit
class PageControl: BasePageViewController, UIPageViewControllerDataSource {
var pageViewController: UIPageViewController!
var pageTitles: [[String]]!
var pageImages: NSArray!
var complete : CGFloat = 0.0;
override func viewDidLoad(){
super.viewDidLoad()
HealthKit().recentSteps() { steps, error in
self.pageTitles = steps;
self.pageViewController = self
self.pageViewController.dataSource = self
let startVC = self.viewControllerAtIndex(0) as StepsDetailViewController
let viewControllers = NSArray(object: startVC)
dispatch_async(dispatch_get_main_queue(), {
self.pageViewController.setViewControllers(viewControllers as? [UIViewController], direction: .Forward, animated: true, completion: nil)
})
}
}
override func percentCompleteDidChange() {
if let percentComplete = self.percentComplete {
complete = percentComplete
let vc: StepsDetailViewController = self.storyboard?.instantiateViewControllerWithIdentifier("StepsDetailViewController") as! StepsDetailViewController
vc.updateProgressComplete(percentComplete, index: 0)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func viewControllerAtIndex(index: Int) -> StepsDetailViewController
{
if ((self.pageTitles.count == 0) || (index >= self.pageTitles.count)) {
return StepsDetailViewController()
}
let vc: StepsDetailViewController = self.storyboard?.instantiateViewControllerWithIdentifier("StepsDetailViewController") as! StepsDetailViewController
vc.titleText = self.pageTitles[index][0]
vc.stepData = self.pageTitles[index][1]
vc.pageIndex = index
return vc
}
// MARK: - Page View Controller Data Source
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
{
let vc = viewController as! StepsDetailViewController
var index = vc.pageIndex as Int
if (index == 0 || index == NSNotFound)
{
return nil
}
index -= 1
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let vc = viewController as! StepsDetailViewController
var index = vc.pageIndex as Int
if (index == NSNotFound){
return nil
}
index += 1
if (index == self.pageTitles.count)
{
return nil
}
return self.viewControllerAtIndex(index)
}
}
I have this view controller with function: updateProgressComplete. But I can't add values to the view controller where this function is in.
My view controller:
import UIKit
class StepsDetailViewController: UIViewController {
var pageIndex: Int! = 0
var titleText: String!
var stepData: String!
var pageControl: PageControl?
let screenSize: CGRect = UIScreen.mainScreen().bounds
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var stepsLabel: UILabel!
var g : ShapeView?
override func viewDidLoad(){
super.viewDidLoad()
self.dateLabel.text = self.titleText
self.stepsLabel.text = self.stepData
g = ShapeView(origin: CGPoint(x: UIScreen.mainScreen().bounds.width / 2, y: UIScreen.mainScreen().bounds.height / 2))
self.view.addSubview(g!)
}
func updateProgressComplete(newValue : CGFloat, view: StepsDetailViewController) {
self.dateLabel.text = "hello" <-- this doesn't work
}
}
I think this isn't working because of updateProgressComplete comes from another class:
import UIKit
class PageControl: BasePageViewController, UIPageViewControllerDataSource {
var pageViewController: UIPageViewController!
var pageTitles: [[String]]!
var complete : CGFloat = 0.0;
var count = 0
override func viewDidLoad(){
super.viewDidLoad()
HealthKit().recentSteps() { steps, error in
self.pageTitles = steps;
self.count = self.pageTitles.count
self.count -= 1
self.pageViewController = self
self.pageViewController.dataSource = self
let startVC = self.viewControllerAtIndex(self.count) as StepsDetailViewController
let viewControllers = NSArray(object: startVC)
print(viewControllers)
dispatch_async(dispatch_get_main_queue(), {
self.pageViewController.setViewControllers(viewControllers as? [UIViewController], direction: .Forward, animated: true, completion: nil)
})
}
}
override func percentCompleteDidChange() {
if let percentComplete = self.percentComplete {
complete = percentComplete
let vc: StepsDetailViewController = self.storyboard?.instantiateViewControllerWithIdentifier("StepsDetailViewController") as! StepsDetailViewController
vc.updateProgressComplete(percentComplete, view: vc)
}
}
func viewControllerAtIndex(index: Int) -> StepsDetailViewController
{
if ((self.pageTitles.count == 0) || (index >= self.pageTitles.count)) {
return StepsDetailViewController()
}
let vc: StepsDetailViewController = self.storyboard?.instantiateViewControllerWithIdentifier("StepsDetailViewController") as! StepsDetailViewController
vc.titleText = self.pageTitles[index][0]
vc.stepData = self.pageTitles[index][1]
vc.pageIndex = index
return vc
}
// MARK: - Page View Controller Data Source
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
{
let vc = viewController as! StepsDetailViewController
var index = vc.pageIndex as Int
if (index == 0 || index == NSNotFound)
{
return nil
}
index -= 1
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let vc = viewController as! StepsDetailViewController
var index = vc.pageIndex as Int
if (index == NSNotFound){
return nil
}
index += 1
if (index == self.pageTitles.count)
{
return nil
}
return self.viewControllerAtIndex(index)
}
}
How can I fix this? I think it has something to do that I use this: instantiateViewControllerWithIdentifier? The value at the updateProgressComplete changes all the time, that's why I use that function and don't pass it as variable.
You're right, instantiateViewControllerWithIdentifier is a problem. If creates a new object. ('new' as in different from the one you want to update.) However you create the PageControl, try defining a delegate for it and setting StepsDetailViewController as the delegate so that you have the correct object to update.
I want to retrieve the value from the scrollViewDidScroll function at my view controller. It gives the right value back in the console so that's nice.
My view controller:
import UIKit
extension UIPageViewController: UIScrollViewDelegate {
public override func viewDidLoad() {
super.viewDidLoad()
for subView in view.subviews {
if subView is UIScrollView {
(subView as! UIScrollView).delegate = self
}
}
}
public func scrollViewDidScroll(scrollView: UIScrollView) {
let point = scrollView.contentOffset
var percentComplete: CGFloat
percentComplete = fabs(point.x - view.frame.size.width)/view.frame.size.width
print("percentComplete: ", percentComplete)
}
}
class StepsDetailViewController: UIViewController {
var pageIndex: Int!
override func viewDidLoad()
{
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Where the view controller came from:
import UIKit
class PageControl: UIPageViewController, UIPageViewControllerDataSource {
var pageViewController: UIPageViewController!
var pageTitles: [[String]]!
override func viewDidLoad(){
super.viewDidLoad()
testC().getRecent() { result, error in
self.pageTitles = result;
self.pageViewController = self
self.pageViewController.dataSource = self
let startVC = self.viewControllerAtIndex(0) as StepsDetailViewController
dispatch_async(dispatch_get_main_queue(), {
let viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as? [UIViewController], direction: .Forward, animated: true, completion: nil)
})
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func viewControllerAtIndex(index: Int) -> StepsDetailViewController
{
if ((self.pageTitles.count == 0) || (index >= self.pageTitles.count)) {
return StepsDetailViewController()
}
let vc: StepsDetailViewController = self.storyboard?.instantiateViewControllerWithIdentifier("StepsDetailViewController") as! StepsDetailViewController
vc.pageIndex = index
return vc
}
// MARK: - Page View Controller Data Source
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
{
let vc = viewController as! StepsDetailViewController
var index = vc.pageIndex as Int
if (index == 0 || index == NSNotFound)
{
return nil
}
index -= 1
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let vc = viewController as! StepsDetailViewController
var index = vc.pageIndex as Int
if (index == NSNotFound)
{
return nil
}
index += 1
if (index == self.pageTitles.count)
{
return nil
}
return self.viewControllerAtIndex(index)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
{
return self.pageTitles.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
{
return 0
}
}
Is this possible?
You could do like this:
1. Replace your extension with a custom UIPageViewController
class BasePageViewController: UIPageViewController, UIScrollViewDelegate {
var percentComplete: CGFloat = 0.0 {
didSet { self.percentCompleteDidChange() }
}
func percentCompleteDidChange() {
print("percentCompleteDidChange")
}
public override func viewDidLoad() {
super.viewDidLoad()
for subView in view.subviews {
if subView is UIScrollView {
(subView as! UIScrollView).delegate = self
}
}
}
public func scrollViewDidScroll(scrollView: UIScrollView) {
let point = scrollView.contentOffset
self.percentComplete = fabs(point.x - view.frame.size.width)/view.frame.size.width
print("percentComplete: ", percentComplete)
}
}
2. PageControl is a BasePageViewController subclass
class PageControl: BasePageViewController, UIPageViewControllerDataSource {
// here you have the access to percentComplete of superclass
func printPercentComplete() {
if let percentComplete = self.percentComplete {
print(percentComplete)
}
}
override func percentCompleteDidChange() {
print("percentCompleteDidChange from child")
}
......
}
Define the percentComplete as a property of your custom class that inherits from UIPageViewController.
Then you can access it everywhere.