BigUIPaging

repository·master·Indexed 43 days ago

https://github.com/notsobigcompany/biguipaging

A collection of SwiftUI views for handling pages of content with advanced navigation. It provides high-level abstractions like PageView and PageIndicator for iOS 16.0+ and macOS 13.0+, supporting various navigation styles such as .scroll, .book, and .cardDeck.

Tokens
1.9K
Snippets
10
Records
10
Agent score
6%

What's inside BigUIPaging

  1. Install BigUIPaging via Swift Package Manager

    master

    BigUIPaging is available as a Swift Package. Add the following dependency to your Package.swift file:

    .package(url: "https://github.com/notsobigcompany/BigUIPaging.git", from: "0.0.1")

    Alternatively, in Xcode, go to File -> Add Packages and enter the repository URL. Ensure you link the package to your required target.

    Requirements:

    • iOS 16.0+
    • macOS 13.0+
  2. Create a PageView with a ForEach data source

    master

    The simplest way to initialize a PageView is by providing a ForEach data source. You must provide a selection binding to manage the current page.

    Note: A PageView has no gestures or interactions by default. You must apply a style (e.g., .pageViewStyle(.scroll)) to enable interaction.

    @State private var selection: Int = 1
    
    var body: some View {
        PageView(selection: $selection) {
            ForEach(1...10, id: ".self) { id in
                Text("Page \(id)")
            }
        }
        .pageViewStyle(.scroll)
    }
  3. Create a PageView with dynamic next/previous closures

    master

    For scenarios where pages are not known upfront (e.g., loading data 'in flight'), use the next and previous closures. These closures return the value for the next or previous page relative to the current value. If a transition is not possible, return nil.

    @State private var selection: Int = 1
    
    var body: some View {
        PageView(selection: $selection) {
            value in
            value + 1
        } previous: { value in
            value > 1 ? value - 1 : nil
        } content: { value in
            Text("Page \(value)")
        }
    }
  4. Set PageView orientation

    master

    Styles that support both vertical and horizontal navigation (specifically .scroll and .book) can be configured using the .pageViewOrientation(_:) modifier. This also affects the direction of chevrons in PageViewNavigationButton.

    .pageViewOrientation(.vertical)
  5. Configure PageView styles and transitions

    master

    Use the .pageViewStyle(_:) modifier to define how users navigate between pages. The available styles vary by platform:

    StyleiOSmacOS
    .plain✅✅
    .scroll✅✅
    .book✅
    .historyStack✅
    .bookStack✅
    .cardDeck✅

    Example usage:

    PageView(selection: $selection) {
        ...
    }
    .pageViewStyle(.bookStack)
    .pageViewStyle(.bookStack)
  6. Implement a custom PageViewStyle

    master

    To create a custom transition or interaction, conform to the PageViewStyle protocol and implement the makeBody(configuration:) method. The Configuration object provides access to:

    • content: The view for the current page.
    • selection: The current selection binding.
    • next: The next page value.
    • previous: The previous page value.
    public struct PlainPageViewStyle: PageViewStyle {
       public init() { }
    
       public func makeBody(configuration: Configuration) -> some View {
           ZStack {
               configuration.content(configuration.selection.wrappedValue)
           }
       }
    }
  7. Use PageIndicator to show page progress

    master

    The PageIndicator displays a horizontal series of dots corresponding to pages. It requires a selection binding and a total count. On iOS, this bridges to UIPageControl.

    @State private var selection = 1
    
    var body: some View {
        PageIndicator(selection: $selection, total: 5)
    }
  8. Add custom icons to PageIndicator

    master

    You can customize the icons used in the PageIndicator using a view builder. The closure provides the page index and the selected boolean state.

    Note: Currently, only the systemName initializer for images is supported.

    // Vary icons based on page index
    PageIndicator(selection: $selection, total: total) { (page, selected) in
        if page == 0 {
            Image(systemName: "location.fill")
        }
    }
    
    // Vary icons based on selection state
    PageIndicator(selection: $selection, total: total) { (page, selected) in
        if selected {
            Image(systemName: "folder.fill")
        } else {
            Image(systemName: "folder")
        }
    }
  9. Customize PageIndicator appearance

    master

    Use the following modifiers to style the PageIndicator:

    • .pageIndicatorColor(_:): Tint color for inactive dots.
    • .pageIndicatorCurrentColor(_:): Tint color for the active dot.
    • .allowsContinuousInteraction(_:): Boolean to allow continuous interaction.
    • .pageIndicatorBackgroundStyle(_:): Sets the background style (e.g., .prominent).
    • .singlePageVisibility(_:): Determines if the indicator is hidden when there is only one page (e.g., .hidden).
    • .pageIndicatorDuration(_:): Sets a duration (in seconds) for the indicator to automatically advance to the next page.
    PageIndicator(selection: $selection, total: 5)
        .pageIndicatorColor(.purple)
        .pageIndicatorCurrentColor(.pink)
        .allowsContinuousInteraction(true)
        .pageIndicatorBackgroundStyle(.prominent)
        .singlePageVisibility(.hidden)
        .pageIndicatorDuration(3.0)
  10. Navigate PageView programmatically

    master

    Beyond the selection binding, you can trigger navigation using the environment's PageViewNavigateAction. You can also use the built-in PageViewNavigationButton for standardized controls.

    To use PageViewNavigationButton, you must call .pageViewEnvironment() on the PageView to inject the necessary environment values.

    @Environment(\.navigatePageView) private var navigate
    @Environment(\.canNavigatePageView) private var canNavigate
        
    var body: some View {
        Button {
            navigate(.forwards)
        } label: {
            Text("Next")
        }
        .disabled(!canNavigate.contains(.forwards))
    }
    
    // Using built-in buttons in a toolbar
    PageView {
        ...
    }
    .toolbar {
        ToolbarItem {
            PageViewNavigationButton()
                .pageViewOrientation(.vertical)
        }
    }
    .pageViewEnvironment()