📖 A lightweight, paging view solution for SwiftUI

Related tags

UI swiftui
Overview

Getting Started | Customization | Installation

CI Platforms License: MIT


Getting Started

Basic usage

Using Pages is as easy as:

import Pages

struct WelcomeView: View {

    @State var index: Int = 0

    var body: some View {
        Pages(currentPage: $index) {
             Text("Welcome! This is Page 1")
             Text("This is Page 2")
             Text("...and this is Page 3")
             Circle() // The 4th page is a Circle
        }
    }
}

One can also use Pages with dynamic content:

import Pages

struct Car {
    var model: String
}

struct CarsView: View {
    let cars = [Car(model: "Ford"), Car(model: "Ferrari")]
    @State var index: Int = 0
    
    var body: some View {
        ModelPages(cars, currentPage: $index) { pageIndex, car in
            Text("The \(pageIndex) car is a \(car.model)")
                .padding(50)
                .foregroundColor(.white)
                .background(Color.blue)
                .cornerRadius(10)
        }
    }
}

How it works

Pages uses a function builder to accomplish a SwiftUI feel while using a UIPageViewController under the hood. As in VStack or HStack, the current limit of pages to add in a static way using the Pages view is 10. If more are needed use a ModelPages instead. The Pages view will take up all the available space it is given.

Note: The Pages view needs more than one page. Otherwise the compiler treats what's inside Pages as a closure.

Customization

The following aspects of Pages can be customized:

  • navigationOrientation: Whether to paginate horizontally or vertically. Default is .horizontal.
Pages(navigationOrientation: .vertical) {
    Text("Page 1")
    Text("Page 2")
}
  • transitionStyle: Whether to perform a page curl or a scroll effect on page turn. The first two examples in the GIFs above use a scroll effect, and the last one uses page curl. Default is .scroll.
Pages(
    navigationOrientation: .vertical,
    transitionStyle: .pageCurl
) {
    Text("Page 1")
    Text("Page 2")
}
  • bounce: Whether to perform a bounce effect when the user tries to scroll past the number of pages. Default is true.
Pages(
    navigationOrientation: .vertical,
    transitionStyle: .pageCurl,
    bounce: false
) {
    Text("Page 1")
    Text("Page 2")
}
  • wrap: Whether to wrap the pages once a user tries to go to the next page after the last page. Similarly whether to go to the last page when the user scrolls to the previous page of the first page. Default is false.
Pages(
    navigationOrientation: .vertical,
    transitionStyle: .pageCurl,
    bounce: false,
    wrap: true
) {
    Text("Page 1")
    Text("Page 2")
}
  • hasControl: Whether to display a page control or not. Default is true.
Pages(
    navigationOrientation: .vertical,
    transitionStyle: .pageCurl,
    bounce: false,
    wrap: true,
    hasControl: false
) {
    Text("Page 1")
    Text("Page 2")
}
  • control: A user-defined control if one wants to tune it. If this field is not provided and hasControl is true then the classical iOS page control will be used. Note control must conform to UIPageControl.
Pages(
    navigationOrientation: .vertical,
    transitionStyle: .pageCurl,
    bounce: false,
    wrap: true,
    control: MyPageControl()
) {
    Text("Page 1")
    Text("Page 2")
}
  • controlAlignment: Where to put the page control inside Pages. Default is .bottom.
Pages(
    navigationOrientation: .vertical,
    transitionStyle: .pageCurl,
    bounce: false,
    wrap: true,
    controlAlignment: .topLeading
) {
    Text("Page 1")
    Text("Page 2")
}

FAQ

  • How do I position my view to the left (.leading) or to the bottom right (.bottomTrailing)?

    • For example, if we want to position our Text view on the bottom trailing corner, we can use a GeometryReader to fill the available space:
    Pages(currentPage: $index) {
        GeometryReader { geometry in
            Text("Page 1")
                .frame(width: geometry.size.width,
                       height: geometry.size.height,
                       alignment: .bottomTrailing)
        }
        .background(Color.blue)
        GeometryReader { geometry in
            Text("Page 2")
        }.background(Color.red)
    }

    Or the Spacer trick:

    Pages(currentPage: $index) {
        VStack {
            Spacer()
            HStack {
                Spacer()
                Text("Page 1")
            }
        }
        .background(Color.blue)
        GeometryReader { geometry in
            Text("Page 2")
        }.background(Color.red)
    }

Demos

