📖 A simple, highly informative page view controller

Overview

Pageboy

TL;DR UIPageViewController done properly.

⭐️ Features

  • Simplified data source management & enhanced delegation.
  • Dynamically insert & remove pages.
  • Infinite scrolling support.
  • Automatic timer-based page transitioning.
  • Support for custom animated page transitions.

📋 Requirements

Pageboy requires iOS 9 / tvOS 10; and is compatible with Swift 5.

📲 Installation

Swift Package Manager

Pageboy is compatible with Swift Package Manager and can be integrated via Xcode.

CocoaPods

Pageboy is also available through CocoaPods:

pod 'Pageboy', '~> 3.6'

Carthage

Pageboy is also available through Carthage:

github "uias/Pageboy" ~> 3.6

🚀 Usage

The Basics

  1. Create an instance of a PageboyViewController and provide it with a PageboyViewControllerDataSource.
class PageViewController: PageboyViewController, PageboyViewControllerDataSource {

    override func viewDidLoad() {
        super.viewDidLoad()
        
	self.dataSource = self
    }
}
  1. Implement the PageboyViewControllerDataSource functions.
func numberOfViewControllers(in pageboyViewController: PageboyViewController) -> Int {
    return viewControllers.count
}

func viewController(for pageboyViewController: PageboyViewController,
                    at index: PageboyViewController.PageIndex) -> UIViewController? {
    return viewControllers[index]
}

func defaultPage(for pageboyViewController: PageboyViewController) -> PageboyViewController.Page? {
    return nil
}

PageboyViewControllerDelegate

The delegate functions provided by a PageboyViewController are much more reliable and useful than what a raw UIPageViewController provides. You can use them to find out exactly where the current page is, and when it's moved, where it's headed.

willScrollToPageAtIndex

About to embark on a transition to a new page.

func pageboyViewController(_ pageboyViewController: PageboyViewController,
                           willScrollToPageAt index: Int,
                           direction: NavigationDirection,
                           animated: Bool)

didScrollToPosition

Scrolled to a relative position along the way transitioning to a new page.

func pageboyViewController(_ pageboyViewController: PageboyViewController,
                           didScrollTo position: CGPoint,
                           direction: NavigationDirection,
                           animated: Bool)

didScrollToPage

Successfully completed a scroll transition to a page.

func pageboyViewController(_ pageboyViewController: PageboyViewController,
                           didScrollToPageAt index: Int,
                           direction: NavigationDirection,
                           animated: Bool)

didReload

Child view controllers have been reloaded.

func pageboyViewController(_ pageboyViewController: PageboyViewController,
                           didReloadWith currentViewController: UIViewController,
                           currentPageIndex: PageIndex)

Navigation

You can navigate programmatically through a PageboyViewController using scrollToPage():

pageViewController.scrollToPage(.next, animated: true)
  • Infinite scrolling can be enabled with .isInfiniteScrollEnabled.
  • Interactive scrolling can also be controlled with .isScrollEnabled.

Insertion & Deletion

Pageboy provides the ability to insert and delete pages dynamically in the PageboyViewController.

func insertPage(at index: PageIndex, then updateBehavior: PageUpdateBehavior)
func deletePage(at index: PageIndex, then updateBehavior: PageUpdateBehavior)

This behaves similarly to the insertion of rows in UITableView, in the fact that you have to update the data source prior to calling any of the update functions.

Example:

let index = 2
viewControllers.insert(UIViewController(), at: index)
pageViewController.insertPage(at: index)

The default behavior after inserting or deleting a page is to scroll to the update location, this however can be configured by passing a PageUpdateBehavior value other than .scrollToUpdate.

⚡️ Other Extras

  • reloadData() - Reload the view controllers in the page view controller. (Reloads the data source).
  • .navigationOrientation - Whether to orientate the pages horizontally or vertically.
  • .currentViewController - The currently visible view controller if it exists.
  • .currentPosition - The exact current relative position of the page view controller.
  • .currentIndex - The index of the currently visible page.
  • .parentPageboy - Access the immediate parent PageboyViewController from any child view controller.

