Repository files navigation

TableKit

Build StatusSwift 5.1 compatibleCarthage compatibleCocoaPods compatiblePlatform iOSLicense: MIT

TableKit is a super lightweight yet powerful generic library that allows you to build complex table views in a declarative type-safe manner. It hides a complexity of UITableViewDataSource and UITableViewDelegate methods behind the scene, so your code will be look clean, easy to read and nice to maintain.

Features

  • Type-safe generic cells
  • Functional programming style friendly
  • The easiest way to map your models or view models to cells
  • Automatic cell registration*
  • Correctly handles autolayout cells with multiline labels
  • Chainable cell actions (select/deselect etc.)
  • Support cells created from code, xib, or storyboard
  • Support different cells height calculation strategies
  • Support portrait and landscape orientations
  • No need to subclass
  • Extensibility

Getting Started

An example app is included demonstrating TableKit's functionality.

Basic usage

Create your rows:

import TableKit
letrow1=TableRow<StringTableViewCell>(item:"1")letrow2=TableRow<IntTableViewCell>(item:2)letrow3=TableRow<UserTableViewCell>(item:User(name:"John Doe", rating:5))

Put rows into section:

letsection=TableSection(rows:[row1, row2, row3])

And setup your table:

lettableDirector=TableDirector(tableView: tableView)
tableDirector += section

Done. Your table is ready. Your cells have to conform to ConfigurableCell protocol:

classStringTableViewCell:UITableViewCell,ConfigurableCell{func configure(with string:String){
textLabel?.text = string
}}classUserTableViewCell:UITableViewCell,ConfigurableCell{staticvarestimatedHeight:CGFloat?{return100}
// is not required to be implemented
// by default reuse id is equal to cell's class name
staticvarreuseIdentifier:String{return"my id"}func configure(with user:User){
textLabel?.text = user.name
detailTextLabel?.text ="Rating: \(user.rating)"}}

You could have as many rows and sections as you need.

Row actions

It nice to have some actions that related to your cells:

letaction=TableRowAction<StringTableViewCell>(.click){(options)in
// you could access any useful information that relates to the action
// options.cell - StringTableViewCell?
// options.item - String
// options.indexPath - IndexPath
// options.userInfo - [AnyHashable: Any]?
}letrow=TableRow<StringTableViewCell>(item:"some", actions:[action])

Or, using nice chaining approach:

letrow= TableRow<StringTableViewCell>(item:"some").on(.click){(options)in}.on(.shouldHighlight){(options)->Boolinreturnfalse}

You could find all available actions here.

Custom row actions

You are able to define your own actions:

structMyActions{staticletButtonClicked="ButtonClicked"}classMyTableViewCell:UITableViewCell,ConfigurableCell{@IBActionfunc myButtonClicked(sender:UIButton){TableCellAction(key:MyActions.ButtonClicked, sender:self).invoke()}}

And handle them accordingly:

letmyAction=TableRowAction<MyTableViewCell>(.custom(MyActions.ButtonClicked)){(options)in}

Multiple actions with same type

It's also possible to use multiple actions with same type:

letclick1=TableRowAction<StringTableViewCell>(.click){(options)in}
click1.id ="click1" // optional
letclick2=TableRowAction<StringTableViewCell>(.click){(options)in}
click2.id ="click2" // optional
letrow=TableRow<StringTableViewCell>(item:"some", actions:[click1, click2])

Could be useful in case if you want to separate your logic somehow. Actions will be invoked in order which they were attached.

If you define multiple actions with same type which also return a value, only last return value will be used for table view.

You could also remove any action by id:

row.removeAction(forActionId:"action_id")

Advanced

Cell height calculating strategy

By default TableKit relies on self-sizing cells. In that case you have to provide an estimated height for your cells:

classStringTableViewCell:UITableViewCell,ConfigurableCell{
// ...
staticvarestimatedHeight:CGFloat?{return255}}

It's enough for most cases. But you may be not happy with this. So you could use a prototype cell to calculate cells heights. To enable this feature simply use this property:

lettableDirector=TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation:true)

It does all dirty work with prototypes for you behind the scene, so you don't have to worry about anything except of your cell configuration:

classImageTableViewCell:UITableViewCell,ConfigurableCell{func configure(with url:NSURL){loadImageAsync(url: url, imageView: imageView)}overridefunc layoutSubviews(){
super.layoutSubviews()
contentView.layoutIfNeeded()
multilineLabel.preferredMaxLayoutWidth = multilineLabel.bounds.size.width
}}

You have to additionally set preferredMaxLayoutWidth for all your multiline labels.

Functional programming

It's never been so easy to deal with table views.

letusers= /* some users array */
let click =TableRowAction<UserTableViewCell>(.click){}letrows= users.filter({ $0.state ==.active }).map({TableRow<UserTableViewCell>(item: $0.name, actions:[click])})
tableDirector += rows

Done, your table is ready.

Automatic cell registration

TableKit can register your cells in a table view automatically. In case if your reusable cell id matches cell's xib name:

MyTableViewCell.swiftMyTableViewCell.xib

You can also turn off this behaviour:

lettableDirector=TableDirector(tableView: tableView, shouldUseAutomaticCellRegistration:false)

and register your cell manually.

Installation

CocoaPods

To integrate TableKit into your Xcode project using CocoaPods, specify it in your Podfile:

pod'TableKit'

Carthage

Add the line github "maxsokolov/tablekit" to your Cartfile.

Manual

Clone the repo and drag files from Sources folder into your Xcode project.

Requirements

  • iOS 8.0
  • Xcode 9.0

Changelog

Keep an eye on changes.

License

TableKit is available under the MIT license. See LICENSE for details.

About

Type-safe declarative table views.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

TableKit

Build StatusSwift 5.1 compatibleCarthage compatibleCocoaPods compatiblePlatform iOSLicense: MIT

TableKit is a super lightweight yet powerful generic library that allows you to build complex table views in a declarative type-safe manner. It hides a complexity of UITableViewDataSource and UITableViewDelegate methods behind the scene, so your code will be look clean, easy to read and nice to maintain.

Features

  • Type-safe generic cells
  • Functional programming style friendly
  • The easiest way to map your models or view models to cells
  • Automatic cell registration*
  • Correctly handles autolayout cells with multiline labels
  • Chainable cell actions (select/deselect etc.)
  • Support cells created from code, xib, or storyboard
  • Support different cells height calculation strategies
  • Support portrait and landscape orientations
  • No need to subclass
  • Extensibility

Getting Started

An example app is included demonstrating TableKit's functionality.

Basic usage

Create your rows:

import TableKit
letrow1=TableRow<StringTableViewCell>(item:"1")letrow2=TableRow<IntTableViewCell>(item:2)letrow3=TableRow<UserTableViewCell>(item:User(name:"John Doe", rating:5))

Put rows into section:

letsection=TableSection(rows:[row1, row2, row3])

And setup your table:

lettableDirector=TableDirector(tableView: tableView)
tableDirector += section

Done. Your table is ready. Your cells have to conform to ConfigurableCell protocol:

classStringTableViewCell:UITableViewCell,ConfigurableCell{func configure(with string:String){
textLabel?.text = string
}}classUserTableViewCell:UITableViewCell,ConfigurableCell{staticvarestimatedHeight:CGFloat?{return100}
// is not required to be implemented
// by default reuse id is equal to cell's class name
staticvarreuseIdentifier:String{return"my id"}func configure(with user:User){
textLabel?.text = user.name
detailTextLabel?.text ="Rating: \(user.rating)"}}

You could have as many rows and sections as you need.

Row actions

It nice to have some actions that related to your cells:

letaction=TableRowAction<StringTableViewCell>(.click){(options)in
// you could access any useful information that relates to the action
// options.cell - StringTableViewCell?
// options.item - String
// options.indexPath - IndexPath
// options.userInfo - [AnyHashable: Any]?
}letrow=TableRow<StringTableViewCell>(item:"some", actions:[action])

Or, using nice chaining approach:

letrow= TableRow<StringTableViewCell>(item:"some").on(.click){(options)in}.on(.shouldHighlight){(options)->Boolinreturnfalse}

You could find all available actions here.

Custom row actions

You are able to define your own actions:

structMyActions{staticletButtonClicked="ButtonClicked"}classMyTableViewCell:UITableViewCell,ConfigurableCell{@IBActionfunc myButtonClicked(sender:UIButton){TableCellAction(key:MyActions.ButtonClicked, sender:self).invoke()}}

And handle them accordingly:

