Jenga - 基于Swift ResultBuilder优雅的构建UITableView
🇨🇳
天朝子民
This framework allows you to build Table views using UIKit with syntax similar to SwiftUI. You can think about this as an improved UITableView
.
Features
- Use declarative chaining syntax to build lists Smooth coding experience Elegant and natural styling.
- Rich Cell type support, support system setting styles and custom types.
- Support
@propertyWrapper
, usestate
andbinding
to bind UI state - Support automatic calculation and row height
- Support automatic registration of Cell
- Continue to add more new features.
Screenshot
Installation
CocoaPods - Podfile
pod 'Jenga'
Swift Package Manager for Apple platforms
Select Xcode menu File > Swift Packages > Add Package Dependency
and enter repository URL with GUI.
Repository: https://github.com/fanglinwei/Jenga
Swift Package Manager
Add the following to the dependencies of your Package.swift
:
.package(url: "https://github.com/fanglinwei/Jenga", from: "version")
Usage
First make sure to import the framework:
import Jenga
How to initialize:
JengaEnvironment.isEnabledLog = true //日志
JengaEnvironment.setup(JengaProvider())
Then you just need short code to build UITableView
@TableBuilder
var tableBody: [Table] {
rows...
}
Here are some usage examples. All devices are also available as simulators:
DSLAutoTable
is recommended for fast builds:
import Jenga
class ViewController: UIViewController, DSLAutoTable {
@TableBuilder
var tableBody: [Table] {
TableSection {
NavigationRow("设置样式")
.onTap(on: self) { (self) in
self.navigationController?.pushViewController(SettingViewController(), animated: true)
}
NavigationRow("自定义Cell")
.onTap(on: self) { (self) in
self.navigationController?.pushViewController(CustomViewController(), animated: true)
}
}
}
}
preview:
Custom Cell:
@TableBuilder
var tableBody: [Table] {
TableSection {
TableRow<BannerCell>("image1")
.height(1184 / 2256 * (UIScreen.main.bounds.width - 32))
.customize { [weak self] cell in
cell.delegate = self
}
SpacerRow(10)
TableRow<BannerCell>()
.height(1540 / 2078 * (UIScreen.main.bounds.width - 32))
.data("image2")
.customize { (cell, value) in
print(cell, value)
}
}
.headerHeight(20)
}
preview:
State
and Binding
:
@State var text = "objective-c"
@State var detailText = "TableView"
@State var isHiddenCat = false
// DSL
@TableBuilder
var tableBody: [Table] {
TableSection {
NavigationRow($text)
.detailText($detailText)
ToggleRow("显示小猫", isOn: $isHiddenCat)
.onTap(on: self) { (self, isOn) in
self.isHiddenCat = isOn
}
}
.header("Toggle")
.rowHeight(52)
.headerHeight(UITableView.automaticDimension)
TableSection(binding: $isHiddenCat) { isOn in
NavigationRow("🐶")
NavigationRow("🐶")
NavigationRow("🐶")
if isOn {
NavigationRow("🐱")
NavigationRow("🐱")
NavigationRow("🐱")
}
}
.header("Animal")
.headerHeight(UITableView.automaticDimension)
}
Modify State
to update UI
text = "Swift"
detailText = "Jenga"
isShowCat = true
preview:
Section Binding:
@State var emojis: [String] = ["🐶", "🐱", "🐭", "🦁", "🐼"]
// DSL
@TableBuilder
var tableBody: [Table] {
TableSection(binding: $emojis) {
TableRow<EmojiCell>()
.data($0)
.height(44)
}
.headerHeight(UITableView.automaticDimension)
TableSection {
TapActionRow("Random")
.onTap(on: self) { (self) in
guard self.emojis.count > 3 else { return }
self.emojis[2] = randomEmojis[Int.random(in: 0 ... 4)]
self.emojis[3] = randomEmojis[Int.random(in: 0 ... 4)]
}
TapActionRow("+")
.onTap(on: self) { (self) in
self.emojis.append(randomEmojis[Int.random(in: 0 ... 4)])
}
TapActionRow("-")
.onTap(on: self) { (self) in
guard self.emojis.count > 0 else { return }
_ = self.emojis.popLast()
}
}
.headerHeight(UITableView.automaticDimension)
}
preview:
It is also possible not to use TableSection, but I am still weighing the pros and cons of this API approach:
@TableBuilder
var tableBody: [Table] {
TableHeader("我是头部")
NavigationRow("设置样式")
NavigationRow("自定义Cell")
NavigationRow("自定义TableView")
TableFooter("我是底部")
TableHeader("第二组")
.height(100)
NavigationRow("cell")
}
DSLAutoTable
创建的TableView
自定义struct JengaProvider: Jenga.JengaProvider {
func defaultTableView(with frame: CGRect) -> UITableView {
let tableView: UITableView
if #available(iOS 13.0, *) {
tableView = UITableView(frame: frame, style: .insetGrouped)
} else {
tableView = UITableView(frame: frame, style: .grouped)
}
return tableView
}
}
JengaEnvironment.setup(JengaProvider())
If you want to listen to UIScrollViewDelegate
or create your own TableView, you can't use DSLAutoTable
protocol
Just view CustomTableViewController
in Demo
-
TableDirector
lazy var table = TableDirector(tableView, delegate: self)
-
Describe TableBody using @TableBuilder
@TableBuilder var tableBody: [Table]] { TableSection(binding: $array) { TableRow<EmojiCell>() .data($0) .height(44) } .headerHeight(UITableView.automaticDimension) }
-
Update TableBody
table.set(sections: tableBody)
Done, your table is ready.
For more examples, see the sample application.
Cell height calculating strategy:
Implementation ideas come fromFDTemplateLayoutCell
You can set height to UITableView.highAutomaticDimension
to enable automatic calculation and cache row height
Just view AutoHeightViewController
in Demo
// row
NavigationRow()
.height(UITableView.highAutomaticDimension)
// section
TableSection {
rows...
}
.rowHeight(UITableView.highAutomaticDimension)
SystemRow
protocol provides chaining
Row | 描述 |
---|---|
text |
标题 |
detailText |
子标题(默认value1) |
detailText(.subtitle) |
子标题subtitle |
detailText(.value1) |
子标题value1 |
detailText(.value2) |
子标题value2 |
detailText(.none) |
子标题空样式 |
isOn |
开关 |
height |
行高 |
estimatedHeight |
预估行高 |
selectionStyle |
选中样式 |
onTap |
点击事件 |
customize |
自定义 |
Contributing
If you have the need for a specific feature that you want implemented or if you experienced a bug, please open an issue. If you extended the functionality of Jenga yourself and want others to use it too, please submit a pull request.
Thanks for inspiration
License
Jenga is under MIT license. See the LICENSE file for more info.