From 8cb00ec42d6095be0677fcde78f81f1e55908496 Mon Sep 17 00:00:00 2001 From: Robert Nash Date: Mon, 30 Jan 2023 02:00:26 +0000 Subject: [PATCH] Update README. --- README.md | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 148b05a..3025546 100644 --- a/README.md +++ b/README.md @@ -1 +1,128 @@ -# accordion-table \ No newline at end of file +# AccordionTable + +A wrapper for a diffable data source which facilitates collapsible table view sections. You may want to disable floating headers (see [demo](https://github.com/nashysolutions/MeltingList) app). + + + +## Implementation + +The following steps are for a typical table view implementation. + +### Prepare a model + +
+ Example + + ```swift + struct Food: Hashable { + let id: UUID + let title: String + let items: [Item] // rows + } + + struct Item: Hashable { + let id: UUID + let title: String + } + ``` +
+ +### Prepare an instance of `AccordionTable`. + +
+ Show me + + ```swift + let tableDataSource = UITableViewDiffableDataSource( + tableView: tableView, + cellProvider: cellProvider + ) + + let diffableTableManager = AccordionTable( + dataSource: tableDataSource, + headerProvider: headerProvider + ) + ``` +
+ +> The `AccordionTable` type also has an `enabledFeatures` parameter. + +### Prepare a table view delegate. + +
+ Show me + + ```swift + class TypicalTableViewDelegate: NSObject, UITableViewDelegate { + + let tableManager: AccordionTable + + init(_ tableManager: AccordionTable) { + self.tableManager = tableManager + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + tableManager.viewForHeader(in: tableView, at: section) + } + + func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + tableManager.selectRowIfNeeded(in: tableView, at: indexPath) + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let isSelected = tableManager.toggleSelectedStateForRow(at: indexPath) else { + return + } + if !isSelected { + tableView.deselectRow(at: indexPath, animated: true) + } + } + + func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { + tableManager.saveDeselectedStateForRow(at: indexPath) + } + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return 0 // your header height + } + } + ``` +
+ +### Map your data + +```swift +let data = [Food: [Item]]() +``` + +## Usage + +To reload the data, call the following on your instance of `AccordionTable`. + +```swift +func update(with data: [Food, [Item]], animated: true) +``` + +> On initial load, pass `animated: false`. + +To programmatically select a row. + +```swift +diffableTableManager.saveSelectedStateForRow(at: indexPath) +tableView.selectRow(at: indexPath, animated: animated, scrollPosition: scrollPosition) +``` + +To programmatically deselect a row. + +```swift +diffableTableManager.saveDeselectedStateForRow(at: indexPath) +tableView.deselectRow(at: indexPath, animated: animated) +``` + +## Side Note + +Consider avoiding any user interface state management in your model structures (and hashable implementation), such as `isHighlighted`, as this will be destroyed per snapshot (use a backing store instead, such as a `Set` or `key/value` collection - see [demo](https://github.com/nashysolutions/MeltingList) app). + +If your user interface is reloading more rows/sections than it should, it is likely the hashable implementation to blame. It seems each row must be unique within the entire dataset (not just within the section it belongs). Use GUID's on your dataset to achieve this. Alternatively, at the row level, make a reference to the section in which that row belongs (be careful of retain cycle when using classes) and use that in the hashable implementation of the row. + +> If you need to use `reloadItems(_ identifiers: [ItemIdentifierType])` then you will need to use class's for your model.