letmyAction=TableRowAction<MyTableViewCell>(.custom(MyActions.ButtonClicked)){(options)in}

Multiple actions with same type

It's also possible to use multiple actions with same type:

letclick1=TableRowAction<StringTableViewCell>(.click){(options)in}
click1.id ="click1" // optional
letclick2=TableRowAction<StringTableViewCell>(.click){(options)in}
click2.id ="click2" // optional
letrow=TableRow<StringTableViewCell>(item:"some", actions:[click1, click2])

Could be useful in case if you want to separate your logic somehow. Actions will be invoked in order which they were attached.

If you define multiple actions with same type which also return a value, only last return value will be used for table view.

You could also remove any action by id:

row.removeAction(forActionId:"action_id")

Advanced

Cell height calculating strategy

By default TableKit relies on self-sizing cells. In that case you have to provide an estimated height for your cells:

classStringTableViewCell:UITableViewCell,ConfigurableCell{
// ...
staticvarestimatedHeight:CGFloat?{return255}}

It's enough for most cases. But you may be not happy with this. So you could use a prototype cell to calculate cells heights. To enable this feature simply use this property:

lettableDirector=TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation:true)

It does all dirty work with prototypes for you behind the scene, so you don't have to worry about anything except of your cell configuration:

classImageTableViewCell:UITableViewCell,ConfigurableCell{func configure(with url:NSURL){loadImageAsync(url: url, imageView: imageView)}overridefunc layoutSubviews(){
super.layoutSubviews()
contentView.layoutIfNeeded()
multilineLabel.preferredMaxLayoutWidth = multilineLabel.bounds.size.width
}}

You have to additionally set preferredMaxLayoutWidth for all your multiline labels.

Functional programming

It's never been so easy to deal with table views.

letusers= /* some users array */
let click =TableRowAction<UserTableViewCell>(.click){}letrows= users.filter({ $0.state ==.active }).map({TableRow<UserTableViewCell>(item: $0.name, actions:[click])})
tableDirector += rows

Done, your table is ready.

Automatic cell registration

TableKit can register your cells in a table view automatically. In case if your reusable cell id matches cell's xib name:

MyTableViewCell.swiftMyTableViewCell.xib

You can also turn off this behaviour:

lettableDirector=TableDirector(tableView: tableView, shouldUseAutomaticCellRegistration:false)

and register your cell manually.

Installation

CocoaPods

To integrate TableKit into your Xcode project using CocoaPods, specify it in your Podfile:

pod'TableKit'

Carthage

Add the line github "maxsokolov/tablekit" to your Cartfile.

Manual

Clone the repo and drag files from Sources folder into your Xcode project.

Requirements

  • iOS 8.0
  • Xcode 9.0

Changelog

Keep an eye on changes.

License

TableKit is available under the MIT license. See LICENSE for details.

About

Type-safe declarative table views.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

TableKit

Build StatusSwift 5.1 compatibleCarthage compatibleCocoaPods compatiblePlatform iOSLicense: MIT

TableKit is a super lightweight yet powerful generic library that allows you to build complex table views in a declarative type-safe manner. It hides a complexity of UITableViewDataSource and UITableViewDelegate methods behind the scene, so your code will be look clean, easy to read and nice to maintain.

Features

  • Type-safe generic cells
  • Functional programming style friendly
  • The easiest way to map your models or view models to cells
  • Automatic cell registration*
  • Correctly handles autolayout cells with multiline labels
  • Chainable cell actions (select/deselect etc.)
  • Support cells created from code, xib, or storyboard
  • Support different cells height calculation strategies
  • Support portrait and landscape orientations
  • No need to subclass
  • Extensibility

Getting Started

An example app is included demonstrating TableKit's functionality.

Basic usage

Create your rows:

import TableKit
letrow1=TableRow<StringTableViewCell>(item:"1")letrow2=TableRow<IntTableViewCell>(item:2)letrow3=TableRow<UserTableViewCell>(item:User(name:"John Doe", rating:5))

Put rows into section:

letsection=TableSection(rows:[row1, row2, row3])

And setup your table:

lettableDirector=TableDirector(tableView: tableView)
tableDirector += section

Done. Your table is ready. Your cells have to conform to ConfigurableCell protocol:

