Skip to content

Commit

Permalink
Update README.
Browse files Browse the repository at this point in the history
  • Loading branch information
nashysolutions committed Jan 30, 2023
1 parent f18395b commit 8cb00ec
Showing 1 changed file with 128 additions and 1 deletion.
129 changes: 128 additions & 1 deletion README.md
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.

0 comments on commit 8cb00ec

Please sign in to comment.