测试已测试 | ✗ |
语言语言 | SwiftSwift |
许可证 | MIT |
发布上次发布 | 2017 年 10 月 |
SwiftSwift 版本 | 3.0 |
SPM支持 SPM | ✓ |
由 Daniele Margutti 维护。
★★ 给 Flow 点赞以帮助该项目! ★★
由 Daniele Margutti (@danielemargutti) 创建。
Flow 是一个 Swift 轻量级库,可帮助您更好地管理 UITableViews 中的内容。
它简单快捷,完全符合 Swift 的类型安全性。
再见!UITableViewDataSource
和 UITableViewDelegate
:只需声明和设置您的数据,然后让 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。performBatchUpdates
的 UICollectionView
,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管理。
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
.
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:
public typealias T = MyClass
)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:
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).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:
public static var defaultHeight: CGFloat? = ...
in your cell subclass.estimatedHeight
or evaluateEstimatedHeight()
function.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).
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)
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) })
Section
and manage header/footerIf 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 sectionheaderTitle
/ 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
animationsFlow 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:
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 })
Row
/Cell EventsFlow 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文档。
Flow的当前版本为0.8.1。
完整变更日志可在CHANGELOG.MD文件中找到。
CocoaPods是Objective-C的依赖关系管理器,它自动简化了在项目中使用像Flow这样的第三方库的过程。您可以通过以下命令安装它:
$ gem install cocoapods
构建Flow需要CocoaPods 1.0.1+。
要使用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的基本需求是
Flow是由Daniele Margutti创建和维护的;您可以通过[email protected]或推特上的@danielemargutti与我联系。
本库基于MIT许可协议。
如果您在您的软件中使用它
Flow for UITableViews - © 2017 Daniele Margutti - www.danielemargutti.com
创建和维护库需要时间,作为开发者,您比任何人都了解这一点。
如果您想为这个项目的开发做出贡献或表示感谢,请考虑通过PayPal进行小额捐赠。
进行小额捐赠 并支持项目。