I’m having trouble with a UITableviewController embedded in a UIPageViewController…the system can’t distinguish between a swipe left to change pages and a swipe to delete an item in the UITableView. This particular UITableViewController happens to be on the last page of the PageViewController, so since swiping left doesn’t really do anything (nothing to the right) can I disable PageViewController from detecting swipes when current page == 3. The tricky part is I only want to ignore swipes to the left (so that the tableView reads the left swipe as a swipe to delete), whereas I want to still detect a swipe right so that the user can navigate back to page 2.
UIPageViewController code:
class WorkoutPageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
//var pageControl = UIPageControl()
var referenceToNewWorkoutKingVC: NewWorkoutKingViewController?
// MARK: UIPageViewControllerDataSource
lazy var orderedViewControllers: [UIViewController] = {
var viewControllers = [UIViewController]()
if referenceToNewWorkoutKingVC?.sessionType == goalieSessionString || referenceToNewWorkoutKingVC?.sessionType == refSessionString || referenceToNewWorkoutKingVC?.sessionType == coachSessionString || referenceToNewWorkoutKingVC?.sessionType == openSkateSessionString {
viewControllers = [self.newVc(viewController: "NewIceDataTableViewController"),
self.newVc(viewController: "NewHealthDataTableViewController")]
} else {
viewControllers = [self.newVc(viewController: "NewIceDataTableViewController"),
self.newVc(viewController: "NewHealthDataTableViewController"),
self.newVc(viewController: "NewShiftTableViewController")]
}
return viewControllers
}()
let impact = UIImpactFeedbackGenerator(style: UIImpactFeedbackGenerator.FeedbackStyle.medium)
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)
}
}
func newVc(viewController: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: viewController)
}
// MARK: Delegate methods
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
let pageContentViewController = pageViewController.viewControllers![0]
referenceToNewWorkoutKingVC?.pageControl.numberOfPages = orderedViewControllers.count
referenceToNewWorkoutKingVC?.pageControl.currentPage = orderedViewControllers.index(of: pageContentViewController)!
}
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
//Haptic when pages switch, code from: https://www.hackingwithswift.com/example-code/uikit/how-to-generate-haptic-feedback-with-uifeedbackgenerator
impact.prepare()
impact.impactOccurred()
}
// 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
// Uncomment 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]
}
}
Try this
func managePageControllerBounces(isEnable : Bool) {
for view in pageViewController.view.subviews {
if view is UIScrollView {
let scrollView = view as? UIScrollView
scrollView?.bounces = isEnable
}
}
}
Toggle bounces as per requirement
self.manegePageControllerBounces(isEnable: false)
Hope it's help to you....
Related
I trying to use UIPageViewController to move between controllers. Everything is working fine, but the only issue is that I am changing the transition style in the storyboard from page-curl to scroll. but it is not working. When I run the app and move between controllers, the animation is page-curl !!!
my code is simple, you can check it:
override func viewDidLoad() {
super.viewDidLoad()
let firstViewController = orderedViewControllers[1]
setViewControllers([firstViewController],
direction: .forward,
animated: false,
completion: nil)
self.delegate = self
self.dataSource = self
}
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 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 nil
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
```
You need to change this transitionStyle while instantiating your controller
PageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
I have a walkthrough/onboarding process when a user first uses the app. I am doing this with a Page View Controller. On the last screen I have a button which I just dragged to a tab bar view controller to create a segue. But I actually need an IBaction now for the button tap. I have tried CTRL dragging the button everywhere but the only option I get is is "Exit" instead of an action. How can I create an IBaction for this button?
Here is the page view controller code
import UIKit
class PageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
var pageControl = UIPageControl()
// MARK: UIPageViewControllerDataSource
lazy var orderedViewControllers: [UIViewController] = {
return [self.newVc(viewController: "sbBlue"),
self.newVc(viewController: "sbRed"),
self.newVc(viewController: "sbGreen")]
}()
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.white
self.pageControl.pageIndicatorTintColor = UIColor.lightGray
self.pageControl.currentPageIndicatorTintColor = UIColor.white
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]
}
}
Since you just want to set a value in user defaults showing that the user has "seen the 3rd screen" you could:
set the value in viewDidLoad() of the 3rd VC
set the value in `prepareForSegue in the 3rd VC
set the value in viewDidLoad() of the 1st tab's VC of the tabViewController
From your comments, looks like you settled on option "A"
Make sure the page view controller in the storyboard is connected to the corresponding controller file.
Need some help. I've implemented a uipagecontrol where the dots are programmatically added. It was just working. I didn't touch the code but I was working on the UI in storyboard. Then I ran it and they've disappeared. I tried using the debugger tool View Hierarchy but no luck. They don't appear there. Could me working on the UI in storyboard trigger something?
import UIKit
class EmployeeTutorialViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
var pageControl = UIPageControl()
// MARK: UIPageViewControllerDataSource
lazy var orderedViewControllers: [UIViewController] = {
return [self.newVc(viewController: "employee1"),
self.newVc(viewController: "employee2"),
self.newVc(viewController: "employee3"),
self.newVc(viewController: "employee4"),
self.newVc(viewController: "employee5"),
self.newVc(viewController: "employee6"),
self.newVc(viewController: "employee7"),
self.newVc(viewController: "employee8")]
}()
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.gray
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]
}
}
I'm creating an onboarding app for New Patients of a physical therapy clinic. The new patient will answer questions on each view controller within the UIPageViewController and tap a button to go to the next question.
I followed a tutorial and set up my UIPageViewController PageVC along with 3 view controllers. PageVC currently changes pages by swiping, but I want to be able to navigate backward and forward through view controllers using two buttons that are subviews of PageVC itself. How do I accomplish this?
I like using UIPageViewController and want to understand it a bit more. If there is a more effective method besides using UIPageViewController to accomplish this same task I'm happy to consider it.
class PageVC: UIPageViewController, UIPageViewControllerDataSource {
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.VCInstance(name: "BodyChart"),
self.VCInstance(name: "Symptoms"),
self.VCInstance(name: "HadSurgery")]
}()
private func VCInstance(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewController(withIdentifier: name)
}
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]
}
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController], direction: .forward, animated: true, completion: nil)
}
}
}
I think what you're looking for is here. But this is another way.
UIPageViewController has an instance method transition. The snippet below will move from the first to the second page child view controller.
pageViewController.transition(from: pageViewController.viewControllers[0], to: pageViewController.viewControllers[1], duration: 0.3, options: UIViewAnimationOptions.curveEaseInOut, animations: nil, completion: nil)
PageVC currently changes pages by swiping, but I want to be able to navigate backward and forward through view controllers using two buttons
Add your buttons to page content, then disable horizontal scrolling on PageVC's scrollview or disable views' user interaction in a mannered fashion.
I am using Swift 3.0 and have created a UIPageViewController currently with two pages (each one is a UIViewController). When I run the app, everything works fine, except that there are black spaces at the bottom and the right side of the app when the ViewControllers are shown. I also cannot see the page dots in the PageViewController. I have implemented the functions to place the dots on the screen:
func pageViewController(pageViewController: PageViewController,
didUpdatePageCount count: Int)
func pageViewController(pageViewController: PageViewController,
didUpdatePageIndex index: Int)
Layout
View Controller Inside the PageViewController
PageViewController Settings
Would anyone know how to fix the black borders around the pages and add the page dots?
Update
import UIKit
class PageViewController: UIPageViewController {
weak var pageDelegate: PageViewControllerDelegate?
private(set) lazy var orderedViewControllers: [UIViewController] = {
// The view controllers will be shown in this order
return [self.newViewController(name: "view1"),
self.newViewController(name: "view2"),
self.newViewController(name: "view3"),
self.newViewController(name: "view4"),
self.newViewController(name: "view5")]
}()
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
delegate = self
if let initialViewController = orderedViewControllers.first {
scrollToViewController(viewController: initialViewController, direction: UIPageViewControllerNavigationDirection.forward)
}
pageDelegate?.pageViewController(pageViewController: self, didUpdatePageCount: orderedViewControllers.count)
}
/**
Scrolls to the next view controller.
*/
func scrollToNextViewController() {
if let visibleViewController = viewControllers?.first,
let nextViewController = pageViewController(self, viewControllerAfter: visibleViewController) {
scrollToViewController(viewController: nextViewController, direction: UIPageViewControllerNavigationDirection.forward)
}
}
/**
Scrolls to the view controller at the given index. Automatically calculates
the direction.
- parameter newIndex: the new index to scroll to
*/
func scrollToViewController(index newIndex: Int) {
if let firstViewController = viewControllers?.first,
let currentIndex = orderedViewControllers.index(of: firstViewController) {
let direction: UIPageViewControllerNavigationDirection = newIndex >= currentIndex ? .forward : .reverse
let nextViewController = orderedViewControllers[newIndex]
scrollToViewController(viewController: nextViewController, direction: direction)
}
}
private func newViewController(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewController(withIdentifier: "\(name)ViewController")
}
/**
Scrolls to the given 'viewController' page.
- parameter viewController: the view controller to show.
*/
private func scrollToViewController(viewController: UIViewController,
direction: UIPageViewControllerNavigationDirection = .forward) {
setViewControllers([viewController],
direction: direction,
animated: true,
completion: { (finished) -> Void in
// Setting the view controller programmatically does not fire
// any delegate methods, so we have to manually notify the
// 'pageDelegate' of the new index.
self.notifyPageDelegateOfNewIndex()
})
}
/**
Notifies '_pageDelegate' that the current page index was updated.
*/
func notifyPageDelegateOfNewIndex() {
if let firstViewController = viewControllers?.first,
let index = orderedViewControllers.index(of: firstViewController) {
self.pageDelegate?.pageViewController(pageViewController: self, didUpdatePageIndex: index)
}
}
}
// MARK: UIPageViewControllerDataSource
extension PageViewController: UIPageViewControllerDataSource {
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
}
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
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
}
extension PageViewController: UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool) {
notifyPageDelegateOfNewIndex()
}
}
protocol PageViewControllerDelegate: class {
/**
Called when the number of pages is updated.
- parameter pageViewController: the PageViewController instance
- parameter count: the total number of pages.
*/
func pageViewController(pageViewController: PageViewController,
didUpdatePageCount count: Int)
/**
Called when the current index is updated.
- parameter pageViewController: the PageViewController instance
- parameter index: the index of the currently visible page.
*/
func pageViewController(pageViewController: PageViewController,
didUpdatePageIndex index: Int)
}
I had the same issue now, I've just solved that problem by changing the background color to white in the viewDidLoad() function
self.view.backgroundColor = .white