Project Icon

Parchment

灵活的iOS分页控制器库,提供自定义菜单项、内存高效的视图控制器管理和无限滚动功能

Parchment是一款灵活的iOS分页控制器库,提供自定义菜单项、内存高效的视图控制器管理和无限滚动功能。该库支持多种自定义选项,如菜单项大小、间距和指示器样式等。兼容SwiftUI和UIKit,Parchment可轻松集成到iOS应用中,适用于构建各类分页界面。

开始使用 | 自定义 | 安装


城市示例 Unsplash示例 日历示例

特性

Parchment 允许您在视图控制器之间翻页,同时显示任何类型的与内容一起滚动的通用指示器。使用 Parchment 的一些好处包括:

  • 高度可定制
    菜单项是使用 UICollectionView 构建的,这意味着您几乎可以显示任何想要的内容。您甚至可以子类化布局以创建完全自定义的行为。

  • 内存效率高:
    Parchment 只在需要时分配视图控制器,这意味着如果您有很多视图控制器,您不必一开始就全部初始化它们。

  • 无限滚动:
    由于视图控制器只在您滚动时分配,您可以创建无限大的数据源。这非常适合像日历这样的东西。

目录

开始使用

使用 UIKit?转到 UIKit 文档

SwiftUI

基本用法

创建一个 PageView 实例,包含您想要显示的页面。每个 Page 需要一个标题和一个内容视图,可以是任何 SwiftUI 视图。

PageView {
    Page("标题 0") {
        Text("页面 0")
    }
    Page("标题 1") {
        Text("页面 1")
    }
}

默认情况下,菜单项显示为标题,但您也可以传入任何 SwiftUI 视图作为菜单项。状态参数允许您根据选中状态和视图的滚动位置自定义菜单项。例如,您可以显示一个基于其进度旋转的图标,如下所示:

PageView {
    Page { state in
        Image(systemName: "star.fill")
            .rotationEffect(Angle(degrees: 90 * state.progress))
    } content: {
        Text("页面 1")
    }
}

动态页面

要创建具有动态数量页面的 PageView,您可以传入一个项目集合,其中每个项目映射到一个 Page

PageView(items, id: \.self) { item in
    Page("标题 \(item)") {
        Text("页面 \(item)")
    }
}

更新选择

要选择特定项目,您可以将绑定传递到 PageView,其中包含当前选中项目的索引。更新绑定时,Parchment 将滚动到新索引。

@State var selectedIndex: Int = 0
...
PageView(selectedIndex: $selectedIndex) {
    Page("标题 1") {
        Button("下一个") {
            selectedIndex = 1
        }
    }
    Page("标题 2") {
        Text("页面 2")
    }
}

修饰符

您可以使用以下修饰符自定义 PageView。有关每个选项的更多详细信息,请参阅选项

PageView {
    Page("标题 1") {
        Text("页面 1")
    }
}
.menuItemSize(.fixed(width: 100, height: 60))
.menuItemSpacing(20)
.menuItemLabelSpacing(30)
.menuBackgroundColor(.white)
.menuInsets(.vertical, 20)
.menuHorizontalAlignment(.center)
.menuPosition(.bottom)
.menuTransition(.scrollAlongside)
.menuInteraction(.swipe)
.contentInteraction(.scrolling)
.contentNavigationOrientation(.vertical)
.selectedScrollPosition(.preferCentered)
.indicatorOptions(.visible(height: 4))
.indicatorColor(.blue)
.borderOptions(.visible(height: 4))
.borderColor(.blue.opacity(0.2))
UIKit

使用 UIKit 的基本用法

Parchment 围绕 PagingViewController 类构建。您可以使用视图控制器数组初始化它,它将使用它们的 title 属性为每个视图控制器显示菜单项。

let firstViewController = UIViewController()
let secondViewController = UIViewController()

let pagingViewController = PagingViewController(viewControllers: [
  firstViewController,
  secondViewController
])

查看更多:基本用法

数据源

在大多数情况下,使用视图控制器数组初始化 PagingViewController 是可以的,但如果您有多个视图控制器,您可能不想一开始就全部分配它们。如果您要显示固定数量的视图控制器,您可以通过实现 PagingViewControllerDataSource 来设置自己的数据源:

extension ViewController: PagingViewControllerDataSource {
    func numberOfViewControllers(in pagingViewController: PagingViewController) -> Int {
        return 10
    }

    func pagingViewController(_ pagingViewController: PagingViewController, viewControllerAt index: Int) -> UIViewController {
        return ChildViewController(index: index)
    }

    func pagingViewController(_: PagingViewController, pagingItemAt index: Int) -> PagingItem {
        return PagingIndexItem(title: "视图 \(index)", index: index)
    }
}

然后您需要设置 dataSource 属性并选择初始项目:

let pagingViewController = PagingViewController()
pagingViewController.dataSource = self
pagingViewController.select(index: 0)

使用数据源意味着 Parchment 只会为当前选中的项目及其兄弟项分配视图控制器。如果您有许多视图控制器,这比使用 PagingViewController(viewControllers:) 更节省内存。

阅读更多:使用数据源

无限数据源

使用 PagingViewControllerDataSource 意味着您需要知道要显示多少个视图控制器。如果您正在创建像日历这样的东西,视图控制器的数量可能是无限大的。在这种情况下,您可以使用 PagingViewControllerInfiniteDataSource 协议:

extension ViewController: PagingViewControllerInfiniteDataSource {
    func pagingViewController(_: PagingViewController, viewControllerFor pagingItem: PagingItem) -> UIViewController {
        return ItemViewController(item: pagingItem)
    }

    func pagingViewController(_: PagingViewController, itemBefore pagingItem: PagingItem) -> PagingItem? {
        guard let item = pagingItem as? Item else { return nil }
        return Item(index: item.index - 1)
    }

    func pagingViewController(_ : PagingViewController, itemAfter pagingItem: PagingItem) -> PagingItem? {
        guard let item = pagingItem as? Item else { return nil }
        return Item(index: item.index + 1)
    }
}

然后设置 infiniteDataSource 属性并选择初始项目:

let pagingViewController = PagingViewController()
pagingViewController.infiniteDataSource = self
pagingViewController.select(pagingItem: Item(index: 0))

这种模式与 UIPageViewControllerDataSource 协议非常相似。主要区别在于,您不是直接返回视图控制器,而是必须返回一个符合 PagingItem 协议的实例。Parchment 会递归调用这些方法,直到可用空间被填满。

阅读更多:使用无限数据源

选择项目

您可以使用以下方法以编程方式选择项目:

func select(pagingItem: PagingItem, animated: Bool = false)

假设您想选择第一个项目:

override func viewDidLoad() {
  super.viewDidLoad()
  if let first = pagingViewController.children.first as? PagingItem {
    pagingViewController.select(pagingItem: first)
  }
}

或者如果您设置了 dateSource 属性,您可以根据索引选择项目:

func select(index: Int, animated: Bool = false)

重新加载数据

您可以使用此方法重新加载数据:

func reloadData()

如果之前选中的项目仍然是更新数据的一部分,这将保留它。如果不是,它将选择列表中的第一个项目。它还将重新加载页面视图控制器中显示的视图控制器。如果您只想重新加载菜单项,可以使用此方法:

func reloadMenu()

当使用 PagingViewControllerInfiniteDataSource 时,调用 reloadData() 将不起作用,因为我们需要知道初始项目应该是什么。在这种情况下,您应该使用此方法:

func reloadData(around: PagingItem)

这将标记给定的分页项目为已选中,并在其周围生成新项目。

代理

Parchment 通过 PagingViewControllerDelegate 协议为过渡过程的每个步骤提供代理方法。

protocol PagingViewControllerDelegate: class {

    func pagingViewController(
        _: PagingViewController,
        isScrollingFromItem currentPagingItem: PagingItem,
        toItem upcomingPagingItem: PagingItem?,
        startingViewController: UIViewController,
        destinationViewController: UIViewController?,
        progress: CGFloat)

