Is it possible to segue to a UIPageViewController? - ios

I am attempting to add a tutorial section to my application after the user logs in. When the app loads up, they will first come to a login screen and enter their credentials. Once they are verified, I want to segue to a UIPageViewController and show them two pages of instructions on how to use the application. However, all of my attempts at doing so have resulted in a black screen. Here is my current attempt:
import UIKit
class TutorialPageViewController: UIPageViewController {
var pages: [UIViewController] = []
override func viewDidLoad()
{
super.viewDidLoad()
self.dataSource = self
self.delegate = self
print("LOADING")
let firstView = storyboard?.instantiateViewController(withIdentifier: "TutorialPage1") as! TutorialPage1ViewController
let secondView = storyboard?.instantiateViewController(withIdentifier: "TutorialPage2") as! TutorialPage2ViewController
pages = [firstView, secondView]
let startingViewController = self.pages[0]
let viewControllers: [UIViewController] = [startingViewController]
self.setViewControllers(viewControllers, direction: UIPageViewControllerNavigationDirection.forward, animated: true, completion: {(done: Bool) in})
}
func viewControllerAtIndex(index:NSInteger) -> UIViewController {
return pages[index]
}
}
extension TutorialPageViewController: 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 TutorialPageViewController: UIPageViewControllerDelegate { }
The print statement in my viewDidLoad method is not being called, so I don't understand what I am doing wrong. I have created a push segue to TutorialPageViewController from the login view controller, but nothing ever loads.
Any help is appreciated.

Try checking the sample Project at Below GitHub Repo :
Link - https://github.com/RockinGarg/Stack_PageVC.git
Contains two Different Ways to Access
1. Making use of Container View and PageVC as SubView
2. Using Segue

Related

Change pages with UIPageViewController using buttons in my View Controllers swift 4

I have a UIPageViewController that let's me navigate between multiple view controller.
My app works swiping my screen, but I want to remove the swipe gesture to navigate between the pages for different buttons inside my views
This is the code that I'm using on with my page view cxontroller
class RootPageViewController: UIPageViewController,UIPageViewControllerDataSource {
lazy var viewControllerList: [UIViewController] = {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let VC1 = storyBoard.instantiateViewController(withIdentifier: "orangeVC")
let VC2 = storyBoard.instantiateViewController(withIdentifier: "blueVC")
let VC3 = storyBoard.instantiateViewController(withIdentifier: "purpleVC")
return [VC1, VC2, VC3]
}()
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
if let firstViewController = viewControllerList.first {
self.setViewControllers([firstViewController], direction: .forward, animated: true, completion: nil)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let vcIndex = viewControllerList.index(of: viewController) else {return nil}
let previousIndex = vcIndex-1
guard previousIndex >= 0 else {return nil}
guard viewControllerList.count > previousIndex else {return nil}
return viewControllerList[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let vcIndex = viewControllerList.index(of: viewController) else {return nil}
let nextIndex = vcIndex+1
guard viewControllerList.count != nextIndex else {return nil}
guard viewControllerList.count > nextIndex else {return nil}
return viewControllerList[nextIndex]
}
}
You only need this line inside the button action
self.setViewControllers([viewControllerList[indexHere]], direction: .forward, animated:false, completion: nil)

Perform segue from onboarding / tutorial screen

I am using the UIPageViewController for an on boarding / tutorial screen, and my problem is as follows:
When the user is on the last page of the tutorial, i want a left swipe gesture not to try and swipe the page controller, but instead to perform a segue to the registration page. I threw a print statement into the viewDidLoad in the UIViewController for the last page of the tutorial, and it registers.
In that same viewDidLoad I create my gestures to perform the segue, but nothing happens. I even tried putting the performSegue inside of the viewDidLoad, but nothing happened. I have my segue connected to the UIPageViewController as well as my GrowController, and neither register. I'm at a loss!
This is the UIViewController for the last page of tutorial
class GrowController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("*********************************")
createGesture()
// performSegue(withIdentifier: "ShowRegister", sender: self)
}
func createGesture() {
let showReg = UISwipeGestureRecognizer(target: self, action: #selector(showRegister))
showReg.direction = .left
view.addGestureRecognizer(showReg)
}
func showRegister(gesture: UISwipeGestureRecognizer) {
performSegue(withIdentifier: "showRegister", sender: self)
}
}
This is the UIPageViewController that handles the tutorial logic
class TutorialViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
var viewControllerIndex: Int?
lazy var tutorialArray: [UIViewController] = {
return [self.tutorialInstance(name: "page1"), self.tutorialInstance(name: "page2"), self.tutorialInstance(name: "page3")]
}()
private func tutorialInstance(name: String?) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name!)
}
override func viewDidLoad() {
super.viewDidLoad()
if let image = UIImage(named: "export.png") {
view.backgroundColor = UIColor.init(patternImage: image)
} else {
print("Error")
}
self.dataSource = self
self.delegate = self
if let firstViewController = tutorialArray.first {
setViewControllers([firstViewController], direction: .forward, animated: false, completion: nil)
}
}
These are the delgate and datasource functions
// Page View Controller delegate functions
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = tutorialArray.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return nil
}
guard tutorialArray.count > previousIndex else {
performSegue(withIdentifier: "ShowRegister", sender: self) // Added this line just testng around, nothing happend here though.
return nil
}
return tutorialArray[previousIndex]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = tutorialArray.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
guard nextIndex < tutorialArray.count else {
return nil
}
guard tutorialArray.count > nextIndex else {
return nil
}
return tutorialArray[nextIndex]
}
public func presentationCount(for pageViewController: UIPageViewController) -> Int {
return tutorialArray.count
}
public func presentationIndex(for pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first, let firstViewControllerIndex = tutorialArray.index(of: firstViewController) else {
return 0
}
return firstViewControllerIndex
}
Lots of code for what is probably a simple answer.
tl;dr - How can I make a swipe gesture perform a segue from a specific page of the UIPageViewController
There is a delegate method that you can implement pageViewController(_:willTransitionTo:) but I don't know that its worth the trouble. Maybe just put another view in front of it and put a swipe gesture recognizer view in that view and use the gesture recognized to manually change the page or segue to the new controller.

