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:
Related
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)
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
I have a uipageviewcontroller with four individual view controllers in it. I want to disable the right swipe action for the first view controller and disable the left swipe action for the fourth view controller. When the app launches, I want the user to only be able to swipe left and not right, because swiping right, takes the user to the fourth view controller and swiping left on the fourth view controller takes the user to the first view controller. Here is my code.
class TutorialPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
weak var tutorialDelegate: TutorialPageViewControllerDelegate?
lazy var VCArr: [UIViewController] = {
return [self.VCInstance(name: "FirstVC"),
self.VCInstance(name: "SecondVC"),
self.VCInstance(name: "ThirdVC"),
self.VCInstance(name: "FourthVC")]
}()
private func VCInstance(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name)
}
override func viewDidLoad() {
super.viewDidLoad()
let pageControl = UIPageControl.appearance()
//Customizing
pageControl.pageIndicatorTintColor = UIColor.lightGray
pageControl.currentPageIndicatorTintColor = UIColor(red:0.23, green:0.73, blue:1.00, alpha:1.0)
dataSource = self
delegate = self
self.dataSource = self
self.delegate = self
if let FirstVC = VCArr.first {
setViewControllers([FirstVC], direction: .forward, animated: true, completion: nil)
tutorialDelegate?.tutorialPageViewController(tutorialPageViewController: self, didUpdatePageCount: VCArr.count)
}
}
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
}
}
extension TutorialPageViewController {
func pageViewController(pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool) {
if let firstViewController = viewControllers?.first,
let index = VCArr.index(of: firstViewController) {
tutorialDelegate?.tutorialPageViewController(tutorialPageViewController: self, didUpdatePageIndex: index)
}
}
}
Basically you need to change following lines
guard previousIndex >= 0 else {
return nil
}
guard nextIndex < VCArr.count else {
return nil
}
----- OR -----
Change viewControllerAfter as
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 nil
}
guard VCArr.count > nextIndex else {
return nil
}
return VCArr[nextIndex]
}
Change ViewControllerBefore as
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 nil
}
guard VCArr.count > previousIndex else {
return nil
}
return VCArr[previousIndex]
}
You can use this extension for disable all direction swipe :
extension UIPageViewController {
var isPagingEnabled: Bool {
get {
var isEnabled: Bool = true
for view in view.subviews {
if let subView = view as? UIScrollView {
isEnabled = subView.isScrollEnabled
}
}
return isEnabled
}
set {
for view in view.subviews {
if let subView = view as? UIScrollView {
subView.isScrollEnabled = newValue
}
}
}
}
}
and call this:
pageCtrl.isPagingEnabled = false
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.
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!