GitHub - malcommac/Owl: A declarative type-safe framework for building fast and...
source link: https://github.com/malcommac/Owl
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
README.md
Owl offers a data-driven declarative approach for building fast & flexible list in iOS.
It supports both UICollectionView
& UITableView
; UIStackView
is on the way!
Features Highlights
?
No more delegates & datasource. Just a fully type-safe declarative content approach
?
Better architecture to reuse components e decuple data from UI
?
Animate content changes automatically, no reloadData
/performBatchUpdates
?
Blazing fast diff algorithm based upon DifferenceKit
?
It uses standard UIKit components at its core. No magic!
?
(COMING SOON) Support for scrollable declarative/fully customizable stack view
?
Fully made in Swift from Swift ❥ lovers
Owl was created and maintaned by Daniele Margutti - My home site www.danielemargutti.com.
Requirements
- Xcode 9.0+
- iOS 10.0+
- Swift 5+
Installation
The preferred installation method is with CocoaPods. Add the following to your Podfile:
pod 'OwlKit', '~> 1.0'
What you will get
This 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!
director = TableDirector(table: table) // An adapter encapsulate the logic to render a model (Contact) with a specific ui (ContactCell). let contactAdapter = TableCellAdapter<Contact,ContactCell> { dr in // Each adapter exposes all the standard methods of a list, events // and properties. ctx (context) received from event is a type-safe // object both for model and cell! dr.events.dequeue = { ctx in // element is type-safe, it's the Contact instance! // cell is type-safe, it's the ContactCell instance ctx.cell?.item = ctx.element } dr.events.didSelect = { ctx in let vc = ContactDetailVC(people: ctx.element) navigationController.pushViewController(vc, animated: true) } dr.events.shouldHighlight = { ctx in return ctx.element.isBlacklisted == false } } // Since now our table can show Contact istances using ContactCell // All events are received only in its adapter. director?.registerCellAdapter(singleItem) /// Manage your content in a declarative way! let friendsSection = TableSection(elements: [john,mark,anita]) director?.add(section: friendsSection) director?.reload()
License
- Owl is released under the Apache 2.0 License.
- DifferenceKit is released under the Apache 2.0 License.
- Owl Icon is made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
Documentation
1 - Introduction: Director & Adapters 2 - Getting Started 3 - How-To- 3.1 - Create Model
- 3.2 - Create UI (cells)
- 3.3 - Manage Self-Sized Cells
- 3.4 - Loading Cells from Storyboard, Xib or class
- 3.5 - Custom Section's Header/Footer (String/View based)
- 3.6 - Reload data with automatic animations
UITableView
5 - APIs Doc: Manage UICollectionView
6 - Listen for UIScrollViewDelegate
events
Introduction: Director & Adapters
All the Owl's SDK is based upon two concepts: the director and the adapter.
Director
The following directors are available:
TableDirector
used to manage UITableView
instances
CollectionDirector
and FlowCollectionDirector
used to manage UICollectionView
with custom or UICollectionViewFlowLayout
layout.
Adapters
The scope of the adapter is to declare a pair of Model & UI a director can manage.
You will register as much adapters as models you have.
Getting Started
There are a plenty list of methods to manage both sections and elements into section:
Once you have changed your data models you can call reload()
function to sync it with UI.
The following code demostrate how to add swipe to delete functionality to an adapter:
How-To
Create Model
The following example show our Contact
model declaration:
Protocol Conformance
Protocol conformance is made by adding:
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)
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.
Create UI (Cells)
Reuse Identifier
Loading Cells from Storyboard, Xib or class
fromStoryboard
: load from storyboard (the default value, use it when your cell is defined as prototype inside table's instance).
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 your cell class.
fromClass
: loading from class.
The following example load a cell from an external xib file named MyContactCell.xib
:
Best Practices
Manage Self-Sized Cells
Self-sized cells are easy to be configured, both for tables and collection views.
default
: you must provide the height (table) or size (collection) of the cell
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.
explicit(CGFloat)
: provide a fixed height for all cell types (faster if you plan to have all cell sized same)
Custom Section's Header/Footer
To create a simple string-based header/footer just pass its value into the init of section object:
To create a view-based header/footer you must register an adapter using:
TableHeaderFooterAdapter<View>
class for table (where View
is a subclass of UITableViewHeaderFooterView
)
CollectionHeaderFooterAdapter<View>
for collections (where View
is a subclass of UICollectionReusableView
)
Once you have registered your header/footer you can assign it to the init of any section:
in the ctx
parameter you will receive the section which generates the event.
Reload data with automatic animations
This because in performBatchUpdates
of UITableView
and UICollectionView
, there are combinations of operations that cause crash when applied simultaneously.
To solve this problem, DifferenceKit takes an approach of split the set of differences at the minimal stages that can be perform batch-updates with no crashes.
The following example:
director?.reload(afterUpdate: { dir in dir.firstSection()!.elements.shuffled() // shuffle some items dir.firstSection()!.remove(at: 4) // remove item at index 4 // ... do any other stuff with sections or elements in section }, completion: nil)
At the end of the call Owl will compare data models before/after and produce a changeset of animations to execute,
Diffing 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.
Compared to IGListKit it allows:
Supported collection
Linear Sectioned Duplicate Element/Section Owl ✅ ✅ ✅ RxDataSources ❌ ✅ ❌ IGListKit ✅ ❌ ✅
Linear
means 1-dimensional collection.Sectioned
means 2-dimensional collection.
Supported element differences
Delete Insert Move Reload Move across sections Owl ✅ ✅ ✅ ✅ ✅ RxDataSources ✅ ✅ ✅ ✅ ✅ IGListKit ✅ ✅ ✅ ✅ ❌
Supported section differences
Delete Insert Move Reload Owl ✅ ✅ ✅ ✅ RxDataSources ✅ ✅ ✅ ❌ IGListKit ❌ ❌ ❌ ❌
Moreover it way faster than IGListKit and RxDataSources counterparts!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK