PagingKit 1.18.2

PagingKit 1.18.2

测试已测试
Lang语言 SwiftSwift
许可证 MIT
Released最后发布2023年3月
SPM支持SPM

Kazuhiro Hayashi维护。



PagingKit 1.18.2

  • 作者
  • Kazuhiro Hayashi

img

Platform Swift 5.1 License Version Carthage compatible Swift Package Manager compatible

PagingKit为自定义菜单和内容UI提供。它比其他库有更灵活的布局和设计。

这是什么?

有许多库提供“分页UI”,它们都有菜单和内容区域。它们很方便,但不可自定义,因为您的应用程序必须与库的布局和视图组件兼容。当您的应用程序的UI设计不适合库时,您需要分叉它们或寻找另一个。

PagingKit比其他库有更灵活的布局和设计。您可以构建“菜单”和“内容”UI,并且它们可以一起使用。这就是这个库提供的所有功能。您可以按您喜欢的任何设计将任何设计适合到您的应用程序。

paging_sample

定制布局

您可以按您喜欢的方式对齐视图。

更改位置 将视图放置在内容和菜单之间 在右侧添加浮动按钮 在导航栏上
sample_5 sample_4 sample6 2018-12-04 10 00 51

定制菜单设计

您可以按您喜欢的任何方式自定义菜单。

标签样式菜单 高亮文本菜单 下划线菜单 App Store应用样式指示器
sample_3 sample_1 indicator

我在寻找您自定义菜单设计的拉取请求 :)

特性

  • 易于构建分页UI
  • 可定制布局和设计
  • 类似UIKit的API
  • 支持iPhone、iPad和iPhone X

需求

  • iOS 9.0+
  • Xcode 11.0+
  • Swift 5.1

安装

Swift 包管理器

打开 Swift 包文件(位于构建设置旁边)。您可以从此标签页添加和删除包。

请参见将包依赖添加到您的应用程序

CocoaPods

  • 安装 CocoaPods
> gem install cocoapods
> pod setup
  • 创建 Podfile
> pod init
  • 编辑 Podfile
target 'YourProject' do
  use_frameworks!

  pod "PagingKit" # add

  target 'YourProject' do
    inherit! :search_paths
  end

  target 'YourProject' do
    inherit! :search_paths
  end

end
  • 安装
> pod install

打开 .xcworkspace

Cartfile

  • 使用 Homebrew 安装 Carthage
> ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
> brew update
> brew install carthage
  • 移动您的项目目录并创建 Cartfile
> touch Cartfile
  • 在 Cartfile 中添加以下行
github "kazuhiro4949/PagingKit"
  • 创建框架
> carthage update --platform iOS
  • 在Xcode中,转到“生成 > 生成过程 > 链接框架和库”
  • 将框架添加到您的项目中
  • 添加一个新的运行脚本并将以下代码放入其中
/usr/local/bin/carthage copy-frameworks
  • 在“输入文件”处单击"+"并添加框架路径
$(SRCROOT)/Carthage/Build/iOS/PagingKit.framework
  • 在源文件上写入导入语句
import PagingKit

入门指南

本库中包含一些示例。

https://github.com/kazuhiro4949/PagingKit/tree/master/iOS%20Sample/iOS%20Sample

您可以将PagingKit按示例那样集成到您的项目中。查看此存储库并打开工作区。

PagingKit有两个核心类。

  • PagingMenuViewController
  • PagingContentViewController

PagingMenuViewController为每个内容提供交互式菜单。PagingContentViewController在滚动视图中提供内容。

如果您创建了一个包含PagingKit的新项目,请按照以下步骤操作。

  1. 添加PagingMenuViewController和PagingContentViewController
  2. 将它们分配到属性
  3. 创建菜单UI
  4. 显示数据
  5. 同步菜单和内容视图控制器

1. 添加PagingMenuViewController和PagingContentViewController

首先,请在Storyboard中的容器视图中添加PagingMenuViewController和PagingContentViewController。

1. 将容器视图放在Storyboard中

为每个视图控制器将容器视图放在Storyboard中。

2017-08-25 16 33 51

2. 更改类名

在自定义类设置中输入PagingMenuViewController。2017-08-25 16 36 36

在自定义类设置中输入PagingContentViewController。

2017-08-25 16 36 54