    func pagingViewController(
        _: PagingViewController,
        willScrollToItem pagingItem: PagingItem,
        startingViewController: UIViewController,
        destinationViewController: UIViewController)

    func pagingViewController(
        _ pagingViewController: PagingViewController,
        didScrollToItem pagingItem: PagingItem,
        startingViewController: UIViewController?,
        destinationViewController: UIViewController,
        transitionSuccessful: Bool)

    func pagingViewController(
        _ pagingViewController: PagingViewController,
        didSelectItem pagingItem: PagingItem)
}

大小代理

默认情况下,菜单项的大小由 menuItemSize 属性控制。如果您需要单独控制每个菜单项的宽度,可以使用 PagingControllerSizeDelegate 协议:

protocol PagingViewControllerSizeDelegate: class {
    func pagingViewController(
        _: PagingViewController,
        widthForPagingItem pagingItem: PagingItem,
        isSelected: Bool) -> CGFloat
}

然后在 PagingViewController 上设置 sizeDelegate

let pagingViewController = PagingViewController()
pagingViewController.sizeDelegate = self

自定义

Parchment 的设计非常灵活。菜单项使用 UICollectionView 显示,因此它们几乎可以显示任何您想要的内容。如果您需要进一步自定义,甚至可以子类化集合视图布局。所有自定义都由下面列出的属性处理。

自定义单元格

要使用自定义单元格,您需要子类化 PagingCell 并为给定的 PagingItem 注册单元格类型:

let pagingViewController = PagingViewController()
pagingViewController.register(CalendarPagingCell.self, for: CalendarItem.self)

当您在数据源中返回给定的PagingItem时,Parchment 将取消排队您的自定义单元格。您可以为不同的PagingItem注册多种单元格类型。

属性

所有自定义属性都在PagingViewController上设置:

let pagingViewController = PagingViewController()
pagingViewController.menuItemSize = .fixed(width: 40, height: 40)
pagingViewController.menuItemSpacing = 10

有关所有自定义选项,请参见选项

选项

menuItemSize

菜单项的大小。当使用sizeDelegate时,宽度将被忽略。

enum PagingMenuItemSize {
  case fixed(width: CGFloat, height: CGFloat)

  // 根据单元格的固有内容大小自动计算菜单项的大小。
  // 尝试得出一个与单元格预期宽度相似的估计宽度。
  case selfSizing(estimatedWidth: CGFloat, height: CGFloat)

  // 尝试将所有菜单项适应屏幕边界内。
  // 如果项目无法适应,项目将正常滚动并
  // 将菜单项宽度设置为`minWidth`。
  case sizeToFit(minWidth: CGFloat, height: CGFloat)
}

默认值:.sizeToFit(minWidth: 150, height: 40)

menuItemSpacing

菜单项之间的间距。

默认值:0

menuItemLabelSpacing

菜单项标签的水平约束。

默认值:20

menuInsets

所有菜单项周围的内边距。

默认值:UIEdgeInsets()

menuHorizontalAlignment

enum PagingMenuHorizontalAlignment {
  case `default`

  // 当PagingMenuItemSize为.fixed且所有分页项的宽度总和
  // 小于分页菜单时,允许所有分页项在分页菜单内居中
  case center
}

默认值:.default

menuTransition

确定在滚动内容时菜单项的过渡行为。

enum PagingMenuTransition {
  // 根据内容滚动的程度更新滚动偏移量。使菜单项在滚动时平滑过渡。
  case scrollAlongside

  // 在过渡完成后动画菜单项位置。
  case animateAfter
}

默认值:.scrollAlongside

menuInteraction

确定用户如何与菜单项交互。

enum PagingMenuInteraction {
  case scrolling
  case swipe
  case none
}

默认值:.scrolling

menuLayoutClass

集合视图布局的类型。如果您想使用自己的布局子类,请覆盖此属性。设置此属性将初始化新的布局类型并更新集合视图。

默认值:PagingCollectionViewLayout.Type

selectedScrollPosition

确定选定的菜单项在被选中时应如何对齐。实际上与UICollectionViewScrollPosition相同。

enum PagingSelectedScrollPosition {
  case left
  case right

  // 尽可能将选定的菜单项居中。如果项目
  // 在最左侧或最右侧,它将不会更新滚动位置。
  // 实际上与UIScrollView上的.centeredHorizontally相同。
  case preferCentered
}

默认值:.preferCentered

indicatorOptions

为选定的菜单项添加指示器视图。指示器宽度将等于选定菜单项的宽度。内边距仅水平应用。

enum PagingIndicatorOptions {
  case hidden
  case visible(
    height: CGFloat,
    zIndex: Int,
    spacing: UIEdgeInsets,
    insets: UIEdgeInsets)
}

默认值:

.visible(
  height: 4,
  zIndex: Int.max,
  spacing: UIEdgeInsets.zero,
  insets: UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8))

indicatorClass

指示器视图的类型。如果您想使用自己的PagingIndicatorView子类,请覆盖此属性。

默认值:PagingIndicatorView.self

indicatorColor

指示器视图的背景颜色。

默认值:UIColor(red: 3/255, green: 125/255, blue: 233/255, alpha: 1)

borderOptions

在菜单项底部添加边框。边框将与所有菜单项一样宽。内边距仅水平应用。

enum PagingBorderOptions {
  case hidden
  case visible(
    height: CGFloat,
    zIndex: Int,
    insets: UIEdgeInsets)
}

默认值:

.visible(
  height: 1,
  zIndex: Int.max - 1,
  insets: UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8))

borderClass

边框视图的类型。如果您想使用自己的PagingBorderView子类,请覆盖此属性。

默认值:PagingBorderView.self

borderColor

边框视图的背景颜色。

默认值:UIColor(white: 0.9, alpha: 1)

includeSafeAreaInsets

根据.safeAreaInsets属性更新菜单项的内容内边距。

默认值:true

font

菜单项上标题标签使用的字体。

默认值:UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium)

selectedFont

当前选定菜单项上标题标签使用的字体。

默认值:UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.medium)

textColor

菜单项上标题标签的颜色。

默认值:UIColor.black

selectedTextColor

当前选定菜单项的文本颜色。

默认值:UIColor(red: 3/255, green: 125/255, blue: 233/255, alpha: 1)

backgroundColor

菜单项的背景颜色。

默认值:UIColor.white

selectedBackgroundColor

选定菜单项的背景颜色。

默认值:UIColor.clear

menuBackgroundColor

菜单项后面视图的背景颜色。

默认值:UIColor.white

安装

Parchment 将与 Swift 的最新公开发布版本兼容。

要求

  • iOS 12.0+
  • Xcode 14.0+

CocoaPods

Parchment 可通过 CocoaPods 获得。要安装它,请将以下内容添加到您的 Podfile

pod 'Parchment', '~> 4.0'

Swift Package Manager

Parchment 可通过 Swift Package Manager 获得。将 Parchment 作为依赖项添加到您的 Package.swift

.package(url: "https://github.com/rechsteiner/Parchment", from: "4.0.0")

Carthage

Parchment 也支持 Carthage。要安装它,请将以下内容添加到您的 Cartfile

github "rechsteiner/Parchment" ~> 4.0

有关使用 Carthage 的更多详细信息,请参阅本指南

更新日志

这可以在 CHANGELOG 文件中找到。

许可证

Parchment 根据 MIT 许可证发布。有关详细信息,请参阅 LICENSE

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

稿定AI

稿定设计 是一个多功能的在线设计和创意平台,提供广泛的设计工具和资源,以满足不同用户的需求。从专业的图形设计师到普通用户,无论是进行图片处理、智能抠图、H5页面制作还是视频剪辑,稿定设计都能提供简单、高效的解决方案。该平台以其用户友好的界面和强大的功能集合,帮助用户轻松实现创意设计。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号