Animated Transitions

Pageboy also provides custom transition support for animated transitions. This can be customized via the .transition property on PageboyViewController.

pageboyViewController.transition = Transition(style: .push, duration: 1.0)

Note: By default this is set to nil, which uses the standard animation provided by UIPageViewController.

Auto Scrolling

PageboyAutoScroller is available to set up timer based automatic scrolling of the PageboyViewController:

pageboyViewController.autoScroller.enable()

Support for custom intermission duration and other scroll behaviors is also available.

👨🏻‍💻 About

❤️ Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/uias/Pageboy.

👮🏻‍♂️ License

The library is available as open source under the terms of the MIT License.

Comments
  • Crash (Assertion failure) in Pageboy.PatchedPageViewController when swiping fast

    Crash (Assertion failure) in Pageboy.PatchedPageViewController when swiping fast

    @msaps We have 14 apps that are using pageboy as a dependency of Tabman and when we swipe about 10 pages fast after each other the app crashes with an Assertion failure. From the stacktrace we get an Assertion failure in Pageboy without any further specifications. We have a big userbase and it is currently causing a lot of crashes so a quick fix would be very helpful for us!

    Our stacktrace: ` 2022-10-28 11:33:14.353745+0200 Groei[58716:22674120] *** Assertion failure in -[Pageboy.PatchedPageViewController queuingScrollView:didEndManualScroll:toRevealView:direction:animated:didFinish:didComplete:], UIPageViewController.m:2040

    2022-10-28 11:33:14.357491+0200 Groei[58716:22674120] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'No view controller managing visible view <UIView: 0x131686a70; frame = (0 0; 390 844); autoresize = W+H; backgroundColor = <UIDynamicCatalogColor: 0x282618320; name = Primary/Background>; layer = <CALayer: 0x28013e460>>'

    *** First throw call stack: (0x1affea248 0x1a93afa68 0x1aa96e81c 0x1b290014c 0x1b290a164 0x1b290ba9c 0x1b290926c 0x1b290a35c 0x1b28fde3c 0x1b28fe000 0x10592ba20 0x10592bef4 0x10592549c 0x105925194 0x1059246e8 0x105924ad0 0x100d482ac 0x100d48a40 0x100d4a97c 0x100d4ad30 0x10950a048 0x10950a8ac 0x1094b8a34 0x10950cdb4 0x10950cfac 0x10952d880 0x109532874 0x109533fd8 0x109532954 0x10950bcfc 0x10950c7b8 0x1094fca2c 0x10950bfd8 0x10950cdb4 0x10950cfac 0x10952d880 0x1094df5b0 0x1094df640 0x1094b900c 0x1094b8c78 0x10952a0e4 0x100ad84e8 0x100ad88e4 0x101b2584c 0x1019ffd74 0x1019dd364 0x1019ff498 0x1019436a0 0x100ad8114 0x100ad7dec 0x100ad7f48 0x100ec82cc 0x101b2584c 0x1019ffd74 0x1019dd364 0x101b9e71c 0x101bc7ad4 0x101b9e2f0 0x101b50a84 0x101b510f8 0x101b5133c 0x101b4fcc8 0x101b72cdc 0x101b6275c 0x101b6230c 0x101b6a014 0x101b69938 0x101b71440 0x101c3c140 0x101be9130 0x101c42e10 0x101bf108c 0x101bf1144 0x101bf1250 0x101b72980 0x101b637d8 0x101b6296c 0x101b60d30 0x101b6a5f4 0x101b69fd8 0x101b69bc4 0x101b46794 0x1019dd638 0x101b9e664 0x101b9e9b8 0x101b9e28c 0x101b50a84 0x101b51e78 0x101b84bf0 0x101b79b58 0x101b789b8 0x101b8d2a4 0x101e071a8 0x101e099ec 0x1b1174968 0x1069a0598 0x1069a204c 0x1069b2800 0x1069b2344 0x1b007aa08 0x1b005c368 0x1b00611e4 0x1e8e81368 0x1b2510d88 0x1b25109ec 0x100af1780 0x1ce385948)

    libc++abi: terminating with uncaught exception of type NSException

    *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'No view controller managing visible view <UIView: 0x131686a70; frame = (0 0; 390 844); autoresize = W+H; backgroundColor = <UIDynamicCatalogColor: 0x282618320; name = Primary/Background>; layer = <CALayer: 0x28013e460>>' terminating with uncaught exception of type NSException `

    When anything more is needed you can let us know.

    opened by BasBuijsen 21
  • PageBoyViewController freeze when push a new VC during auto-scrolling transition

    PageBoyViewController freeze when push a new VC during auto-scrolling transition

    If push a VC when the auto-scroll transition start, when back to the PageBoyVC, it will not respond to scroll/tap and autoScroller stop working too.

    This is only happen with autoScroller. It's working fine with user-scroll

    bug 
    opened by nsmag 17
  • PageboyView not extending to Bottom of Screen on iPhone X

    PageboyView not extending to Bottom of Screen on iPhone X

    Hey @msaps This is a freak issue in that it just appeared out of nowhere but Im hoping you can give me hints.

    So basically my ViewHierachy is like this

    -> ParentViewController 
    ----> PageboyViewController (Child of Parent)
    ----------> LeftVC
    ----------> MainVC
    

    It appears my PageboyVC's childControllers (LeftVC & MainVC) does not extend to the bottom of the screen on an iPhone X. Ive had instances where it was working fine but the issue recently reappeared without me able to retrace what exactly I could have done to cause it(up to now i can't pinpoint)

    Any hints? I haven't been able to recreate it with your example project but will try it out tomorrow.

    Thanks

    bug 
    opened by otymartin 15
  • Memory leak in PageboyViewController+Management.swift

    Memory leak in PageboyViewController+Management.swift

    Hello! Thanks for a great tool!

    When doing a memory leak audit I found one inside your Pod.

    I believe the fix is to use [weak self] on lines 71 and 76 of PageboyViewController+Management.swift

    Here's a screenshot from xcode showing the leak:

    https://imgur.com/a/V5mprcD

    bug 
    opened by RamblinWreck77 12
  • Strong And Weak Reference Issue (MemoryLeak)

    Strong And Weak Reference Issue (MemoryLeak)

    @msaps Hi man Here I am again 😊 😁 I found another issue that is critical in my opinion. The "Tabman" or "Pageboy", I do not know exactly. However, you use some strong reference cause appear this issue.

    For Simulate what happened to me. Please Use NavigationController as application rootViewController then push object which inherits from "Tabman" library. After that back to rootViewController of NavigationController. The "Tabman" or "Pageboy" must be deallocated after pop TabmanViewController or PageboyViewController but never called deinit Note: declare deinit function in "Tabman" or "Pageboy" or A class inherit from both deinit{ print("\(self)") print self for debug log of the memory address.

    bug 
    opened by farshadmb 10
  • Memory leak

    Memory leak

    I've recently started coding again after quite some time off due to studying a lot. But somehow my app's been crashing pretty often due to memory issues and naturally, I tried to find the cause, e.g. a retain cycle - in my own code. However, even after hours of searching and trying to strip my code down to the very basics I just couldn't get rid of the leak. And when debugging the memory graph I noticed something odd: The number of pages, i.e. SubViewControllers of my PageboyViewController, was way too high. Although I had in total 6 pages it displayed a bunch more (e.g. 21) when I debugged it. I noticed I could induce the problem by going back and forth a lot (e.g. from page 1 to 6 and back) and indeed I could watch my memory go up in the memory leak-typical manner with virtually every new page. The problem wouldn't occur, though if I didn't go back and forth.

    To make sure it wasn't because of my code I first replaced the old pages with other, simpler ViewControllers but it still wasn't going away. The app still crashed although it did seem to take a little longer. The videos in my usual pages seemed to add to the problem.

    Anyways, now I created a simple project with a normal UIPageViewController and a PageBoyViewController to compare them. I'm using Pageboy (3.0.5) (from Podefile.lock) and also installed a simple video player pod (Player) so that the pages in the controller would resemble my project to some extent.

    In my example project, I used exactly 10 pages which always showed the same video. As you can see on the left of the picture (debugged memory graphs), 21 instances of VideoPlayer (my SubViewController of the respective PageViewController) were created when I used PageBoy. On the contrary, only 10 (the total number of SubViewControllers) were created when I used a "normal" (therefore NormalViewController) UIPageViewController.
    To be able to compare them I tried to induce the crash as described above in both controllers. When I noticed the former (PageBoyController) became slower, I debugged. If I don't stop, however, it will invariably crash with Message from debugger: Terminated due to memory issue. Trying to induce the crash the same way in the "normal" UIPageViewController didn't work (even after one entire minute of rapidly going back and forth).

    If you would like me to share my example project, please let me know.

    Now the bad news: Unfortunately, I couldn't find the cause. I've analyzed the debugged memory graph a lot but couldn't find e.g. a retain cycle. I have to admit, though, that I am not very experienced at this, too. Therefore, I would really appreciate if someone could help me with this problem. I really like the pod and wouldn't wanna work without it but this is a little too hard of a problem for me (as no CS student) to solve. Thank you!

    PS: Please let me know if there are any questions or if anything remains unclear. I'll try to provide answers as precisely as possible.

    bug high 
    opened by blurtime 9
  • Second swipe when the first is not finished

    Second swipe when the first is not finished

    Hi!

    Sorry for my bad english... I have found a bug when I do a second swipe and the first is not finished :

    • The background color is blue and not orange
    • The Current Position and Current Page are 0.0, not 1.0
    • "Previous" button is disabled
    • Text is good (Page 2) for the first test but nothing appear for the second test

    The screen record :

    ezgif com-optimize

    Thanks for your help

    bug 
    opened by jbdujardin 9
  • [Improve Performance] ViewController for PageViewController should be initialized lazily

    [Improve Performance] ViewController for PageViewController should be initialized lazily

    PageViewController display ViewController as full screen. Therefore, Next or Previous ViewController should not be initialized when they before displaying.

    But, Current DataSource APIs require all ViewControllers.

     func viewControllers(forPageboyViewController pageboyViewController: PageboyViewController) -> [UIViewController]?
    

    I think that add below API likes UICollectionViewDataSource. Example,

     func viewController(forPageboyViewController pageboyViewController: PageboyViewController, at index: Int) -> UIViewController
    

    This API improves initialize performance when PageBoy has many viewControllers.

    What do you think?

    opened by muukii 7
  • Support for Page on Top & Below

    Support for Page on Top & Below

    @msaps Is there any plan to support pages on top or below? Im looking to create a 3 page view [Left, Center, Top] where Top is above Left.

    Also side question didnt want to raise issue but can bounce be disabled on First and Last page?

    opened by otymartin 7
  • Incorrect behaviour when calling insertPage at currentIndex

    Incorrect behaviour when calling insertPage at currentIndex

    Hi,

    so, let's say we have a PageboyViewController that is set up with 1 page. Then I am updating my input for the PageboyViewControllerDataSource and calling insertPage(at: 0, then: .doNothing).

    What I would expect is that the new page will be inserted right before my current ViewController but I'm staying at the current ViewController, because I've specified .doNothing as PageUpdateBehavior.

    What happens is that the new page is indeed inserted before my current ViewController, but also it "cross-dissolves" to the inserted ViewController.

    I see that this is implemented in the func performUpdates when it's checked if newIndex == currentIndex and then the crossDissolve + updateViewControllers is done. I guess that totally makes sense for deleting the page at the currentIndex, but I'm not sure if this is the desired behaviour for inserting?

    Thanks in advance! (And thanks for building/improving this great library 👍 )


    Version

    Pageboy (3.2.0) (actually used with Tabman (2.3.0) but this should be just related to Pageboy)

    bug 
    opened by crehbichler 6
  • NavigationBar Item Transition on Swipe

    NavigationBar Item Transition on Swipe

    @msaps Is it possible to have navigation bar items and titles transition in the manner shown below (Either with Pageboy or Tabman)? It makes for a very simple & intuitive navigation scheme in my opinion.

    nav

    support 
    opened by otymartin 6
  • The scrollView delegate is not triggered when sliding

    The scrollView delegate is not triggered when sliding

    func pageboyViewController(_ pageboyViewController: PageboyViewController, didScrollTo position: CGPoint, direction: PageboyViewController.NavigationDirection, animated: Bool)

    This delegate method does not fire

    opened by cs571393 0
  • [Bug] When isInfiniteScrollEnabled, ViewController disappears intermittently.

    [Bug] When isInfiniteScrollEnabled, ViewController disappears intermittently.

    When isInfiniteScrollEnabled is true and the scroll direction changes on the last page, the view controller disappears intermittently. This is easy to reproduce if you have two viewcontrollers.

    Here is my code

    import UIKit
    import Pageboy
    
    class ViewController: PageboyViewController {
        let firstVC: UIViewController = {
            let viewController = UIViewController()
            viewController.view.backgroundColor = .systemOrange
            return viewController
        }()
        
        let secondVC: UIViewController = {
            let viewController = UIViewController()
            viewController.view.backgroundColor = .systemPurple
            return viewController
        }()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            self.dataSource = self
            self.isInfiniteScrollEnabled = true
        }
    }
    
    extension ViewController: PageboyViewControllerDataSource {
        func numberOfViewControllers(in pageboyViewController: PageboyViewController) -> Int {
            2
        }
        
        func viewController(for pageboyViewController: PageboyViewController, at index: PageboyViewController.PageIndex) -> UIViewController? {
            switch index {
            case 0:
                return firstVC
            case 1:
                return secondVC
            default:
                fatalError("unknown index!")
            }
        }
        
        func defaultPage(for pageboyViewController: PageboyViewController) -> PageboyViewController.Page? {
            .first
        }
    }
    
    opened by KYHyeon 1
  • calling .scrollToPage(.at(index: 1), animated: true) when called from view will appear will scroll passed the 1 index

    calling .scrollToPage(.at(index: 1), animated: true) when called from view will appear will scroll passed the 1 index

    Issue: I have a PageBoyViewController set up to handle 2 other view controllers. From a different controller I am trying to navigate to the second viewController of the PageBoyViewController. When I navigate to the PageBoyViewController and switch indexes on viewWillAppear it go passed the intended index. Also, this issue only seems to appear on an iPhone 8 which is running version 12.4.

    To Replicate:

    • Have a PageBoyViewController as a tab on a tabBarController.
    • On the PageBoyViewController, navigate to the first index
    • Navigate to a different ViewController
    • From that ViewController switch to the PageBoyViewController
    • In the viewWillAppear of the PageBoyViewController navigate to the second index
    opened by austin-keith-vigo 1
  • Can you open the `updateCurrentPageIndexIfNeeded` function?

    Can you open the `updateCurrentPageIndexIfNeeded` function?

    Hello~ thank you for developing Pageboy open source🙂

    i wanna modify the currentIndex while using the Pageboy. can you modify the updateCurrentPageIndexIfNeeded located in the internal class that it can be used in an external project? The reason for modifying the currentIndex is that when a datasource is updated as new one, the currentIndex is changed to 0, which is why scrollToPage is not being used as my intended.

    for example, current tab index is 3 -> update tab -> currentIndex is changed to 0

    I wanna keep currentIndex as 3. that's the reason why I wanna modify updateCurrentPageIndexIfNeeded function!

    of course if you have other solutions, please let me know🙏🏼

    thank you and have a nice day~!

    opened by escapeanaemia 0
Releases(4.0.2)
Owner
UI At Six
Pure UIKit gravy
UI At Six
Meet Page View Controller for iOS by Cleveroad

While a standard page view allows you to navigate between pages by using simple gestures, our component goes further

Cleveroad 397 Aug 20, 2022
A controller that uses a UIStackView and view controller composition to display content in a list

StackViewController Overview StackViewController is a Swift framework that simplifies the process of building forms and other static content using UIS

Seed 867 Dec 27, 2022
Highly customizable Action Sheet Controller with Assets Preview written in Swift

PPAssetsActionController Play with me ▶️ ?? If you want to play with me, just tap here and enjoy! ?? ?? Show me ?? Try me ?? The easiest way to try me

Pavel Pantus 72 Feb 4, 2022
Simple and highly customizable iOS tag list view, in Swift.

TagListView Simple and highly customizable iOS tag list view, in Swift. Supports Storyboard, Auto Layout, and @IBDesignable. Usage The most convenient

Ela Workshop 2.5k Jan 5, 2023
Easy to use, highly customizable gauge view

GDGauge - Customizable Gauge View Requirements Xcode 11+ Swift 5 iOS 9+ Installation Swift Package Manager .package(url: "https://github.com/saeid/GDG

Saeid 74 Dec 5, 2022
Flutter Apple Product Store App UI Home Page With Getx

Flutter Apple Product Store App UI Home Page With Getx A new Flutter UI Project on my Youtube Channel . About The Project Create a beautiful Flutter U

Muawia Saeed 11 Dec 23, 2022
A page control similar to that used in Instagram

ISPageControl ISPageControl has a page control similar to that used in the Instagram Contents Requirements Installation Usage Communication Credits Li

Interactive 291 Dec 5, 2022
EdvoraUI - The UI for the login page for Edvora made in SwiftUI

About This is the UI for the login page for Edvora made in SwiftUI. Screenshot L

Noman Ahmad 0 Jan 11, 2022
A child view controller framework that makes setting up your parent controllers as easy as pie.

Description Family is a child view controller framework that makes setting up your parent controllers as easy as pie. With a simple yet powerful publi

Christoffer Winterkvist 246 Dec 28, 2022
A library, which adds the ability to hide navigation bar when view controller is pushed via hidesNavigationBarWhenPushed flag

HidesNavigationBarWhenPushed A library, which adds the ability to hide navigation bar when view controller is pushed via hidesNavigationBarWhenPushed

Danil Gontovnik 55 Oct 19, 2022
A set of UIKit helpers that simplify the usage of UIKit view's and controller's in SwiftUI.

A set of UIKit helpers that simplify the usage of UIKit view's and controller's in SwiftUI. Many of these helpers are useful even in a pure UIKit project.

SwiftUI+ 6 Oct 28, 2022
iOS custom controller used in Jobandtalent app to present new view controllers as cards

CardStackController iOS custom controller used in the Jobandtalent app to present new view controllers as cards. This controller behaves very similar

jobandtalent 527 Dec 15, 2022
ElongationPreview is an elegant UI push-pop style view controller

ElongationPreview is an elegant UI push-pop style view controller

Ramotion 886 Dec 19, 2022
Provides an iOS view controller allowing a user to draw their signature with their finger in a realistic style.

Swift version now available! Mimicking pen-on-paper signatures with a touch screen presents a difficult set of challenges. The rate touch events are e

Uber Open Source 1.3k Jan 6, 2023
Declarative, configurable & highly reusable UI development as making Lego bricks.

LeeGo is a lightweight Swift framework that helps you decouple & modularise your UI component into small pieces of LEGO style's bricks, to make UI dev

WANG Shengjia 969 Dec 29, 2022
UIPheonix is a super easy, flexible, dynamic and highly scalable UI framework + concept for building reusable component/control-driven apps for macOS, iOS and tvOS

UIPheonix is a super easy, flexible, dynamic and highly scalable UI framework + concept for building reusable component/control-driven apps for macOS, iOS and tvOS

Mohsan Khan 29 Sep 9, 2022
A highly configurable and out-of-the-box-pretty UI library

We absolutely love beautiful interfaces! As an organization named Unicorn, we are obligated to be unique and majestic.

Clayton (McIlrath) Unicorn 225 Feb 11, 2022
SwiftUI components and extensions that seem to be highly reusable

SwiftUI components and extensions that seem to be highly reusable

Yusuke Hosonuma 56 Dec 15, 2022