Change specific page UiPageViewController

I have a Page view controller (see below), what i want to achieve is to go to the FirstVC from ThirdVC and only from that!! with a function or a button, can anyone help me?
Here you can see the code of my PageVC
import UIKit
class PageVC: UIPageViewController,UIPageViewControllerDataSource, UIPageViewControllerDelegate{
lazy var VCArr: [UIViewController] = {
return [self.VCInstance(name: "FirstVC"), self.VCInstance(name: "SecondVC"), self.VCInstance(name: "ThirdVC")]
}()
private func VCInstance(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle:nil).instantiateViewController(withIdentifier: name)
}
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
self.delegate = self
if let firstVC = VCArr.first {
setViewControllers([firstVC], direction: .forward, animated: true, completion: nil)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for view in self.view.subviews {
if view is UIScrollView {
view.frame = UIScreen.main.bounds
} else if view is UIPageControl {
view.backgroundColor = UIColor.clear
}
}
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = VCArr.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else{
return VCArr.last
}
guard VCArr.count > previousIndex else {
return nil
}
return VCArr[previousIndex]
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = VCArr.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
guard nextIndex < VCArr.count else{
return VCArr.first
}
guard VCArr.count > nextIndex else {
return nil
}
return VCArr[nextIndex]
}
public func presentationCount(for pageViewController: UIPageViewController) -> Int {
return VCArr.count
}
public func presentationIndex(for pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first, let firstViewControllerIndex = VCArr.index(of: firstViewController) else{
return 0
}
return firstViewControllerIndex
}
}
Ok i found a solution here:
How do I change the UIPageViewController from WITHIN one of the UIViewControllers that is part of the UIPageViewController?
Simply i added this function to PageVC:
func nextPageWithIndex(index: Int)
{
let nextWalkthroughVC = VCArr[index]
setViewControllers([nextWalkthroughVC], direction: .forward, animated: true, completion: nil)
}
Then in the ThirdVC view controller i added in my function:
let parent = self.parent as! PageVC
parent.nextPageWithIndex(index: 0)
Where 0 is the index of the view controller that i want to show
And works perfectly!

UIPageViewContoller in container view with page control icon

I am writing a Swift app with three views. I have achieved this by putting a UIPageViewController inside a container and having that switch when you swipe, from another Stack Overflow article: UIPageViewController and storyboard. I would also like to have dots on the bottom indicating the page you are on. But, the UIPageControl I am using can only be placed on the view controller with the container, and the code with the current page is in the page controller's code. Here is my code:
class ContainerView : UIViewController {
#IBOutlet var myPageController: UIPageControl!
}
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: "Page1ID")
let page2: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "Page2ID")
let page3: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "Page3ID")
pages.append(page1)
pages.append(page2)
pages.append(page3)
setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
if (currentIndex == 0) {
return pages[currentIndex]
}
let previousIndex = abs((currentIndex - 1) % pages.count)
//myPageControl.currentPage = previousIndex
return pages[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
if (currentIndex == 2) {
return pages[currentIndex]
}
let nextIndex = abs((currentIndex + 1) % pages.count)
//myPageControl.currentPage = nextIndex
return pages[nextIndex]
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return pages.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
How could I access the parent view controller (ContainerView) from the child in the container (MyPageViewController)? I'm new to Swift/Objective-C but have programmed in other languages (C++, Python). If you need the storyboard, please ask.

PageViewController embedded in containerView shows small margin of previous view

I've created a viewController with a containerView in which I've embedded a pageViewController. In this pageController I've added 3 viewControllers inside. This seems to work fine, however every time I swipe to the next view it seem to show a little margin on the side of the previous view. What could cause this?
ViewDidLoad
if let firstViewController = orderedViewControllers.first {
self.pageViewController?.setViewControllers([firstViewController],
direction: .Forward,
animated: true,
completion: nil)
}
newViewController
func newViewController(view: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier(view)
}
PageControllerDelegate
extension CameraViewController: UIPageViewControllerDataSource {
func pageViewController(pageViewController: UIPageViewController,
viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.indexOf(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
}
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
// User is on the last view controller and swiped right to loop to
// the first view controller.
guard orderedViewControllersCount != nextIndex else {
return orderedViewControllers.first
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
}
You probably speak about page spacing. You can edit it in the storyboard. Select your Page View Controller, and open Attributes inspector. There will be corresponding section:

Resources