FlowTables 0.8.2

FlowTables 0.8.2

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布上次发布2017 年 10 月
SwiftSwift 版本3.0
SPM支持 SPM

Daniele Margutti 维护。



Flow

Flow




★★ 给 Flow 点赞以帮助该项目! ★★

支持该项目。 现在捐赠。

Daniele Margutti (@danielemargutti) 创建。

Flow 是一个 Swift 轻量级库,可帮助您更好地管理 UITableViews 中的内容。
它简单快捷,完全符合 Swift 的类型安全性。

再见!UITableViewDataSourceUITableViewDelegate :只需声明和设置您的数据,然后让 Flow 处理其余部分!

您能做什么

以下代码是创建一个完整TableView的唯一必需代码,该TableView显示一些足球运动员的列表。
每个球员由一个名为 PlayerModel 的类(模型)表示;该实例由 PlayerCell UITableViewCell 子类在 table VIEW 中表示。

let players: [PlayerModel] = ... // your array of players
let rows = Row<PlayerCell>.create(players, { row in // create rows
row.onTap = { _, path in // reponds to tap on cells
  print("Tap on '\(row.item.fullName)'")
  return nil
}
tableManager.add(rows: rows) // just add them to the table
tableManager.reloadData()

用几行代码就创建了一个完整的表格;感觉很棒,对吧?是的,还有很多。
您可以处理触摸事件,自定义编辑,轻松创建自定义页脚和页眉,并将内容简单管理,就像它是数组一样!。

有关此主题的完整文章,请参阅此处
“忘记数据源和委托:UITableView 的新方法”

主要功能

Flow 的主要功能包括

  • 声明内容:决定单元格的类、模型,并使用类似数组的方法添加/删除或管理表格中的行。不再需要数据源,不再需要委托,只需直观的方法来管理要显示的数据类型(包含自动动画!)。
  • 关注点分离:让单元格做它应该做的事情;将表示项(模型)传递给单元格可以添加一层分离,将您的模型、视图控制器和表示模型的单元格分开。停止在数据源函数的 tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) 中进行单元格填充。成为一个 SOLID
  • 类型安全:描述您的单元格类、表示模型,并让库为您处理。分配和配置是自动的:不再需要重用标识符字符串,不再需要取消队列操作,不再需要强制转换。
  • FP(函数式编程)样式。单元格配置简单;您可以观察事件并以函数式风格管理单元格的行为。
  • 自动布局支持:提供一个简单的机制来指定单元格的高度,或让类根据描述的约束来决定最佳值。
  • 动画:像 performBatchUpdatesUICollectionView,Flow 会自动管理在更改布局时在表上进行的动画。

您可能还喜欢的其他库

我还正在开发几个您可能喜欢的其他项目。
以下是一瞥

描述
SwiftDate 在Swift中管理日期/时区最简单的方法
Hydra 编写更好的异步代码:async/await & promises
SwiftRichString 在Swift中优雅又不痛苦的NSAttributedString
SwiftLocation 高效的位置管理器
Flow 在iOS中创建和管理表格的绝佳方式
SwiftMsgPack 快速的/高效的msgPack编译器/解码器


文档

主要架构

流程由四个不同的实体组成:

  • TableManager:唯一负责管理UITableView实例内容的表管理器。
  • Section:代表表格中的一个节。它管理行的列表以及可选的头和尾。
  • Row:代表节中的一个行;行链接到一对对象:模型(任何类;如不适用,则有效的Void)和细胞(符合DeclarativeCell协议的UITableViewCell子类)。
  • SectionView:一个节可能显示头/尾;这些对象可能是简单的String或自定义视图:SectionView。正如Row一样,SectionView与模型和视图(UITableViewHeaderFooterView的子类)相关联。

示例应用

一个实时的工作示例可以在FlowDemoApp目录中找到。它演示了如何在假社交网络应用的简单登录屏幕中使用Flow。查看它以了解Flow如何真正帮助您简化UITableView管理。

Create the TableManager

In order to use Flow you must set the ownership of a UITableView instance to an instance of TableManager:

self.tableManager = TableManager(table: self.table!)

From now the UITableView instance is backed by Flow; every change (add/remove/move rows or sections) must be done by calling appropriate methods of the TableManager itself or any child Section/Row.

Prepare a Cell (for Row)

A row is resposible to manage the model and its graphical representation (the cell instance).
To create a new Row instance you need to specify the model class received by the instance cell and the cell class to instantiate into the table.

While sometimes a model is not applicable (your cell maybe a simple static representation or its decorative), the cell class is mandatory.
The cell must be a subclass of UITableViewCell conforms to DeclarativeCell protocol.
This protocol defines at least two important properties:

  • the model assocated with the Cell (public typealias T = MyClass)
  • a method called right after the row’s cell is dequeued (public func configure(_: T, path: IndexPath))

This is an example of a PlayerCell which is responsible to display data for a single football player (class PlayerModel):

import UIKit import Flow public class PlayerCell: UITableViewCell, DeclarativeCell { // assign to the cell the model to be represented  public typealias T = PlayerModel // if your cell has a fixed height you can set it directly at class level as below  public static var defaultHeight: CGFloat? = 157.0 // this func is called when a new instance of the cell is dequeued  // and you need to fill the data with a model instance.  public func configure(_ player: PlayerMode, path: IndexPath) { self.playerName.text = player.firstName self.playerLast.text = player.lastName self.playerImage.setURL(player.avatarURL) // ... and so on  } }

If your cell does not need of a model you can assign public typealias T = Void.

User interface of the cell can be made in two ways:

  • Prototype (only in Storyboards): create a new prototype cell, assign the class to your class (here PlayerCell) and set the reuseIdentifier in IB to the same name of the class (again PlayerCell). By default Flow uses as identifier of the cell the same name of the class itself (you can change it by overriding reuseIdentifier static property).
  • External XIB File: create a new xib file with the same name of your cell class (here PlayerCell.xib) and drag an instance of UITableViewCell class as the only single top level object. Assign to it the name of your class and the reuseIdentifier.

Height of a cell can be set in differen ways:

  • If cell has a fixed height you can set it at class level by adding public static var defaultHeight: CGFloat? = ... in your cell subclass.
  • If cell is autosized you can evaluate the height in row configuration (see below) by providing a value into estimatedHeight or evaluateEstimatedHeight() function.

Prepare a Row

You can now create a new row to add into the table; a Row instance is created by passing the DeclarativeCell type and an instance of the model represented.

let ronaldo = PlayerModel("Christiano","Ronaldo",.forward) ... let row_ronaldo = Row<PlayerCell>(model: ronaldo, { row in // ... configuration })

If model is not applicable just pass Void() as model param.

Inside the callback you can configure the various aspect of the row behaviour and appearance.
All standard UITableView events can be overriden; a common event is onDequeue, called when Row‘s linked cell instance is dequeued and displayed. Anoter one (onTap) allows you to perform an action on cell’s tap event.
So, for example:

let row_ronaldo = Row<PlayerCell>(model: ronaldo, { row in row.onDequeue = { _ in row.cell?.fullNameLabel.text = ronaldo.fullName return nil // when nil is returned cell will be deselected automatically  } row.onTap = { _ in print("Tapped cell") } })

There are lots of other events you can set into the row configuration callback (onDelete,onSelect,onDeselect,onShouldHighlit and so on).

Prepare Rows for an array of model

When you have an array of model instances to represent, one for each Row, you can use create shortcut.
The following code create an array of Rows<PlayerCell> where each row receive the relative item from self.players array.

let players_rows = Row<PlayerCell>.create(self.players)

Add Rows into the table

Adding rows to a table is easy as call a simple add function.

self.tableManager.add(rows: players_rows) // add rows (by appending a new section) self.tableManager.reloadData() // apply changes

(Remember: when you add rows without specifing a section a new section is created automatically for you).

Please note: when you apply a change to a table (by using add, remove or move functions, both for Section or Row instances) you must call the reloadData() function in order to reflect changes in UI.

If you want to apply changes using standard table’s animations just call update() function; it allows you to specify a list of actions to perform. In this case reloadData() is called automatically for you and the engine evaluate what’s changed automatically (inserted/moved/removed rows/section).

The following example add a new section at the end of the table and remove the first:

self.tableManager?.update(animation: .automatic, { self.tableManager?.insert(section: profileDetailSection, at: profileSection!.index! + 1) self.tableManager?.remove(sectionAt: 0) })

Create Section and manage header/footer

If not specified sections are created automatically when you add rows into a table.
Section objects encapulate the following properties:

  • rows list of rows (RowProtocol) inside the section
  • headerTitle / headerView a plain header string or a custom header view (SectionView)
  • footerTitle / footerView a plain footer string or a custom footer view (SectionView)

Creating a new section with rows is pretty simple:

let rowPlayers: [RowProtocol] = ... let sectionPlayers = Section(id: SECTION_ID_PLAYERS, row: rowPlayers, headerTitle: "\(rowPlayers.count) PLAYERS")"

As like for Row even Section may have custom view for header or footer; in this case your custom header/footer must be an UITableViewHeaderFooterView subclass defined in a separate XIB file (with the same name of the class) which is conform to DeclarativeView protocol.
DeclarativeView protocol defines the model accepted by the custom view (as for Row you can use Void if not applicable).

For example:

import UIKit import Flow public class TeamSectionView: UITableViewHeaderFooterView, DeclarativeView { public typealias T = TeamModel // the model represented by the view, use `Void` if not applicable  public static var defaultHeight: CGFloat? = 100 public func configure(_ item: TeamModel, type: SectionType, section: Int) { self.sectionLabel?.text = item.name.uppercased() } }

Now you can create custom view as header for section:

let realMadridSection = Section(teamPlayers, headerView: SectionView<TeamSectionView>(team)) self.tableManager.add(section: realMadridSection) self.tableManager.reloadData()

UITableView animations

Flow fully supports animation for UITableView changes. As like for UICollectionView you will need to call a func which encapsulate the operations you want to apply.

In Flow it’s called update(animation: UITableViewRowAnimation, block: ((Void) -> (Void))).

Inside the block you can alter the sections of the table, remove or add rows and section or move things into other locations. At the end of the block Flow will take care to collect the animations needed to reflect applied changes both to the model and the UI and execute them.

You just need to remember only two things:

  • Deletes are processed before inserts in batch operations. This means the indexes for the deletions are processed relative to the indexes of the collection view’s state before the batch operation, and the indexes for the insertions are processed relative to the indexes of the state after all the deletions in the batch operation.
  • In order to make a correct refresh of the data, insertion must be done in order of the row index.

For example:

self.tableManager?.update(animation: .automatic, { self.tableManager?.remove(sectionAt: 1) // remove section 1  self.tableManager?.add(row: newPlayer, in: self.tableManager?.section(atIndex: 0)) // add a new row in section 0 })

Observe Row/Cell Events

Flow allows you to encapsulate the logic of your UITableViewCell instances directly in your Row objects. You can listen for dequeue, tap, manage highlights or edit… pratically everything you can do with plain tables, but more confortably.

All events are available and fully described into the Row class.
In this example you will see how to respond to the tap:

// Respond to tap on player's cells let rows = Row<PlayerCell>.create(players, { row in row.onTap = { _,path in print("Tap on player at \(String(path.row)): '\(row.item.fullName)'") return nil } })

所有可观察的事件在API SDK中描述。


完整SDK文档

完整方法文档既可在源代码中查看,也可在API_SDK文件中查看。
点击这里以阅读完整的SDK文档

变更日志

Flow的当前版本为0.8.1。
完整变更日志可在
CHANGELOG.MD文件中找到。

安装

通过CocoaPods安装

CocoaPods是Objective-C的依赖关系管理器,它自动简化了在项目中使用像Flow这样的第三方库的过程。您可以通过以下命令安装它:

$ gem install cocoapods

构建Flow需要CocoaPods 1.0.1+。

通过Podfile安装

要使用CocoaPods将Flow集成到您的Xcode项目中,请在您的Podfile中指定它:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'

target 'TargetName' do
  use_frameworks!
  pod 'FlowTables'
end

然后,运行以下命令

$ pod install

需求 & 许可证

Flow的基本需求是

我们同时支持CocoaPodsChartage

Flow是由Daniele Margutti创建和维护的;您可以通过[email protected]或推特上的@danielemargutti与我联系。

本库基于MIT许可协议

如果您在您的软件中使用它

  • 请在您的致谢/版权框中添加一个通知:Flow for UITableViews - © 2017 Daniele Margutti - www.danielemargutti.com
  • (可选但很受欢迎) 点击这里报告我 使用Flow的您的应用

支持项目

创建和维护库需要时间,作为开发者,您比任何人都了解这一点。

如果您想为这个项目的开发做出贡献或表示感谢,请考虑通过PayPal进行小额捐赠。

进行小额捐赠 并支持项目。

paypal