2. 将它们分配给属性

在容器的视图控制器代码中分配它们。

1. 为视图控制器声明属性

在容器视图控制器中声明属性。

class ViewController: UIViewController {
    
    var menuViewController: PagingMenuViewController!
    var contentViewController: PagingContentViewController!

2. 覆盖 prepare(segue:sender:) 并分配视图控制器

将视图控制器分配给 prepare(segue:sender:) 中的每个属性。

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let vc = segue.destination as? PagingMenuViewController {
            menuViewController = vc
        } else if let vc = segue.destination as? PagingContentViewController {
            contentViewController = vc
        }
    }

3. 构建应用

更改菜单颜色。 2017-08-25 17 47 58

构建并检查当前状态。

2017-08-25 17 47 29

显示了包含 PagingMenuViewController 和 PagingContentViewController 的容器视图控制器。

3. 创建菜单 UI

接下来,你需要准备菜单元素。

1. 继承PagingMenuViewCell并创建自定义单元格

PagingKit包含PagingMenuViewCell。PagingMenuViewController使用它来构建每个菜单元素。

import UIKit
import PagingKit

class MenuCell: PagingMenuViewCell {
    @IBOutlet weak var titleLabel: UILabel!
}

2017-08-25 16 56 56

2. 继承PagingFocusView并创建自定义视图

PagingKit有PagingFocusView。PagingMenuViewController使用它来指示当前的焦点菜单。

2017-08-25 16 59 07

3. 将上述视图注册到PagingMenuViewController

class ViewController: UIViewController {
    
    var menuViewController: PagingMenuViewController!
    var contentViewController: PagingContentViewController!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        menuViewController.register(nib: UINib(nibName: "MenuCell", bundle: nil), forCellWithReuseIdentifier: "MenuCell")
        menuViewController.registerFocusView(nib: UINib(nibName: "FocusView", bundle: nil))
    }

4. 显示数据

然后,实现数据源来显示内容。它们类似于UITableViewDataSource。

1. 准备数据

class ViewController: UIViewController {
    static var viewController: (UIColor) -> UIViewController = { (color) in
       let vc = UIViewController()
        vc.view.backgroundColor = color
        return vc
    }
    
    var dataSource = [(menuTitle: "test1", vc: viewController(.red)), (menuTitle: "test2", vc: viewController(.blue)), (menuTitle: "test3", vc: viewController(.yellow))]

2. 设置菜单数据源

返回菜单数量、菜单宽度和PagingMenuViewCell对象。

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let vc = segue.destination as? PagingMenuViewController {
            menuViewController = vc
            menuViewController.dataSource = self // <- set menu data source
        } else if let vc = segue.destination as? PagingContentViewController {
            contentViewController = vc
        }
    }
}

extension ViewController: PagingMenuViewControllerDataSource {
    func numberOfItemsForMenuViewController(viewController: PagingMenuViewController) -> Int {
        return dataSource.count
    }
    
    func menuViewController(viewController: PagingMenuViewController, widthForItemAt index: Int) -> CGFloat {
        return 100
    }
    
    func menuViewController(viewController: PagingMenuViewController, cellForItemAt index: Int) -> PagingMenuViewCell {
        let cell = viewController.dequeueReusableCell(withReuseIdentifier: "MenuCell", for: index) as! MenuCell
        cell.titleLabel.text = dataSource[index].menuTitle
        return cell
    }
}

3. 配置内容数据源

返回内容和视图控制器数量。

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let vc = segue.destination as? PagingMenuViewController {
            menuViewController = vc
            menuViewController.dataSource = self
        } else if let vc = segue.destination as? PagingContentViewController {
            contentViewController = vc
            contentViewController.dataSource = self // <- set content data source
        }
    }
}

extension ViewController: PagingContentViewControllerDataSource {
    func numberOfItemsForContentViewController(viewController: PagingContentViewController) -> Int {
        return dataSource.count
    }
    
    func contentViewController(viewController: PagingContentViewController, viewControllerAt index: Int) -> UIViewController {
        return dataSource[index].vc
    }
}

4. 加载UI

从起始点开始调用reloadData()方法。

    override func viewDidLoad() {
        super.viewDidLoad()
        //...
        //...
        menuViewController.reloadData()
        contentViewController.reloadData()
    }

