Skip to content
This repository was archived by the owner on Jan 15, 2026. It is now read-only.

Commit c31c4e2

Browse files
Merge pull request #93 from mansbernhardt/section-reusable
Adds new `HeaderFooterReusable` protocol to allow providing separate `Reus…
2 parents 5242f3f + 502b639 commit c31c4e2

10 files changed

Lines changed: 93 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 1.7.0
2+
3+
- Adds new `HeaderFooterReusable` protocol to allow providing separate `Reusable` types for rendering a section's header and footer.
4+
- Adds letter spacing and line height to `TextStyle`.
5+
- Adds target offset to `willEndDragging` signal of `ScrollViewDelegate`.
6+
- Adds will display cell signal to `CollectionViewDelegate`.
7+
18
## 1.6.3
29
- Performace. Added custom `count` implementation for `TableSection` to improve performance of e.g. `Table.isValidIndex` that might be called a lot for large tables.
310

Documentation/Tables.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,28 @@ If you are ok with losing type information you can also consider using the `Mixe
7777
var mixedTable = Table<(), MixedReusable>(rows: [.init(1), .init("A"), .init("B"), .init(2)])
7878
```
7979

80+
## HeaderFooterReusable
81+
82+
If you conform your `Table`'s `Section` type to `Reusable` a table's section will be rendered by the view provided by `makeAndConfigure()`. However if you like to provide both a header and a footer view from your section model data, you can conform your `Section` type to `HeaderFooterReusable` and provide separate header and footer types for rendering:
83+
84+
```swift
85+
struct MySection { ... }
86+
87+
extension MySection: HeaderFooterReusable {
88+
var header: MyHeaderType { ... }
89+
var footer: MyFooterType { ... }
90+
}
91+
```
92+
93+
As it is common with header and footer that are just strings, Form includes the `HeaderFooter` type for your convenience:
94+
95+
```swift
96+
struct HeaderFooter: HeaderFooterReusable, Hashable {
97+
var header: String
98+
var footer: String
99+
}
100+
```
101+
80102
## TableKit
81103

82104
Once you have your data in a `Table` and your `Row` and `Section` types conforming to `Reusable`, you can construct a `TableKit` that will provide a `UITableView` set up with a proper data source and delegate:

Examples/Demo/Example/Contents.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ extension UIViewController {
116116
bag += section.appendRow(title: "TableKit and Nested Either Reusable").append(.chevron).onValueDisposePrevious {
117117
present { $0.presentTableUsingKitAndNestedEitherReusable(style: style) }
118118
}
119+
120+
bag += section.appendRow(title: "TableKit and HeaderFooterReusable").append(.chevron).onValueDisposePrevious {
121+
present { $0.presentTableUsingKitAndHeaderFooterReusable(style: style) }
122+
}
119123
}
120124

121125
bag += self.install(form)

Examples/Demo/Example/CustomStyle.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ extension TextStyle {
8686
static let regularButton = TextStyle(font: .regularButton, color: .mintGreenDark, alignment: .center)
8787
static let disabledButton = TextStyle(font: .regularButton, color: .textGray, alignment: .center)
8888
static let whiteButton = TextStyle(font: .regularButton, color: .white, alignment: .center)
89-
static let headerText = TextStyle(font: .headerText, color: .textGray).restyled { $0.kerning = 0.8 }.uppercased
89+
static let headerText = TextStyle(font: .headerText, color: .textGray).restyled { $0.letterSpacing = 0.8 }.uppercased
9090
static let footer = smallText.centerAligned.multilined()
9191
static let headerBlack = headerText.colored(.black).multilined()
9292
static let header = headerText.multilined()

Examples/Demo/Example/Tables.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,21 @@ extension UIViewController {
195195

196196
return bag
197197
}
198+
199+
func presentTableUsingKitAndHeaderFooterReusable(style: DynamicTableViewFormStyle) -> Disposable {
200+
displayableTitle = "TableKit and HeaderFooterReusable"
201+
let bag = DisposeBag()
202+
203+
let tableKit = TableKit(table: sectionTable, style: style, bag: bag)
204+
bag += self.install(tableKit.view)
205+
206+
bag += self.navigationItem.addItem(UIBarButtonItem(title: "Swap"), position: .right).onValue {
207+
swap(&sectionTable, &swapSectionTable)
208+
tableKit.set(sectionTable)
209+
}
210+
211+
return bag
212+
}
198213
}
199214

200215
private var table = Table(sections: [("Header 1", 0..<5), ("Header 2", 5..<10)])
@@ -220,3 +235,6 @@ extension Double: Reusable {
220235
})
221236
}
222237
}
238+
239+
private var sectionTable = Table(sections: [(HeaderFooter(header: "Header 1", footer: "Footer 1"), 0..<5), (HeaderFooter(header: "Header 2", footer: "Footer 2"), 5..<10)])
240+
private var swapSectionTable = Table(sections: [(HeaderFooter(header: "Header 1", footer: "Footer 1"), 0..<2), (HeaderFooter(header: "Header 1b", footer: "Footer 1b"), 3..<7), (HeaderFooter(header: "Header 2", footer: "Footer 2"), 7..<10)])

Form/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<key>CFBundlePackageType</key>
1616
<string>FMWK</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>1.6.3</string>
18+
<string>1.7.0</string>
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>

Form/Reusable.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,16 @@ extension Either: Reusable where Left: Reusable, Right: Reusable, Left.ReuseType
147147
}
148148
}
149149
}
150+
151+
/// Conforming types specifies what `Reusable` conforming types to use for a section's header and footer.
152+
/// If you need both a header and footer you typically conforms your `Table`'s `Section` type to this protocol.
153+
public protocol HeaderFooterReusable {
154+
associatedtype Header: Reusable
155+
associatedtype Footer: Reusable
156+
157+
/// The value use to render a section's header
158+
var header: Header { get }
159+
160+
/// The value use to render a section's footer
161+
var footer: Footer { get }
162+
}

Form/TableKit.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,22 @@ public extension TableKit where Row: Reusable, Row.ReuseType: ViewRepresentable,
251251
}
252252
}
253253

254+
public extension TableKit where Row: Reusable, Row.ReuseType: ViewRepresentable, Section: HeaderFooterReusable, Section.Header.ReuseType: ViewRepresentable, Section.Footer.ReuseType: ViewRepresentable {
255+
/// Creates a new instance
256+
/// - Parameters:
257+
/// - table: The initial table. Defaults to an empty table.
258+
/// - bag: A bag used to add table kit activities.
259+
convenience init(table: Table = Table(), style: DynamicTableViewFormStyle = .default, view: UITableView? = nil, bag: DisposeBag) {
260+
self.init(table: table, style: style, view: view, bag: bag, headerForSection: { table, section in
261+
table.dequeueHeaderFooterView(forItem: section.header, style: style.header, formStyle: style.form, reuseIdentifier: "header")
262+
}, footerForSection: { table, section in
263+
table.dequeueHeaderFooterView(forItem: section.footer, style: style.footer, formStyle: style.form, reuseIdentifier: "footer")
264+
}, cellForRow: { table, row in
265+
table.dequeueCell(forItem: row, style: style)
266+
})
267+
}
268+
}
269+
254270
extension TableKit: SignalProvider {
255271
public var providedSignal: ReadWriteSignal<Table> {
256272
return ReadSignal(capturing: self.table, callbacker: callbacker).writable(signalOnSet: true) { self.table = $0 }

Form/UITableViewHeaderFooterView+Utilities.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,16 @@ extension String: Reusable {
146146
}
147147
}
148148

149+
public struct HeaderFooter: HeaderFooterReusable, Hashable {
150+
public var header: String
151+
public var footer: String
152+
153+
public init(header: String, footer: String) {
154+
self.header = header
155+
self.footer = footer
156+
}
157+
}
158+
149159
public struct DateHeader: Equatable {
150160
public let date: Date
151161
public let dateFormatter: DateFormatter

FormFramework.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "FormFramework"
3-
s.version = "1.6.3"
3+
s.version = "1.7.0"
44
s.module_name = "Form"
55
s.summary = "Powerful iOS layout and styling"
66
s.description = <<-DESC

0 commit comments

Comments
 (0)