classStringTableViewCell:UITableViewCell,ConfigurableCell{func configure(with string:String){
textLabel?.text = string
}}classUserTableViewCell:UITableViewCell,ConfigurableCell{staticvarestimatedHeight:CGFloat?{return100}
// is not required to be implemented
// by default reuse id is equal to cell's class name
staticvarreuseIdentifier:String{return"my id"}func configure(with user:User){
textLabel?.text = user.name
detailTextLabel?.text ="Rating: \(user.rating)"}}

You could have as many rows and sections as you need.

Row actions

It nice to have some actions that related to your cells:

letaction=TableRowAction<StringTableViewCell>(.click){(options)in
// you could access any useful information that relates to the action
// options.cell - StringTableViewCell?
// options.item - String
// options.indexPath - IndexPath
// options.userInfo - [AnyHashable: Any]?
}letrow=TableRow<StringTableViewCell>(item:"some", actions:[action])

Or, using nice chaining approach:

letrow= TableRow<StringTableViewCell>(item:"some").on(.click){(options)in}.on(.shouldHighlight){(options)->Boolinreturnfalse}

You could find all available actions here.

Custom row actions

You are able to define your own actions:

structMyActions{staticletButtonClicked="ButtonClicked"}classMyTableViewCell:UITableViewCell,ConfigurableCell{@IBActionfunc myButtonClicked(sender:UIButton){TableCellAction(key:MyActions.ButtonClicked, sender:self).invoke()}}

And handle them accordingly:

letmyAction=TableRowAction<MyTableViewCell>(.custom(MyActions.ButtonClicked)){(options)in}

Multiple actions with same type

It's also possible to use multiple actions with same type:

letclick1=TableRowAction<StringTableViewCell>(.click){(options)in}
click1.id ="click1" // optional
letclick2=TableRowAction<StringTableViewCell>(.click){(options)in}
click2.id ="click2" // optional
letrow=TableRow<StringTableViewCell>(item:"some", actions:[click1, click2])

Could be useful in case if you want to separate your logic somehow. Actions will be invoked in order which they were attached.

If you define multiple actions with same type which also return a value, only last return value will be used for table view.

You could also remove any action by id:

row.removeAction(forActionId:"action_id")

Advanced

Cell height calculating strategy

By default TableKit relies on self-sizing cells. In that case you have to provide an estimated height for your cells:

classStringTableViewCell:UITableViewCell,ConfigurableCell{
// ...
staticvarestimatedHeight:CGFloat?{return255}}

It's enough for most cases. But you may be not happy with this. So you could use a prototype cell to calculate cells heights. To enable this feature simply use this property:

lettableDirector=TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation:true)

It does all dirty work with prototypes for you behind the scene, so you don't have to worry about anything except of your cell configuration:

classImageTableViewCell:UITableViewCell,ConfigurableCell{func configure(with url:NSURL){loadImageAsync(url: url, imageView: imageView)}overridefunc layoutSubviews(){
super.layoutSubviews()
contentView.layoutIfNeeded()
multilineLabel.preferredMaxLayoutWidth = multilineLabel.bounds.size.width
}}

You have to additionally set preferredMaxLayoutWidth for all your multiline labels.

Functional programming

It's never been so easy to deal with table views.

letusers= /* some users array */
let click =TableRowAction<UserTableViewCell>(.click){}letrows= users.filter({ $0.state ==.active }).map({TableRow<UserTableViewCell>(item: $0.name, actions:[click])})
tableDirector += rows

Done, your table is ready.

Automatic cell registration

TableKit can register your cells in a table view automatically. In case if your reusable cell id matches cell's xib name:

MyTableViewCell.swiftMyTableViewCell.xib

You can also turn off this behaviour:

lettableDirector=TableDirector(tableView: tableView, shouldUseAutomaticCellRegistration:false)

and register your cell manually.

Installation

CocoaPods

To integrate TableKit into your Xcode project using CocoaPods, specify it in your Podfile:

pod'TableKit'

Carthage

Add the line github "maxsokolov/tablekit" to your Cartfile.

Manual

Clone the repo and drag files from Sources folder into your Xcode project.

Requirements

  • iOS 8.0
  • Xcode 9.0

Changelog

Keep an eye on changes.

License

TableKit is available under the MIT license. See LICENSE for details.

About

Type-safe declarative table views.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

TableKit