构建和显示数据源。

2017-08-25 17 54 30

5. 同步菜单和内容视图控制器

最后,同步菜单与内容之间的用户交互。

1. 设置菜单代理

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let vc = segue.destination as? PagingMenuViewController {
            menuViewController = vc
            menuViewController.dataSource = self
            menuViewController.delegate = self // <- set menu delegate
        } else if let vc = segue.destination as? PagingContentViewController {
            contentViewController = vc
            contentViewController.dataSource = self
        }
    }
}

实现菜单代理来处理事件。它与UITableViewDelegate相似。您需要在不率分页内容视图控制器的代理方法中实现滚动方法。

extension ViewController: PagingMenuViewControllerDelegate {
    func menuViewController(viewController: PagingMenuViewController, didSelect page: Int, previousPage: Int) {
        contentViewController.scroll(to: page, animated: true)
    }
}

2. 设置内容代理

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let vc = segue.destination as? PagingMenuViewController {
            menuViewController = vc
            menuViewController.dataSource = self
            menuViewController.delegate = self
        } else if let vc = segue.destination as? PagingContentViewController {
            contentViewController = vc
            contentViewController.dataSource = self
            contentViewController.delegate = self // <- set content delegate
        }
    }
}

实现内容代理来处理事件。它与UIScrollViewDelegate类似。您需要实现分页菜单视图控制器的滚动事件。其中,“percent”是从“index”参数到右侧页面索引的距离。

extension ViewController: PagingContentViewControllerDelegate {
    func contentViewController(viewController: PagingContentViewController, didManualScrollOn index: Int, percent: CGFloat) {
        menuViewController.scroll(index: index, percent: percent, animated: false)
    }
}

就是这样。

提示

类设计

在这个库中存在一些设计策略。

  • 行为需要由库指定。
  • 布局应留给开发人员。
  • 内部组件的排列必须留给开发人员。

正因为如此,PagingKit负责行为。但它不指定组件的结构。PagingKit更喜欢组合而不是继承。此图描述了类图的概览。

与RxSwift一起工作

PagingKit与RxSwift配合良好。

https://github.com/kazuhiro4949/RxPagingKit

    let items = PublishSubject<[(menu: String, width: CGFloat, content: UIViewController)]>()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        menuViewController?.register(type: TitleLabelMenuViewCell.self, forCellWithReuseIdentifier: "identifier")
        menuViewController?.registerFocusView(view: UnderlineFocusView())
        
        // PagingMenuViewControllerDataSource
        items.asObserver()
            .map { items in items.map({ ($0.menu, $0.width) }) }
            .bind(to: menuViewController.rx.items(
                cellIdentifier: "identifier",
                cellType: TitleLabelMenuViewCell.self)
            ) { _, model, cell in
                cell.titleLabel.text = model
            }
            .disposed(by: disposeBug)
        
        // PagingContentViewControllerDataSource
        items.asObserver()
            .map { items in items.map({ $0.content }) }
            .bind(to: contentViewController.rx.viewControllers())
            .disposed(by: disposeBug)
        
        // PagingMenuViewControllerDelegate
        menuViewController.rx.itemSelected.asObservable().subscribe(onNext: { [weak self] (page, prev) in
            self?.contentViewController.scroll(to: page, animated: true)
        }).disposed(by: disposeBug)
        
        // PagingContentViewControllerDelegate
        contentViewController.rx.didManualScroll.asObservable().subscribe(onNext: { [weak self] (index, percent) in
            self?.menuViewController.scroll(index: index, percent: percent, animated: false)
        }).disposed(by: disposeBug)
    }

许可证

版权所有(c)2017 Kazuhiro Hayashi

任何人如获得本软件及其相关文档文件(以下简称“软件”)的副本,均有权无偿处理该软件,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件的副本,并允许向软件提供副本的人以同样的方式做,只要遵守以下条件

上述版权声明和本许可协议应包含在软件的所有副本或主要部分中。

本软件按“原样”提供,不提供任何明示或暗示的保证,包括但不限于对适销性、针对特定目的的适用性和非侵权性的保证。在任何情况下,作者或版权持有者均不对任何索赔、损害或其他责任负责,无论这种责任是基于合同、侵权或其他法律依据,无论这种责任产生于、源于或与本软件或其使用或其他交易有关。