{"id":18269831,"url":"https://github.com/ThasianX/ElegantPages","last_synced_at":"2025-04-04T23:31:35.762Z","repository":{"id":43233528,"uuid":"274540274","full_name":"ThasianX/ElegantPages","owner":"ThasianX","description":"The elegant full screen page view missed in SwiftUI","archived":false,"fork":false,"pushed_at":"2020-08-01T21:07:29.000Z","size":29441,"stargazers_count":109,"open_issues_count":3,"forks_count":13,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-24T04:51:39.728Z","etag":null,"topics":["elegantpages","pageview","pageviewcontroller","swift-package-manager","swiftui","swiftui-animations","swiftui-components","swiftui-example"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ThasianX.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-06-24T00:50:57.000Z","updated_at":"2025-03-23T04:59:54.000Z","dependencies_parsed_at":"2022-09-08T06:31:07.019Z","dependency_job_id":null,"html_url":"https://github.com/ThasianX/ElegantPages","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ThasianX%2FElegantPages","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ThasianX%2FElegantPages/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ThasianX%2FElegantPages/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ThasianX%2FElegantPages/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ThasianX","download_url":"https://codeload.github.com/ThasianX/ElegantPages/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247266476,"owners_count":20910831,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elegantpages","pageview","pageviewcontroller","swift-package-manager","swiftui","swiftui-animations","swiftui-components","swiftui-example"],"created_at":"2024-11-05T11:37:28.712Z","updated_at":"2025-04-04T23:31:33.174Z","avatar_url":"https://github.com/ThasianX.png","language":"Swift","funding_links":[],"categories":["swiftui-example"],"sub_categories":[],"readme":"# ElegantPages\n\n\u003cp align=\"leading\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/platform-iOS-blue.svg?style=flat\" alt=\"Platforms\" /\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Swift-5-orange.svg\" /\u003e\n    \u003ca href=\"https://github.com/ThasianX/Elegant-Pages/blob/master/LICENSE\"\u003e\u003cimg src=\"http://img.shields.io/badge/license-MIT-blue.svg?style=flat\" alt=\"License: MIT\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nElegantPages is an efficient and customizable full screen page view written in SwiftUI.\n\n\u003cbr/\u003e\n\n\u003cimg src=\"https://github.com/ThasianX/GIFs/blob/master/ElegantCalendar/dark_demo.gif\" width=\"300\"/\u003e\n\n- [Introduction](#introduction)\n- [Basic Usage](#basic-usage)\n- [How It Works](#how-it-works)\n- [Customization](#customization)\n- [Demos](#demos)\n- [Requirements](#requirements)\n- [Contributing](#contributing)\n- [Installation](#installation)\n- [License](#license)\n\n## Introduction\n\n`ElegantPages` comes with 2 types of components, [`ElegantPagesView`](https://github.com/ThasianX/ElegantPages/blob/master/Sources/ElegantPages/Pages/Internal/ElegantPagesView.swift) and [`ElegantListView`](https://github.com/ThasianX/ElegantPages/blob/master/Sources/ElegantPages/Lists/Internal/ElegantListView.swift). \n\nFor simpler usage, `ElegantPagesView` is recommended as it loads all page views immediately.\n\nFor more complex usage, `ElegantListView` is recommended as it loads page views on demand([learn more](#how-it-works)).\n\nThe elegance of both these views is that they work as a paging component should be intended to work. One bug that is often seen in SwiftUI is that `ScrollView`, `List`, or any `Gesture` almost certainly interferes with other gestures in the view. However, `ElegantPages` fixes this issue and scrolling through a paging component even with embedded `Gestures` works elegantly.\n\n## Basic usage\n\nThe `ElegantPagesView` component is available through [`ElegantHPages`](https://github.com/ThasianX/ElegantPages/blob/master/Sources/ElegantPages/Pages/Public/ElegantHPages.swift) and [`ElegantVPages`](https://github.com/ThasianX/ElegantPages/blob/master/Sources/ElegantPages/Pages/Public/ElegantVPages.swift).\n\n```swift\n\nimport ElegantPages\n\nstruct ElegantVPagesExample: View {\n\n    let manager = ElegantPagesManager(startingPage: 1, pageTurnType: .earlyCutOffDefault)\n\n    var body: some View {\n        ElegantVPages(manager: manager) {\n            CustomButtonView()\n            CustomView()\n            CustomListView()\n        }\n    }\n\n}\n```\n\nThe `ElegantListView` component is available through [`ElegantHList`](https://github.com/ThasianX/ElegantPages/blob/master/Sources/ElegantPages/Lists/Public/ElegantHList.swift) and [`ElegantVList`](https://github.com/ThasianX/ElegantPages/blob/master/Sources/ElegantPages/Lists/Public/ElegantVList.swift).\n\n```swift\n\nimport ElegantPages\n\nlet listData = (1...40).map { _ in \"Ideally, this should be more dynamic content to make the most use out of this list\" }\n\nstruct ElegantVListExample: View {\n\n    let manager = ElegantListManager(pageCount: vListData.count, pageTurnType: .earlyCutOffDefault)\n    \n    var body: some View {\n        ElegantVList(manager: manager,\n                     pageTurnType: .earlyCutOffDefault) { page in \n            ExampleView(page: page).erased\n        }\n    }\n\n}\n\nstruct ExampleView: View {\n\n    let page: Int\n\n    var body: some View {\n        VStack {\n            Text(\"Page \\(page)\")\n                .font(.largeTitle)\n            Text(listData[page])\n                .font(.title)\n        }\n        .padding()\n    }\n    \n}\n\n```\n\n## How it works\n\n`ElegantPagesView` is pretty simple. It uses a [function builder](https://github.com/apple/swift-evolution/blob/9992cf3c11c2d5e0ea20bee98657d93902d5b174/proposals/XXXX-function-builders.md) to gather the page views and puts them in either a `HStack` or `VStack` depending on the type of `ElegantPages` view chosen. As a result, all views are created immediately. \n\n`ElegantListView` is quite interesting. For more flexibility, it uses a `@ViewBuilder` to get the view for any given page(it's the closure at the end of the `ElegantVList` declaration. When it is first initialized, it calls this closure at most 3 times, to get the views for the starting pages. These views are used to initialize an array of at most 3 `UIHostingControllers`, whose `rootViews` are set to a specific origin in a `UIViewController`. Here's the catch, at any given moment, there are at most only 3 pages loaded. As the user scrolls to the next page, old pages are removed and new pages are inserted; the views themselves are juggled as their origins are changed per page turn. This keeps overall memory usage down and also makes scrolling blazingly fast. If you're curious, take a [peek](https://github.com/ThasianX/ElegantPages/blob/master/Sources/ElegantPages/Lists/Internal/ElegantListController.swift).\n\n## Customization\n\nThe following aspects of any `ElegantPages` component can be customized:\n\n#### `pageTurnType`: Whether to scroll to the next page early or until the user lets go of the drag\n\n```swift \n\npublic enum PageTurnType {\n\n    case regular(pageTurnDelta: CGFloat)\n    case earlyCutoff(config: EarlyCutOffConfiguration)\n\n}\n\npublic struct EarlyCutOffConfiguration {\n\n    public let scrollResistanceCutOff: CGFloat\n    public let pageTurnCutOff: CGFloat\n    public let pageTurnAnimation: Animation\n    \n}\n\n```\n\nA regular page turn only turns the page after the user ends their drag. \n\n- The `pageTurnDelta` represents the percentage of how far across the screen the user has to drag in order for the page to turn when they let go. The default value for this is 0.3, as part of an extension of `PageTurnType`. \n- The default regular page turn can be accessed through `PageTurnType.regularDefault`\n\nAn early cutoff page turn turns the page when the user drags a certain distance across the screen.\n\n- `scrollResistanceCutOff`: The distance that the view is offset as the user drags.\n- `pageTurnCutOff`: The distance across the screen the user has to drag before the page is turned(once this value is reached, the page automatically gets turned to and the user's ongoing gesture is invalidated). \n- `pageTurnAnimation`: The animation used when the page is turned\n- The default early cut off page turn can be accessed through `PageTurnType.earlyCutOffDefault`\n\nIn case `scrollResistanceCutOff` isn't clear, here's an example. Say we have a horizontally draggable view. If you drag 80 pixels to the right, the offset that is visible to you is also 80 pixels. The amount you scroll is equal to the visible offset. However, if you have a scroll resistance of say 40 pixels, after dragging 80 pixels to the right, you only see that the view has moved 40 pixels to the right. That is why it is called resistance.\n\n###$ `viewForPage`: datasource method called whenever a new page is displayed that asks for the view of the new page. Available only for `ElegantList` components\n\n```swift \n\n// Use as a function\nElegantVList(..., viewForPage: exampleView)\n\nfunc exampleView(for page: Int) -\u003e AnyView { ExampleView(...) }\n\n// Use as a closure\nElegantHList(...) { page in ExampleView(...) }\n    \n```\n\n#### `onPageChanged`: called whenever a new page is shown. Available for all `ElegantPages` components.\n\n```swift \n\nElegantVList(...)\n    .onPageChanged(...)\n\nElegantHList(...)\n    .onPageChanged(...)\n    \nElegantHPages(...)\n    .onPageChanged(...)\n    \nElegantVPages(...)\n    .onPageChanged(...)\n    \n```\n\n#### `frame`: used to set a custom height or width for `ElegantList` components\n\n```swift \n\n// You may want a smaller width for the VList. However, height for the VList will always be the screen height\nElegantVList(...)\n    .frame(width: ...)\n\n// You may want a smaller height for the HList. However, width for the HList will always be the screen width\nElegantHList(...)\n    .frame(height: ...)\n    \n```\n\n## Demos\n\nThe demo shown in the GIF can be checked out on [ElegantCalendar](https://github.com/ThasianX/ElegantCalendar).\n\nFor simpler demos, look at the [example repo](https://github.com/ThasianX/ElegantPages/tree/master/Example).\n\n## Installation\n\n`ElegantPages` is available using the [Swift Package Manager](https://swift.org/package-manager/):\n\nUsing Xcode 11, go to `File -\u003e Swift Packages -\u003e Add Package Dependency` and enter https://github.com/ThasianX/ElegantPages\n\nIf you are using `Package.swift`, you can also add `ElegantPages` as a dependency easily.\n\n```swift\n\nlet package = Package(\n  name: \"TestProject\",\n  dependencies: [\n    .package(url: \"https://github.com/ThasianX/ElegantPages\", from: \"1.4.0\")\n  ],\n  targets: [\n    .target(name: \"TestProject\", dependencies: [\"ElegantPages\"])\n  ]\n)\n\n```\n\n## Requirements\n\n- iOS 13.0+\n- Xcode 11.0+\n\n## Contributing\n\nIf you find a bug, or would like to suggest a new feature or enhancement, it'd be nice if you could [search the issue tracker](https://github.com/ThasianX/ElegantPages/issues) first; while we don't mind duplicates, keeping issues unique helps us save time and considates effort. If you can't find your issue, feel free to [file a new one](https://github.com/ThasianX/ElegantPages/issues/new).\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FThasianX%2FElegantPages","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FThasianX%2FElegantPages","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FThasianX%2FElegantPages/lists"}