Build StatusSwift 5.1 compatibleCarthage compatibleCocoaPods compatiblePlatform iOSLicense: MIT

TableKit is a super lightweight yet powerful generic library that allows you to build complex table views in a declarative type-safe manner. It hides a complexity of UITableViewDataSource and UITableViewDelegate methods behind the scene, so your code will be look clean, easy to read and nice to maintain.

Features

  • Type-safe generic cells
  • Functional programming style friendly
  • The easiest way to map your models or view models to cells
  • Automatic cell registration*
  • Correctly handles autolayout cells with multiline labels
  • Chainable cell actions (select/deselect etc.)
  • Support cells created from code, xib, or storyboard
  • Support different cells height calculation strategies
  • Support portrait and landscape orientations
  • No need to subclass
  • Extensibility

Getting Started

An example app is included demonstrating TableKit's functionality.

Basic usage

Create your rows:

import TableKit
letrow1=TableRow<StringTableViewCell>(item:"1")letrow2=TableRow<IntTableViewCell>(item:2)letrow3=TableRow<UserTableViewCell>(item:User(name:"John Doe", rating:5))

Put rows into section:

letsection=TableSection(rows:[row1, row2, row3])

And setup your table:

lettableDirector=TableDirector(tableView: tableView)
tableDirector += section

Done. Your table is ready. Your cells have to conform to ConfigurableCell protocol:

classStringTableViewCell:UITableViewCell,ConfigurableCell{func configure(with string:String){
textLabel?.text = string
}}classUserTableViewCell:UITableViewCell,ConfigurableCell{staticvarestimatedHeight:CGFloat?{return100}
// is not required to be implemented
// by default reuse id is equal to cell's class name
staticvarreuseIdentifier:String{return"my id"}func configure(with user:User){
textLabel?.text = user.name
detailTextLabel?.text ="Rating: \(user.rating)"}}

You could have as many rows and sections as you need.

Row actions

It nice to have some actions that related to your cells:

letaction=TableRowAction<StringTableViewCell>(.click){(options)in
// you could access any useful information that relates to the action
// options.cell - StringTableViewCell?
// options.item - String
// options.indexPath - IndexPath
// options.userInfo - [AnyHashable: Any]?
}letrow=TableRow<StringTableViewCell>(item:"some", actions:[action])

Or, using nice chaining approach:

letrow= TableRow<StringTableViewCell>(item:"some").on(.click){(options)in}.on(.shouldHighlight){(options)->Boolinreturnfalse}

You could find all available actions here.

Custom row actions

You are able to define your own actions:

structMyActions{staticletButtonClicked="ButtonClicked"}classMyTableViewCell:UITableViewCell,ConfigurableCell{@IBActionfunc myButtonClicked(sender:UIButton){TableCellAction(key:MyActions.ButtonClicked, sender:self).invoke()}}

And handle them accordingly:

letmyAction=TableRowAction<MyTableViewCell>(.custom(MyActions.ButtonClicked)){(options)in}

Multiple actions with same type

It's also possible to use multiple actions with same type:

letclick1=TableRowAction<StringTableViewCell>(.click){(options)in}
click1.id ="click1" // optional
letclick2=TableRowAction<StringTableViewCell>(.click){(options)in}
click2.id ="click2" // optional
letrow=TableRow<StringTableViewCell>(item:"some", actions:[click1, click2])

Could be useful in case if you want to separate your logic somehow. Actions will be invoked in order which they were attached.

If you define multiple actions with same type which also return a value, only last return value will be used for table view.

You could also remove any action by id:

row.removeAction(forActionId:"action_id")

Advanced

Cell height calculating strategy

By default TableKit relies on self-sizing cells. In that case you have to provide an estimated height for your cells:

classStringTableViewCell:UITableViewCell,ConfigurableCell{
// ...
staticvarestimatedHeight:CGFloat?{return255}}

It's enough for most cases. But you may be not happy with this. So you could use a prototype cell to calculate cells heights. To enable this feature simply use this property:

lettableDirector=TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation:true)

It does all dirty work with prototypes for you behind the scene, so you don't have to worry about anything except of your cell configuration:

