{"id":16699853,"url":"https://github.com/malcommac/owl","last_synced_at":"2025-04-05T13:07:26.266Z","repository":{"id":54161264,"uuid":"184282181","full_name":"malcommac/Owl","owner":"malcommac","description":"A declarative type-safe framework for building fast and flexible lists with UITableViews \u0026 UICollectionViews","archived":false,"fork":false,"pushed_at":"2021-03-06T14:22:36.000Z","size":475,"stargazers_count":434,"open_issues_count":13,"forks_count":32,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-03-14T08:22:08.044Z","etag":null,"topics":["ios-swift","swift","swift-framework","swiftlang","uicollectionview","uikit","uitableview"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/malcommac.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"malcommac","custom":"https://www.paypal.com/paypalme2/danielemargutti"}},"created_at":"2019-04-30T14:56:40.000Z","updated_at":"2023-12-15T11:51:36.000Z","dependencies_parsed_at":"2022-08-13T08:00:40.037Z","dependency_job_id":null,"html_url":"https://github.com/malcommac/Owl","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FOwl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FOwl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FOwl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malcommac%2FOwl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/malcommac","download_url":"https://codeload.github.com/malcommac/Owl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247339158,"owners_count":20923014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ios-swift","swift","swift-framework","swiftlang","uicollectionview","uikit","uitableview"],"created_at":"2024-10-12T18:08:22.721Z","updated_at":"2025-04-05T13:07:26.242Z","avatar_url":"https://github.com/malcommac.png","language":"Swift","readme":"\u003cp align=\"left\" \u003e\n\u003cimg src=\"./owl_logo.png\" width=357px alt=\"SwiftDate\" title=\"Owl\"\u003e\n\u003c/p\u003e\n\nOwl offers a data-driven declarative approach for building fast \u0026 flexible list in iOS.\n\nIt supports both `UICollectionView` \u0026 `UITableView`; *`UIStackView` is on the way!*\n\n|  \t| Features Highlights \t|\n|---\t|---------------------------------------------------------------------------------\t|\n| 🕺 \t| No more delegates \u0026 datasource. Just a fully type-safe declarative content approach \t|\n| 🧩 \t| Better architecture to reuse components e decuple data from UI \t|\n| 🌈 \t| Animate content changes automatically, no `reloadData`/`performBatchUpdates` \t|\n| 🚀 \t| Blazing fast diff algorithm based upon [DifferenceKit](https://github.com/ra1028/DifferenceKit) \t|\n| 🧬 \t| It uses standard UIKit components at its core. No magic! \t|\n| 💎 \t| (COMING SOON) Support for scrollable declarative/fully customizable stack view \t|\n| 🐦 \t| Fully made in Swift from Swift ❥ lovers \t|\n\nOwl was created and maintaned by [Daniele Margutti](https://github.com/malcommac) - My home site [www.danielemargutti.com](https://www.danielemargutti.com).\n\n[![Version](https://img.shields.io/cocoapods/v/OwlKit.svg?style=flat)](http://cocoadocs.org/docsets/OwlKit) \n[![License](https://img.shields.io/cocoapods/l/OwlKit.svg?style=flat)](http://cocoadocs.org/docsets/OwlKit) \n[![Platform](https://img.shields.io/cocoapods/p/OwlKit.svg?style=flat)](http://cocoadocs.org/docsets/OwlKit)\n[![CI Status](https://travis-ci.org/malcommac/OwlKit.svg)](https://travis-ci.org/malcommac/OwlKit) \n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) \n![Twitter Follow](https://img.shields.io/twitter/follow/danielemargutti.svg?label=Follow\u0026style=flat-square)\n\n## Requirements\n\n- Xcode 9.0+\n- iOS 10.0+\n- Swift 5+\n\n## Installation\n\nThe preferred installation method is with CocoaPods. Add the following to your `Podfile`:\n\n```ruby\npod 'OwlKit'\n```\n\nAlso you can install Owl using Carthage. Add this to your `Cartfile`:\n\n```\ngithub \"malcommac/Owl\"\n```\n\nOwl is also compatible with SPM (Swift Package Manager) for Swift 5.x+\n\n## What you will get\n\nThis is how to achieve a fully functional Contacts list with Owl. It's just a silly example but you can create complex layout with heterogeneous models easily!\n\n```swift\ndirector = TableDirector(table: table)\n\n// An adapter encapsulate the logic to render a model (Contact) with a specific ui (ContactCell).\nlet contactAdapter = TableCellAdapter\u003cContact,ContactCell\u003e { dr in\n\n\t// Each adapter exposes all the standard methods of a list, events\n\t// and properties. ctx (context) received from event is a type-safe\n\t// object both for model and cell!\n\tdr.events.dequeue = { ctx in\n\t\t// element is type-safe, it's the Contact instance!\n\t\t// cell is type-safe, it's the ContactCell instance\n\t\tctx.cell?.item = ctx.element\n\t}\n\tdr.events.didSelect = { ctx in\n\t\tlet vc = ContactDetailVC(people: ctx.element)\n\t\tnavigationController.pushViewController(vc, animated: true)\n\t}\n\tdr.events.shouldHighlight = { ctx in\n\t\treturn ctx.element.isBlacklisted == false\n\t}\n}\n// Since now our table can show Contact istances using ContactCell\n// All events are received only in its adapter.\ndirector?.registerCellAdapter(contactAdapter) \n\n/// Manage your content in a declarative way!\nlet friendsSection = TableSection(elements: [john,mark,anita])\ndirector?.add(section: friendsSection)\ndirector?.reload()\n```\n\n## License\n\n- Owl is released under the Apache 2.0 License.\n- DifferenceKit is released under the Apache 2.0 License.\n- Owl Icon is made by \u003ca href=\"https://www.freepik.com/?__hstc=57440181.2ec9032a4253af7a6c076e26b17c0e4f.1556611478423.1556611478423.1556635522124.2\u0026__hssc=57440181.7.1556635522124\u0026__hsfp=2502665684\" title=\"Freepik\"\u003eFreepik\u003c/a\u003e from \u003ca href=\"https://www.flaticon.com/\" \t\t\t    title=\"Flaticon\"\u003ewww.flaticon.com\u003c/a\u003e is licensed by \u003ca href=\"http://creativecommons.org/licenses/by/3.0/\" \t\t\t    title=\"Creative Commons BY 3.0\" target=\"_blank\"\u003eCC 3.0 BY\u003c/a\u003e\u003c/div\u003e\n \n[Full License File](./LICENSE).\n\n\u003ca name=\"doc\"/\u003e\n\n## Documentation\n\n- 1 - [Introduction: Director \u0026 Adapters](#1)\n- 2 - [Getting Started](#2)\n- 3 - [How-To](#3)\n\t- [3.1 - Create Model](#3.1)\n\t- [3.2 - Create The Cell](#3.2)\n\t- [3.3 - Manage Self-Sized Cells](#3.3)\n\t- [3.4 - Load Cells/View from Storyboard/Xib/Class](#3.4)\n\t- [3.5 - Custom Section's Header/Footer (String/View based)](#3.5)\n\t- [3.6 - Reload data with automatic animations](#3.6)\n\t- [3.7 - Working with Headers \u0026 Footers](#3.7)\n- 4 - [APIs Doc: Manage `UITableView`](./Documentation/Manage_Tables.md)\n- 5 - [APIs Doc: Manage `UICollectionView`](./Documentation/Manage_Collections.md)\n- 6 - [Listen for `UIScrollViewDelegate` events](./Documentation/Manage_UIScrollViewDelegate_Events.md)\n\n\u003ca name=\"1\"/\u003e\n\n# 1.0 - Introduction: Director \u0026 Adapters\n\nAll the Owl's SDK is based upon two concepts: the **director** and the **adapter**.\n\n## Director\n\nThe **director** is the class which manage the content of a list, keep in sync data with UI and offers all the methods and properties to manage it. When you need to add, move or remove a section or a cell, change the header or a footer you find all the methods and properties in this class.\nA director instance can be associated with only one list (table or collection); once a director is assigned to a list it become the datasource and delegate of the object.\n\nThe following directors are available:\n\n- `TableDirector` used to manage `UITableView` instances\n- `CollectionDirector` and `FlowCollectionDirector` used to manage `UICollectionView` with custom or `UICollectionViewFlowLayout` layout.\n\n## Adapters\n\nOnce you have created a new director for a list it's time to declare what kind of models your list can accept. Each model is assigned to one UI element (`UITableViewCell` subclass for tables, `UICollectionViewCell` subclass for collections).\n\nThe scope of the adapter is to declare a pair of Model \u0026 UI a director can manage. \n\nThe entire framework is based to this concept: a model can be rendered by a single UI elements and its up to Owl to pick the right UI for a particular model instance.\n\nAn adapter is also the centrail point to receive events where a particular instance of a model is involved in. For example: when an user tap on a row for a model instance of type A, you will receive the event (along with the relevant info: index path, involved model instance etc.) inside the adapter which manage that model.\n\nYou will register as much adapters as models you have.\n\n\u003ca name=\"2\"/\u003e\n\n# 2.0 - Getting Started\n\nThe following code shows how to create a director to manage an `UITableView` (a much similar approach is used for `UICollectionView`).\n\n```swift\npublic class MyController: UIViewController {\n\t@IBOutlet public var table: UITableView!\n\t\n\tprivate var director: TableDirector?\n\t\n\tfunc viewDidLoad() {\n\t\tsuper.viewDidLoad()\n\t\tdirector = TableDirector(table: table)\n// ...\n```\n\nIt's now time to declare what kind of content should manage our director. For shake of simplicity we'll declare just an adapter but you can declare how much adapters you need (you can also create your own director with adapters inside and reuse them as you need. This is a neat approach to reuse and decupling data).\n\n```swift\nfunc viewDidLoad() {\n\t// ...\n\tlet contactAdpt = TableCellAdapter\u003cContact, ContactCell\u003e()\n\t\n\t// You can attach events for adapter configuration.\n\t// The following example show how to set the row height and dequeue\n\t\n\tcontactAdpt.events.rowHeight = { ctx in\n\t\treturn 60.0 // explicit row height\n\t}\n\t\n\tcontactAdpt.events.dequeue = { ctx in\n\t\t// this is the suggested behaviour; your cell should expose a\n\t\t// property of the type of the model it can be render and you will\n\t\t// assign it on dequeue. It's type safe too!!\n\t\tctx.cell?.contact = ctx.element\n\t}\n\tdirector?.registerCellAdapter(contactAdpt)\n```\n\nThis is minimum setup to render objects of type `Contact` using cell of type `ContactCell` using our director.\nYou can configure and attach tons of other properties and events (using the adapter's `.events` property); you will found a more detailed description of each property below but all `UITableView`/`UICollectionView`'s events and properties are supported.\n\nNow it's time to add some content to our table.\nAs we said Owl uses a declarative approach to content: this mean you set the content of a list by using imperative functions like `add`,`remove`,`move` both for sections and elements.\n\n```swift\n// You can add/remove/move elements at anytime in a declarative way\nlet contacts = [\n\tContact(first: \"John\", last: \"Doe\"),\n\tContact(first: \"Adam\", last: \"Best\"),\n\t...\n]\n// ...\ndirector?.add(elements: contacts)\ndirector?.remove(section: 1)\n```\n\nThere are a plenty list of methods to manage both sections and elements into section:\n\n```swift\ndirector?.firstSection?.removeAt(2) // remove an element\ndirector?.remove(section: 2) // remove section\ndirector?.section(\"mySection\").move(swappingAt: 0, with:1) // swap elements\ndirector?.add(elements: [...], at: 5) // add new elements\n// and more...\n```\nOnce you have changed your data models you can call `reload()` function to sync it with UI.\n\nThe following code create (automatically) a new `TableSection` with the `contacts` elements inside. If you need you can create a new section manually:\n\n```swift\n// Create a new section explicitly. Each section has an unique id you can assign\n// explicitly or leave Owl create an UUID for you. It's used for diff features.\nlet newSection = TableSection(id: \"mySectionId\", elements: contacts, header: \"\\(contacts) Contacts\", footer: nil)\ndirector?.add(section: newSection)\n```\n\nIn order to sync the UI just call the `director?.reload()`, et voilà!\nJust few lines of code to create a fully functional list!\n\nAt anytime you are able to add new sections, move or replace items just by using the methods inside the `TableSection`/`CollectionSection` or `TableDirector`/`CollectionDirector` instances then calling `reload()` to refresh the content.\n\nRefresh is made without animation, but Owl is also able to refresh the content of your data by picking the best animation based on a blazing fast diff algorithm which compare the data before/after your changes. More details in the next chapter. \n\nThe following code demostrate how to add swipe to delete functionality to an adapter:\n\n```swift\ncatalogAdapter.events.canEditRow = { ctx in\n  return true\n}\n        \ncatalogAdapter.events.deleteConfirmTitle = { ctx in\n  return \"Delete\"\n}\n        \ncatalogAdapter.events.commitEdit = { [weak self] ctx, style in\n  guard let indexPath = ctx.indexPath else { return }\n  \n  // By using the session to reload you will also receive events like didEndEditingCell\n  self?.tableDirector?.reload(afterUpdate: { dir in\n    dir.sections[indexPath.section].remove(at: indexPath.row)\n    return .none\n  }, completion: nil)\n}\n```\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"3\"/\u003e\n\n# 3.0 - How-To\n\n\u003ca name=\"3.1\"/\u003e\n\n## 3.1 - Create Model\n\nModels can be struct or classes; the only requirement is the conformance to `ElementRepresentable` protocol.\nThis protocol is used by the diff algorithm in order to evaluate the difference between changes applied to models and pick the best animation to show it.\n\nThis means you don't need to make any `performBatches` update neither to call `reloadRows/removeRows/addRows` methods manually... all these boring and crash-proned stuff are made automatically by Owl.\n\nThe following example show our `Contact` model declaration:\n\n```swift\npublic class Contact: ElementRepresentable {\n    let firstName: String\n    let lastName: String\n\n\tpublic var differenceIdentifier: String {\n\t\treturn \"\\(self.firstName)_\\(self.lastName)\"\n\t}\n\n\tpublic func isContentEqual(to other: Differentiable) -\u003e Bool {\n\t\tguard let other = other as? Contact else { return false }\n\t\treturn other == self\n\t}\n\n\tinit(first: String, last: String) {\n\t\tself.firstName = first\n\t\tself.lastName = last\n\t}\n}\n```\n\nProtocol conformance is made by adding:\n\n- `differenceIdentifier` property: An model needs to be uniquely identified to tell if there have been any insertions or deletions (it's the perfect place for a `uuid` property)\n- `isContentEqual(to:)` function: is used to check if any properties have changed, this is for replacement changes. If your model data change between reloads Owl updates the cell's UI accordingly.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"3.2\"/\u003e\n\n## 3.2 - Create the Cell\n\nThe second step is to create an UI representation of your model. Typically is a subclass of `UITableViewCell` or `UICollectionViewCell`.\n\n## Reuse Identifier\n\nCells must have as `reuseIdentifier` value the same name of the class itself (so `ContactCell` has also `ContactCell` as identifier; you can also configure it if you need but it's a good practice).\n\n## Best Practices\n\nYou don't need to conform any special protocol but, in order to keep your code clean, our suggestion is to create a public property which accepts the model instance and set it on adapter's `dequeue` event.\n\n```swift\npublic class ContactCell: UITableViewCell {\n\n\t// Your outlets\n    @IBOutlet public var ...\n\n\t// Define a property you set on adapter's dequeue event\n\tpublic var contact: Contact? {\n\t\tdidSet {\n           // setup your UI according with instance data\n\t\t}\n\t}\n}\n```\n\nIn your adapter:\n\n```swift\ncontactAdpt.events.dequeue = { ctx in\n\tctx.cell?.contact = ctx.element\n}\n```\n\n`ctx` is an object which includes all the necessary informations of an event, including type-safe instance of the model.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"3.3\"/\u003e\n\n## 3.3 - Manage Self-Sized Cells\n\nSelf-sized cells are easy to be configured, both for tables and collection views.\n\nOwl support easy cell sizing using autolayout. You can set the size of the cell by adapter or collection/table based. For autolayout driven cell sizing set the `rowHeight` (for `TableDirector`) or `itemSize` (for `CollectionDirector`/`FlowCollectionDirector`) to the autoLayout value, then provide an estimated value.\n\nAccepted values are:\n\n- `default`: you must provide the height (table) or size (collection) of the cell\n- `auto(estimated: CGFloat)`: uses autolayout to evaluate the height of the cell; for Collection Views you can also provide your own calculation by overriding `preferredLayoutAttributesFitting()` function in cell instance.\n- `explicit(CGFloat)`: provide a fixed height for all cell types (faster if you plan to have all cell sized same)\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"3.4\"/\u003e\n\n## 3.4 - Load Cells/View from Storyboard/Xib/Class\n\nThis is the behaviour used to load/dequeue cells and header/footer both for tables and collection views is:\n\n| Entity \t| Default Reusable Identifier \t| Default Loading Method \t|\n|--------------------------------------------------------------------------------\t|-----------------------------\t|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------\t|\n| Cells (`UITableViewCell`,`UICollectionViewCell`) \t| Name of the class \t| Load the UI with the reusable identifier from the parent table's storyboard cell prototypes list \t|\n| Header/Footer View (`UITableViewHeaderFooterView`, `UICollectionReusableView`) \t| Name of the class \t| Attempt to load the view as the root element of the xib with the same name of the class (ie. \"GroupHeaderView.xib\") contained in the same bundle of the class itself. \t|\n\nYou can however this behaviour by setting the following attributes before the adapter (`TableAdapter` or `TableHeaderFooterAdapter` for table's cells and header/footer, `CollectionAdapter`/`CollectionHeaderFooterAdapter ` for collections's cell and header/footer):\n\n- `reusableViewIdentifier`: set a new reusable identifier to use.\n- `reusableViewLoadSource`: change the source where the view is dequeued (for cells the default value is `.fromStoryboard`, for header/footer is `.fromXib(name:nil, bundle: nil)`). Allowed values are:\n\nBy default cells or header/footer will use the name of the class itself as `reusableIdentifier` (for example, your cell `ContactCell` must have the identifier set to `ContactCell`).\nThis is an useful convention to avoid strange identifiers.\n\nHowever you can still override this behaviour by setting `cellReuseIdentifier` (in `TableAdapter` or `CollectionAdapter` for cells) and `viewReuseIdentifier` (in `TableHeaderFooterAdapter`/`CollectionHeaderFooterAdapter` for header/footer).\n\nAnother interesting property is `reusableViewLoadSource`; this specify where Owl must search the UI of the cell/view you are about to load. Allowed values are:\n\n- `fromStoryboard`: load from storyboard inside the table/collection's prototypes list (default value for cells).\n- `fromXib(name: String?, bundle: Bundle?)`: load from a specific xib file in a bundle (if `name` is nil it uses the same filename of the cell class, ie `ContactCell.xib`; if `bundle` is `nil` it uses the same bundle of the class.\n- `fromClass`: loading from class.\n\nThe following example load a cell from an external xib with the same name of the class (`ContactCell.xib`) and contained in the same bundle of the cell class itself.\nThe same approach is valid for header/footer.\n\n```swift\nlet contactAdpt = TableCellAdapter\u003cContact, ContactCell\u003e()\n// instead of load cell from storyboard it will be loaded by\n// reading the root view inside the xib with the same name of the class\ncontactAdpt.reusableViewLoadSource = .fromXib(name:nil, bundle:nil)\n// optionally you can also set a custom id\ncontactAdpt.reusableViewIdentifier = \"CustomContactCellID\"\n// configure...\ndirector?.registerCellAdapter(contactAdpt)\n```\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"3.5\"/\u003e\n\n## 3.5 - Custom Section's Header/Footer\n\nSections (both `TableSection` and `CollectionSection`) can be configured to have both simple (`String`) or complex (custom views) header/footer.\nCustom Header/Footer are configured in the same way you have made for cells, by using the concept of adapter.\n\nTo create a **simple string-based header/footer** just pass its value into the init of section object:\n\n```swift\nlet newTableSection = TableSection(id: \"sectionId\", elements: contacts, header: \"\\(contacts.count) Contacts\", footer: nil)\n```\n\nThe following code create a section with a string header which contains the number of contacts inside that section.\n\nTo create a **view-based header/footer** you must register an adapter using:\n\n- `TableHeaderFooterAdapter\u003cView\u003e` class for table (where `View` is a subclass of `UITableViewHeaderFooterView `)\n- `CollectionHeaderFooterAdapter\u003cView\u003e` for collections (where `View` is a subclass of `UICollectionReusableView`)\n\nIn this case adapter keeps only the reference of the header/footer view class type (no explicit models are involved).\n\nThe following example create a view-based header for a collection and configure it with some custom data:\n\n```swift\n// In our case our custom view is a class EmojiHeaderView subclass of UICollectionReusableView \nlet emojiHeader = CollectionHeaderFooterAdapter\u003cEmojiHeaderView\u003e { ctx in\n\tctx.events.dequeue = { ctx in // register for view dequeue events to setup some data\n\t\tctx.view?.titleLabel.text = ctx.section?.identifier ?? \"-\"\n\t}\n}\n// Register adapter for header/footer\ndirector?.registerHeaderFooterAdapter(headerAdapter)\n```\n\nOnce you have registered your header/footer you can assign it to the init of any section:\n\n```swift\nlet newSection = CollectionSection(id: \"Section \\(idx)\" ,elements: elements, header: emojiHeader, footer: nil)\n```\n\nin the `ctx` parameter you will receive the section which generates the event.\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"3.6\"/\u003e\n\n## 3.6 - Reload data with automatic animations\n\nOwl supports automatic animated reload of the data between changes.\nWith this feature you don't need to think about calling (even *in the correct order*) `reloadRows/deleteRows/removeRows` methods when changing your data source: just perform changes to your model in a callback and, at the end, Owl will generate the corrrect sequence of the animations.\n\nThis because in `performBatchUpdates` of `UITableView` and `UICollectionView`, there are combinations of operations that cause crash when applied simultaneously.\nTo solve this problem, [DifferenceKit](https://github.com/ra1028/DifferenceKit/blob/master/README.md) takes an approach of split the set of differences at the minimal stages that can be perform batch-updates with no crashes.\n\nThe following example:\n\n```swift\ndirector?.reload(afterUpdate: { dir in\n   dir.firstSection()!.elements.shuffled() // shuffle some items\n   dir.firstSection()!.remove(at: 4) // remove item at index 4\n   // ... do any other stuff with sections or elements in section\n}, completion: nil)\n```\nAt the end of the call Owl will compare data models before/after and produce a changeset of animations to execute,\n\nDiffing algorithm is based upon the DifferenceKit framework, an O(n) difference algorithm optimized for performance in Swift. It supports all operations for animated UI batch-updates including section reloads.\n\nCompared to IGListKit it allows:\n\n**Supported collection**\n\n|             |Linear|Sectioned|Duplicate Element/Section|\n|:------------|:----:|:-------:|:-----------------------:|\n|Owl|✅    |✅       |✅                        |\n|RxDataSources|❌    |✅       |❌                        |\n|IGListKit    |✅    |❌       |✅                        |\n\n`Linear` means 1-dimensional collection.  \n`Sectioned` means 2-dimensional collection.  \n\n**Supported element differences**\n\n|             |Delete|Insert|Move|Reload  |Move across sections|\n|:------------|:----:|:----:|:--:|:------:|:------------------:|\n| Owl |✅    |✅     |✅  |✅      |✅                   |\n|RxDataSources|✅    |✅     |✅  |✅      |✅                   |\n|IGListKit    |✅    |✅     |✅  |✅      |❌                   |\n\n**Supported section differences**\n\n|             |Delete|Insert|Move|Reload|\n|:------------|:----:|:----:|:--:|:----:|\n|Owl|✅    |✅     |✅  |✅    |\n|RxDataSources|✅    |✅     |✅  |❌    |\n|IGListKit    |❌    |❌     |❌  |❌    |\n\nMoreover it way faster than [IGListKit](https://github.com/Instagram/IGListKit) and [RxDataSources](https://github.com/RxSwiftCommunity/RxDataSources) counterparts!\n\n[↑ Back To Top](#index)\n\n\u003ca name=\"3.7\"/\u003e\n\n## 3.7 - Working with Headers \u0026 Footers\n\nHeaders and Footers in Owl are managed in the same way already used for Cells, using Adapter; however due to its nature (instead of cells they are not strictly correlated to a specific model class) you will create an adapter which links just the view used to render it (subclass of `UITableViewHeaderFooterView` for `UITableView` and subclass of `UICollectionReusableView` for `UICollectionView`).\n\n**A. Creating Header/Footer for UITableView**\n\nUsing header/footer in your `UITableView` via storyboard is not easy; the most affordable way is to create a xib file with the same name of your `UITableViewHeaderFooterView` subclass.\n\nSuppose you want to make an header/footer class called `GroupHeaderView` for your Contacts table. You will need to create:\n\n`GroupHeaderView.xib` file which contains the UI of the class (you can still customize how the view is loaded by overriding the `ReusableCellViewProtocol` protocol in your `GroupHeaderView.swift` implementation. By default it expect a separate xib file with UI but you can also load it directly from class or provide a different filename). This xib file contains a single `GroupHeaderView` view subclass.\n\n![](./Documentation/header_footer.png)\n\n`GroupHeaderView .swift` file with your class implementation.\nIn a standard configuration you don't need to make anything special; just subclass `UITableViewHeaderFooterView`:\n\n```swift\nimport UIKit\n\n// Nothing special is required to support header/footer class in Owl.\n// This is just a simple class.\n\npublic class GroupHeaderView: UITableViewHeaderFooterView {\n\t@IBOutlet public var headerTitleLabel: UILabel?\n\t@IBOutlet public var headerSubtitleLabel: UILabel?\n\t...\n}\n```\n\nThe next step is to register your header/footer adapter:\n\n```swift\nlet groupHeaderAdapter = TableHeaderFooterAdapter\u003cGroupHeaderView\u003e { config in\n\n  // Configure content of the header/footer\n  config.events.dequeue = { ctx in\n    ctx.view?.headerTitleLabel?.text = \"...\"\n    ctx.view?.headerSubtitleLabel?.text = \"Section #\\(ctx.section)\"\n  }\n   \n  // Configure height of the header/footer\n  config.events.height = { _ in\n    return 30\n  }\n}\n```\n\nAt this point you can assign this adapter to any `TableSection` instance just by using:\n\n```swift\nlet newSection = TableSection(elements: [...], headerView: groupHeaderAdapter, footerView: nil)\ndirector?.add(section: section)\ndirector?.reload()\n```\n\nThat's all! You can reuse `groupHeaderAdapter` in more than a section without problem.\n\n**B. Creating Header/Footer for UICollectionView**\n\nCreating header/footer for collection views is pretty similar to the approach already used for table; the only big difference is you can use header/footer specified in collectionview (as like for cells) when you are using storyboards.\n\nFirst of all you need to make your class of the header/footer. The following example create a custom header which is a `EmojiHeaderView.swift`:\n\n```swift\nimport UIKit\n\npublic class EmojiHeaderView: UICollectionReusableView {\n    @IBOutlet public var titleLabel: UILabel!\n    // ...\n}\n```\n\nAs for table's header/footer view you don't need to make anything special for your subclass.\n\nThe next step is to enable header/footer for your collection view and set the newly generated view as subclass of your view class.\n\n![](./Documentation/header_footer_coolectionviews.png)\n\nYou are now ready to create the adapter to hold your header/footer:\n\n```swift\nlet headerAdapter = CollectionHeaderFooterAdapter\u003cEmojiHeaderView\u003e { cfg in\n  // dequeue\n  cfg.events.dequeue = { ctx in\n    ctx.view?.titleLabel.text = ctx.section?.identifier ?? \"-\"\n  }\n}\n\ndirector?.registerHeaderFooterAdapter(headerAdapter)\n```\n\nYou can now assign your header/footer adapter to one or more section instances:\n\n```swift\nlet section = CollectionSection(id: \"Section \\(idx)\", elements: rawSection)\nsection.headerView = headerAdapter\nsection.headerSize = CGSize(width: self.collection.frame.size.width, height: 30)\n```\n","funding_links":["https://github.com/sponsors/malcommac","https://www.paypal.com/paypalme2/danielemargutti"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmalcommac%2Fowl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmalcommac%2Fowl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmalcommac%2Fowl/lists"}