All of the demos shown on the GIF can be checked out on the demo repo.

Installation

Pages is available using the Swift Package Manager:

Using Xcode 11, go to File -> Swift Packages -> Add Package Dependency and enter https://github.com/nachonavarro/Pages

Running the tests

Once you select an iPhone destination on Xcode, press ⌘U to run the tests. Alternatively run xcodebuild test -destination 'name=iPhone 11' -scheme 'Pages' on the terminal.

Requirements

  • iOS 13.0+
  • Xcode 11.0+

TODOs

  • Add unit and UI tests.
  • Improve function builder to include conditional clauses.
  • Merge ModelPages and Pages into one common view?

Contributing

Feel free to contribute to Pages!

  1. Fork Pages
  2. Create your feature branch with your changes
  3. Create pull request

License

Pages is available under the MIT license. See the LICENSE for more info.

Comments
  • Cannot add images

    Cannot add images

    Hello, I am trying to make a TikTok style UI.

    I have tried this:

    struct ContentView: View {
         @State var index: Int = 0
       var body: some View {
                Pages(currentPage: $index, navigationOrientation: .vertical) {
                    Image("ModelX")
                }
            }
        }
    

    However I get this error:Cannot convert value of type 'Image' to closure result type '[AnyView]'

    I have also tried doing this:

    struct ContentView: View {
         @State var index: Int = 0
       var body: some View {
                Pages(currentPage: $index, navigationOrientation: .vertical) {
                    NewsView()
                }
            }
        }
    
    struct NewsView: View{
        var body: some View {
            Image("modelX")
        }
        
    }
    

    However I get the same error but lightly different: Cannot convert value of type 'NewsView' to closure result type '[AnyView]'

    Anyhelp is appreciated, I am not a noob but I am new to swiftUI

    opened by iambenmitchell 4
  • Parent.controllers is different than current.controllers.

    Parent.controllers is different than current.controllers.

    I have follow situation. struct HomeView { ... Pages(currentPage: self.$currentPage) { ChannelsView() PublicationsView() MarkedView() } ... The problem is coming from that in PublicationsView, Im realoading some data and view should be rendered again. Then "controllers" array is updated and refill with newest views. But in the Coordinator the parent reference keep the first "controllers" array. That follow to a bug: in "viewControllerBefore" and "viewControllerAfter" -> "parent.controllers.firstIndex(of: viewController)" is returning nil and the pages cannot be changed. I cannot find a easy solution. Could you help me?

    opened by neHCuoHEpa 4
  • Changing the view hierarchy makes scrolling stop working

    Changing the view hierarchy makes scrolling stop working

    Thanks for a great library!

    I am facing a problem where scrolling stops working when the view hierarchy changes. At first, everything works great. However, if I modify the view hierarchy in any way (e.g. by setting a state property that causes a label to change), the page view's scroll just starts bouncing back. The problem appears in both Pages and ModelPages.

    Do you have any idea what may be causing this?

    UPDATE The problem exists in 0.1.3, but is solved in master. Can you consider making a 0.1.4 version of master?

    opened by danielsaidi 3
  • Navigation Link

    Navigation Link

    How do we integrate navigation link with pages? I have tried the code below. The view shows it was clicked (visually), however no desination is actually navigated to.

        var body: some View {
            ModelPages(cars) { index, car in
    NavigationLink(destination: DestinationView(car: car)) {
                Text("The \(index) car is a \(car.model)")
                    .padding(50)
                    .foregroundColor(.white)
                    .background(Color.blue)
                    .cornerRadius(10)
            }
        }
    }
    
    opened by VowerOrg 3
  • Background colors should be clear.

    Background colors should be clear.

    Hi, background colors of the ViewController's.view should be .clear.

    PageViewController.swift:52 Pages.swift:105 ModelPages:115

    I somehow cannot create a brach....

    opened by agiokas 3
  • Image clipping/buggy

    Image clipping/buggy

    Hello, I am trying to make a UI like TikTok but the images are behaving weirdly. It works fine on my 11 pro max but not on a screen size smaller, it sort of jumps around. Screenshot

    Pages image issue (clipping)
    opened by iambenmitchell 2
  • Fatal error: Index out of range

    Fatal error: Index out of range

    Hi, firstly I wanna say thanks for this package.

    I tried to use dynamic page with my ViewModel data but when I tried to run the app, I got to crash with an index out of range error.

    Fatal error: Index out of range

    View:

    struct SliderNewsList: View {
        @ObservedObject var viewModel = SliderNewsVM()
        @State var index: Int = 0
        
        var body: some View {
            ModelPages(viewModel.news, currentPage: $index) { pageIndex, new in
                Text(new.title)
            }
        }
    }
    

    ViewModel:

    class SliderNewsVM: ObservableObject {
        
        @Published var news = [SliderNews]()
        
        init() {
            fetchSliderNews()
        }
    }
    
    extension SliderNewsVM {
        func fetchSliderNews() {
            ApiManager.shared.fetchSliderNews { [weak self] result in
                guard let self = self else { return }
                switch result {
                case .success(let result):
                    self.news = result
                case .failure(_):
                    print("error")
                }
            }
        }
    }
    
    opened by halilyuce 2
  • Feature: Dynamically removing a page

    Feature: Dynamically removing a page

    I would be very interested in being able to dynamically remove a page by, say, clicking on a button on said page. Currently, this is not supported and if the data element is removed, PageViewController crashes with index out of range.

    enhancement 
    opened by NeverwinterMoon 2
  • Page index issue

    Page index issue

    When i want to show current index, rightToLeft swipe is not working. If i enter to some text (like Text("Test header"), it's working. Really interesting.

    `import Pages

    struct WelcomeView: View {

    @State var index: Int = 0
    
    var body: some View {
      VStack {
        Text("\(index)")
        Spacer()
        Pages(currentPage: $index) {
             Text("Welcome! This is Page 1")
             Text("This is Page 2")
             Text("...and this is Page 3")
             Circle() // The 4th page is a Circle
        }
      }
    }
    

    }`

    opened by furasli 2
  • setCurrentItem: positionIndex in ModalPages

    setCurrentItem: positionIndex in ModalPages

    For example, we open a book, stopped reading at page number 33. The book resumes at page 1 and we have to flip page by page until page number 33. If the book resumes at page 33, it would be great.

    opened by csdal 2
  • transition problem with pageCurl transition & didFinishAnimation

    transition problem with pageCurl transition & didFinishAnimation

    The adaptation to SwiftUI works great thanks. I needed this for pageCurl transition and not the scroll transition. However, the transition animation does not work properly. The top page curls up nicely and the next page is visible behind it. However, when the animation is complete the second page curls away too leaving a blank white page. There is an error message shown "Unbalanced calls to begin/end appearance transitions" .

    Two things - (1) this problem does not occur when the transition type is scroll -only there for pageCurl type. (2). if I comment out the UIPageViewControllerDelegate method didFinishAnimating finished the problem goes away - the transition works nicely.

    I am not sure that the problem is your code, because I get the same problem just using the code in the tutorial from Apple ( https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit ) .

    I can send you the complete example if needed. I am hoping that you can figure out a solution.

    opened by RajaSeshadri 1
  • Issue with page animation triggering when ANY state variable changes on page

    Issue with page animation triggering when ANY state variable changes on page

    I'm having an issue with the library, where any time any of my other state variables change on page, the animation triggers.

    If I've got a Page 0, and a Page 1, the "moving back to 0" animation triggers anytime a variable changes on page 0 (UX element, etc).

    Is there any way to prevent this from happening?

    opened by edsonengineering 1
  • Update the number of pages on UIPageControl in case they have changed

    Update the number of pages on UIPageControl in case they have changed

    This PR makes the UIPageControl instance know about changes to the number of pages (which seem to be correctly updated on the PageControl instance already).

    opened by letynsoft 0
  • Disable page turn when tapping

    Disable page turn when tapping

    Hi, this is an amazing package, thanks! Is there a way to modify the code to only allow page turning when swiping? I have views with .onTapGesture{} placed near the edge and it's causing the page to turn instead of performing the function I'm calling. I was looking at the files in this package but can't seem to figure out if this an be done.

    opened by jimdube 0
  • View Disappearing when Pages used multiple times

    View Disappearing when Pages used multiple times

    I have a menu that changes the content shown on the screen. In two of the different views I used Pages. Showing the first instance of Pages works fine, but when I go to the other view with an instance of Pages, the Pages view flashes and then disappears. Then if I try to go back the other one disappears as well. If I remove one instance of Pages it works fine.

    opened by ehardacre 1
Releases(0.1.5)
Owner
Nacho Navarro
Plastics
Nacho Navarro
Infinite paging controller, scrolling through contents and title bar scrolls with a delay

PageController PageController is infinite paging controller, scrolling through contents and title bar scrolls with a delay. Then it provide user inter

Hirohisa Kawasaki 408 Nov 28, 2022
Paging Library for Infinity Scroll using RxSwift

Paging Library for Infinity Scroll using RxSwift

SubinJang 2 Mar 31, 2022
UIAdapter - An elegant solution to the iOS screen adaptation problem

UIAdapter - An elegant solution to the iOS screen adaptation problem ???? 天朝子民 Features Numerical type fast conversion Storyboard equal scale adaptati

LEE 39 Dec 25, 2022
Newly is a drop in solution to add Twitter/Facebook/Linkedin style, new updates/tweets/posts available button

Newly is a drop in solution to add Twitter/Facebook/Linkedin style, new updates/tweets/posts available button. It can be used to notify user about new content availability and can other actions can be triggers using its delegate method.

Dhiraj Rajendra Jadhao 197 Sep 22, 2022
SwiftUI-Margin adds a margin() viewModifier to a SwiftUI view.

SwiftUI-Margin adds a margin() viewModifier to a SwiftUI view. You will be able to layout the margins in a CSS/Flutter-like.

Masaaki Kakimoto(柿本匡章) 2 Jul 14, 2022
A way to quickly add a notification badge icon to any view. Make any view of a full-fledged animated notification center.

BadgeHub A way to quickly add a notification badge icon to any view. Demo/Example For demo: $ pod try BadgeHub To run the example project, clone the r

Jogendra 772 Dec 28, 2022
Confetti View lets you create a magnificent confetti view in your app

ConfettiView Confetti View lets you create a magnificent confetti view in your app. This was inspired by House Party app's login screen. Written in Sw

Or Ron 234 Nov 22, 2022
High performance and lightweight UIView, UIImage, UIImageView, UIlabel, UIButton, Promise and more.

SwiftyUI High performance and lightweight UIView, UIImage, UIImageView, UIlabel, UIButton and more. Features SwiftyView GPU rendering Image and Color

Haoking 336 Nov 26, 2022
Lightweight touch visualization library in Swift. A single line of code and visualize your touches!

TouchVisualizer is a lightweight pure Swift implementation for visualising touches on the screen. Features Works with just a single line of code! Supp

Morita Naoki 851 Dec 17, 2022
Lightweight framework for Unsplash in Swift

Lightweight framework for Unsplash in Swift

Pablo Camiletti 12 Dec 30, 2022
A super lightweight popView.

SNAugusPopView Features High performance: The library's dependencies all use system libraries and files , only a instance global. Automatic layout: Th

Augus 11 Sep 1, 2022
A SwiftUI Library for creating resizable partitions for View Content.

Partition Kit Recently Featured In Top 10 Trending Android and iOS Libraries in October and in 5 iOS libraries to enhance your app! What is PartitionK

Kieran Brown 230 Oct 27, 2022
Advanced List View for SwiftUI with pagination & different states

AdvancedList This package provides a wrapper view around the SwiftUI List view which adds pagination (through my ListPagination package) and an empty,

Chris 246 Jan 3, 2023
SwiftUI view enabling navigation between pages of content, imitating the behaviour of UIPageViewController for iOS and watchOS

PageView SwiftUI view enabling page-based navigation, imitating the behaviour of UIPageViewController in iOS. Why SwiftUI doesn't have any kind of pag

Kacper Rączy 365 Dec 29, 2022
🚀 Elegant Pager View fully written in pure SwiftUI.

PagerTabStripView Made with ❤️ by Xmartlabs team. XLPagerTabStrip for SwiftUI! Introduction PagerTabStripView is the first pager view built in pure Sw

xmartlabs 482 Jan 9, 2023
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
⬆️ A SwiftUI view component sliding in from bottom

⬆️ A SwiftUI view component sliding in from bottom

Tieda 595 Dec 28, 2022
Creating a simple selectable tag view in SwiftUI is quite a challenge. here is a simple & elegant example of it.

SwiftUI TagView Creating a simple selectable tag view in SwiftUI is quite a challenge. here is a simple & elegant example of it. Usage: Just copy the

Ahmadreza 16 Dec 28, 2022
Demonstrate the toolbar view modifier for SwiftUI with different placements

ToolbarProject Demonstrate the toolbar view modifier for SwiftUI with different placements Youtube tutorial --> https://youtu.be/jTW5Z-kyL8g Use toolb

Karin Prater 2 Mar 24, 2022