classImageTableViewCell:UITableViewCell,ConfigurableCell{func configure(with url:NSURL){loadImageAsync(url: url, imageView: imageView)}overridefunc layoutSubviews(){
super.layoutSubviews()
contentView.layoutIfNeeded()
multilineLabel.preferredMaxLayoutWidth = multilineLabel.bounds.size.width
}}

You have to additionally set preferredMaxLayoutWidth for all your multiline labels.

Functional programming

It's never been so easy to deal with table views.

letusers= /* some users array */
let click =TableRowAction<UserTableViewCell>(.click){}letrows= users.filter({ $0.state ==.active }).map({TableRow<UserTableViewCell>(item: $0.name, actions:[click])})
tableDirector += rows

Done, your table is ready.

Automatic cell registration

TableKit can register your cells in a table view automatically. In case if your reusable cell id matches cell's xib name:

MyTableViewCell.swiftMyTableViewCell.xib

You can also turn off this behaviour:

lettableDirector=TableDirector(tableView: tableView, shouldUseAutomaticCellRegistration:false)

and register your cell manually.

Installation

CocoaPods

To integrate TableKit into your Xcode project using CocoaPods, specify it in your Podfile:

pod'TableKit'

Carthage

Add the line github "maxsokolov/tablekit" to your Cartfile.

Manual

Clone the repo and drag files from Sources folder into your Xcode project.

Requirements

  • iOS 8.0
  • Xcode 9.0

Changelog

Keep an eye on changes.

License

TableKit is available under the MIT license. See LICENSE for details.

About

Type-safe declarative table views.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

TableKit

Build StatusSwift 5.1 compatibleCarthage compatibleCocoaPods compatiblePlatform iOSLicense: MIT

TableKit is a super lightweight yet powerful generic library that allows you to build complex table views in a declarative type-safe manner. It hides a complexity of UITableViewDataSource and UITableViewDelegate methods behind the scene, so your code will be look clean, easy to read and nice to maintain.

Features

  • Type-safe generic cells
  • Functional programming style friendly
  • The easiest way to map your models or view models to cells
  • Automatic cell registration*
  • Correctly handles autolayout cells with multiline labels
  • Chainable cell actions (select/deselect etc.)
  • Support cells created from code, xib, or storyboard
  • Support different cells height calculation strategies
  • Support portrait and landscape orientations
  • No need to subclass
  • Extensibility

Getting Started

An example app is included demonstrating TableKit's functionality.

Basic usage

Create your rows:

import TableKit
letrow1=TableRow<StringTableViewCell>(item:"1")letrow2=TableRow<IntTableViewCell>(item:2)letrow3=TableRow<UserTableViewCell>(item:User(name:"John Doe", rating:5))

Put rows into section:

letsection=TableSection(rows:[row1, row2, row3])

And setup your table:

lettableDirector=TableDirector(tableView: tableView)
tableDirector += section

Done. Your table is ready. Your cells have to conform to ConfigurableCell protocol:

classStringTableViewCell:UITableViewCell,ConfigurableCell{func configure(with string:String){
textLabel?.text = string
}}classUserTableViewCell:UITableViewCell,ConfigurableCell{staticvarestimatedHeight:CGFloat?{return100}
// is not required to be implemented
// by default reuse id is equal to cell's class name
staticvarreuseIdentifier:String{return"my id"}func configure(with user:User){
textLabel?.text = user.name
detailTextLabel?.text ="Rating: \(user.rating)"}}

You could have as many rows and sections as you need.

Row actions

It nice to have some actions that related to your cells:

letaction=TableRowAction<StringTableViewCell>(.click){(options)in
// you could access any useful information that relates to the action
// options.cell - StringTableViewCell?
// options.item - String
// options.indexPath - IndexPath
// options.userInfo - [AnyHashable: Any]?
}letrow=TableRow<StringTableViewCell>(item:"some", actions:[action])

Or, using nice chaining approach:

letrow= TableRow<StringTableViewCell>(item:"some").on(.click){(options)in}.on(.shouldHighlight){(options)->Boolinreturnfalse}

You could find all available actions here.

Custom row actions

You are able to define your own actions:

structMyActions{staticletButtonClicked="ButtonClicked"}classMyTableViewCell:UITableViewCell,ConfigurableCell{@IBActionfunc myButtonClicked(sender:UIButton){TableCellAction(key:MyActions.ButtonClicked, sender:self).invoke()}}

And handle them accordingly:

letmyAction=TableRowAction<MyTableViewCell>(.custom(MyActions.ButtonClicked)){(options)in}

Multiple actions with same type

It's also possible to use multiple actions with same type:

letclick1=TableRowAction<StringTableViewCell>(.click){(options)in}
click1.id ="click1" // optional
letclick2=TableRowAction<StringTableViewCell>(.click){(options)in}
click2.id ="click2" // optional
letrow=TableRow<StringTableViewCell>(item:"some", actions:[click1, click2])

Could be useful in case if you want to separate your logic somehow. Actions will be invoked in order which they were attached.

If you define multiple actions with same type which also return a value, only last return value will be used for table view.

You could also remove any action by id:

row.removeAction(forActionId:"action_id")

Advanced

Cell height calculating strategy

By default TableKit relies on self-sizing cells. In that case you have to provide an estimated height for your cells:

classStringTableViewCell:UITableViewCell,ConfigurableCell{
// ...
staticvarestimatedHeight:CGFloat?{return255}}

It's enough for most cases. But you may be not happy with this. So you could use a prototype cell to calculate cells heights. To enable this feature simply use this property:

lettableDirector=TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation:true)

It does all dirty work with prototypes for you behind the scene, so you don't have to worry about anything except of your cell configuration:

classImageTableViewCell:UITableViewCell,ConfigurableCell{func configure(with url:NSURL){loadImageAsync(url: url, imageView: imageView)}overridefunc layoutSubviews(){
super.layoutSubviews()
contentView.layoutIfNeeded()
multilineLabel.preferredMaxLayoutWidth = multilineLabel.bounds.size.width
}}

You have to additionally set preferredMaxLayoutWidth for all your multiline labels.

Functional programming

It's never been so easy to deal with table views.

letusers= /* some users array */
let click =TableRowAction<UserTableViewCell>(.click){}letrows= users.filter({ $0.state ==.active }).map({TableRow<UserTableViewCell>(item: $0.name, actions:[click])})
tableDirector += rows

Done, your table is ready.

Automatic cell registration

TableKit can register your cells in a table view automatically. In case if your reusable cell id matches cell's xib name:

MyTableViewCell.swiftMyTableViewCell.xib

You can also turn off this behaviour:

lettableDirector=TableDirector(tableView: tableView, shouldUseAutomaticCellRegistration:false)

and register your cell manually.

Installation

CocoaPods

To integrate TableKit into your Xcode project using CocoaPods, specify it in your Podfile:

pod'TableKit'

Carthage

Add the line github "maxsokolov/tablekit" to your Cartfile.

Manual

Clone the repo and drag files from Sources folder into your Xcode project.

Requirements

  • iOS 8.0
  • Xcode 9.0

Changelog

Keep an eye on changes.

License

TableKit is available under the MIT license. See LICENSE for details.

About

Type-safe declarative table views.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })();
Skip to content

Repository files navigation

TableKit

Build StatusSwift 5.1 compatibleCarthage compatibleCocoaPods compatiblePlatform iOSLicense: MIT

TableKit is a super lightweight yet powerful generic library that allows you to build complex table views in a declarative type-safe manner. It hides a complexity of UITableViewDataSource and UITableViewDelegate methods behind the scene, so your code will be look clean, easy to read and nice to maintain.

Features

  • Type-safe generic cells
  • Functional programming style friendly
  • The easiest way to map your models or view models to cells
  • Automatic cell registration*
  • Correctly handles autolayout cells with multiline labels
  • Chainable cell actions (select/deselect etc.)
  • Support cells created from code, xib, or storyboard
  • Support different cells height calculation strategies
  • Support portrait and landscape orientations
  • No need to subclass
  • Extensibility

Getting Started

An example app is included demonstrating TableKit's functionality.

Basic usage

Create your rows:

import TableKit
letrow1=TableRow<StringTableViewCell>(item:"1")letrow2=TableRow<IntTableViewCell>(item:2)letrow3=TableRow<UserTableViewCell>(item:User(name:"John Doe", rating:5))

Put rows into section:

letsection=TableSection(rows:[row1, row2, row3])

And setup your table:

lettableDirector=TableDirector(tableView: tableView)
tableDirector += section

Done. Your table is ready. Your cells have to conform to ConfigurableCell protocol:

classStringTableViewCell:UITableViewCell,ConfigurableCell{func configure(with string:String){
textLabel?.text = string
}}classUserTableViewCell:UITableViewCell,ConfigurableCell{staticvarestimatedHeight:CGFloat?{return100}
// is not required to be implemented
// by default reuse id is equal to cell's class name
staticvarreuseIdentifier:String{return"my id"}func configure(with user:User){
textLabel?.text = user.name
detailTextLabel?.text ="Rating: \(user.rating)"}}

You could have as many rows and sections as you need.

Row actions

It nice to have some actions that related to your cells:

letaction=TableRowAction<StringTableViewCell>(.click){(options)in
// you could access any useful information that relates to the action
// options.cell - StringTableViewCell?
// options.item - String
// options.indexPath - IndexPath
// options.userInfo - [AnyHashable: Any]?
}letrow=TableRow<StringTableViewCell>(item:"some", actions:[action])

Or, using nice chaining approach:

letrow= TableRow<StringTableViewCell>(item:"some").on(.click){(options)in}.on(.shouldHighlight){(options)->Boolinreturnfalse}

You could find all available actions here.

Custom row actions

You are able to define your own actions:

structMyActions{staticletButtonClicked="ButtonClicked"}classMyTableViewCell:UITableViewCell,ConfigurableCell{@IBActionfunc myButtonClicked(sender:UIButton){TableCellAction(key:MyActions.ButtonClicked, sender:self).invoke()}}

And handle them accordingly:

letmyAction=TableRowAction<MyTableViewCell>(.custom(MyActions.ButtonClicked)){(options)in}

Multiple actions with same type

It's also possible to use multiple actions with same type:

letclick1=TableRowAction<StringTableViewCell>(.click){(options)in}
click1.id ="click1" // optional
letclick2=TableRowAction<StringTableViewCell>(.click){(options)in}
click2.id ="click2" // optional
letrow=TableRow<StringTableViewCell>(item:"some", actions:[click1, click2])

Could be useful in case if you want to separate your logic somehow. Actions will be invoked in order which they were attached.

If you define multiple actions with same type which also return a value, only last return value will be used for table view.

You could also remove any action by id:

row.removeAction(forActionId:"action_id")

Advanced

Cell height calculating strategy

By default TableKit relies on self-sizing cells. In that case you have to provide an estimated height for your cells:

classStringTableViewCell:UITableViewCell,ConfigurableCell{
// ...
staticvarestimatedHeight:CGFloat?{return255}}

It's enough for most cases. But you may be not happy with this. So you could use a prototype cell to calculate cells heights. To enable this feature simply use this property:

lettableDirector=TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation:true)

It does all dirty work with prototypes for you behind the scene, so you don't have to worry about anything except of your cell configuration:

classImageTableViewCell:UITableViewCell,ConfigurableCell{func configure(with url:NSURL){loadImageAsync(url: url, imageView: imageView)}overridefunc layoutSubviews(){
super.layoutSubviews()
contentView.layoutIfNeeded()
multilineLabel.preferredMaxLayoutWidth = multilineLabel.bounds.size.width
}}

You have to additionally set preferredMaxLayoutWidth for all your multiline labels.

Functional programming

It's never been so easy to deal with table views.

letusers= /* some users array */
let click =TableRowAction<UserTableViewCell>(.click){}letrows= users.filter({ $0.state ==.active }).map({TableRow<UserTableViewCell>(item: $0.name, actions:[click])})
tableDirector += rows

Done, your table is ready.

Automatic cell registration

TableKit can register your cells in a table view automatically. In case if your reusable cell id matches cell's xib name:

MyTableViewCell.swiftMyTableViewCell.xib

You can also turn off this behaviour:

lettableDirector=TableDirector(tableView: tableView, shouldUseAutomaticCellRegistration:false)

and register your cell manually.

Installation

CocoaPods

To integrate TableKit into your Xcode project using CocoaPods, specify it in your Podfile:

pod'TableKit'

Carthage

Add the line github "maxsokolov/tablekit" to your Cartfile.

Manual

Clone the repo and drag files from Sources folder into your Xcode project.

Requirements

  • iOS 8.0
  • Xcode 9.0

Changelog

Keep an eye on changes.

License

TableKit is available under the MIT license. See LICENSE for details.

About

Type-safe declarative table views.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages