-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f18395b
commit 8cb00ec
Showing
1 changed file
with
128 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,128 @@ | ||
# accordion-table | ||
# 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). | ||
|
||
<img src="https://user-images.githubusercontent.com/64097812/150280515-64bbcc1b-ba85-4c56-bd4a-0b77be008e8c.gif" width="300"/> | ||
|
||
## Implementation | ||
|
||
The following steps are for a typical table view implementation. | ||
|
||
### Prepare a model | ||
|
||
<details> | ||
<summary>Example</summary> | ||
|
||
```swift | ||
struct Food: Hashable { | ||
let id: UUID | ||
let title: String | ||
let items: [Item] // rows | ||
} | ||
|
||
struct Item: Hashable { | ||
let id: UUID | ||
let title: String | ||
} | ||
``` | ||
</details> | ||
|
||
### Prepare an instance of `AccordionTable`. | ||
|
||
<details> | ||
<summary>Show me</summary> | ||
|
||
```swift | ||
let tableDataSource = UITableViewDiffableDataSource<Food, Item>( | ||
tableView: tableView, | ||
cellProvider: cellProvider | ||
) | ||
|
||
let diffableTableManager = AccordionTable<Food, Item>( | ||
dataSource: tableDataSource, | ||
headerProvider: headerProvider | ||
) | ||
``` | ||
</details> | ||
|
||
> The `AccordionTable` type also has an `enabledFeatures` parameter. | ||
### Prepare a table view delegate. | ||
|
||
<details> | ||
<summary>Show me</summary> | ||
|
||
```swift | ||
class TypicalTableViewDelegate: NSObject, UITableViewDelegate { | ||
|
||
let tableManager: AccordionTable<Food, Item> | ||
init(_ tableManager: AccordionTable<Food, Item>) { | ||
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 | ||
} | ||
} | ||
``` | ||
</details> | ||
|
||
### 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. |