Simple static table views for iOS in Swift. Static's goal is to separate model data from presentation. Row
s and Section
s are your “view models” for your cells. You simply specify a cell class to use and that handles all of the presentation. See the usage section below for details.
Swift Version | Static Version |
---|---|
4.2+ | 3.0.1 |
3.2+ | 2.1 |
3.0.1 | 2.0.1 |
3.0 | 2.0 |
2.3 | 1.2 |
2.2 | 1.1 |
2.0 - 2.1 | 1.0 |
Carthage is the recommended way to install Static. Add the following to your Cartfile:
github "venmo/Static"
CocoaPods is a dependency manager for Cocoa projects. To install Static with CocoaPods:
Make sure CocoaPods is installed (Static requires version 0.37 or greater).
Update your Podfile to include the following:
use_frameworks!
pod 'Static', git: 'https://github.com/venmo/Static'
Run pod install
.
For manual installation, it's recommended to add the project as a subproject to your project or workspace and adding the appropriate framework as a target dependency.
An example app is included demonstrating Static's functionality.
To use Static, you need to define Row
s and Section
s to describe your data. Here's a simple example:
import Static
Section(rows: [
Row(text: "Hello")
])
You can configure Section
s and Row
s for anything you want. Here's another example:
Section(header: "Money", rows: [
Row(text: "Balance", detailText: "$12.00", accessory: .disclosureIndicator, selection: {
// Show statement
}),
Row(text: "Transfer to Bank…", cellClass: ButtonCell.self, selection: {
[unowned self] in
let viewController = ViewController()
self.presentViewController(viewController, animated: true, completion: nil)
})
], footer: "Transfers usually arrive within 1-3 business days.")
Since this is Swift, we can provide instance methods instead of inline blocks for selections. This makes things really nice. You don't have to switch on index paths in a tableView:didSelectRowAtIndexPath:
any more!
The Row
never has access to the cell. This is by design. The Row
shouldn't care about its appearance other than specifying what will handle it. In practice, this has been really nice. Our cells have one responsibility.
There are several custom cells provided:
Value1Cell
— This is the default cell. It's a plainUITableViewCell
with the.Value1
style.Value2Cell
— PlainUITableViewCell
with the.Value2
style.SubtitleCell
— PlainUITableViewCell
with the.Subtitle
style.ButtonCell
— PlainUITableViewCell
with the.Default
style. ThetextLabel
'stextColor
is set to the cell'stintColor
.
All of these conform to Cell
. The gist of the protocol is one method:
func configure(row row: Row)
This gets called by DataSource
(which we'll look at more in a minute) to set the row on the cell. There is a default implementation provided by the protocol that simply sets the Row
's text
on the cell's textLabel
, etc. If you need to do custom things, this is a great place to hook in.
Row
also has a context
property. You can put whatever you want in here that the cell needs to know. You should try to use this as sparingly as possible.
Row
has an accessory
property that is an Accessory
enum. This has cases for all of UITableViewCellAccessoryType
. Here's a row with a checkmark:
Row(text: "Buy milk", accessory: .checkmark)
Easy enough. Some of the system accessory types are selectable (like that little i button with a circle around it). You can make those and handle the selection like this:
Row(text: "Sam Soffes", accessory: .detailButton({
// Show info about this contact
}))
Again, you could use whatever function here. Instance methods are great for this.
There is an additional case called .view
that takes a custom view. Here's a Row
with a custom accessory view:
Row(text: "My Profile", accessory: .view(someEditButton))
Section
has properties for header
and footer
. These take a Section.Extremity
. This is an enum with Title
and View
cases. Extremity
is StringLiteralConvertible
you can simply specify strings if you want titles like we did the Getting Started section.
For a custom view, you can simply specify the View
case:
Section(header: .view(yourView))
The height returned to the table view will be the view's bounds.height
so be sure it's already sized properly.
To hook up your Section
s and Row
s to a table view, simply initialize a DataSource
:
let dataSource = DataSource()
dataSource.sections = [
Section(rows: [
Row(text: "Hello")
])
]
Now assign your table view:
dataSource.tableView = tableView
Easy as that! If you modify your data source later, it will automatically update the table view for you. It is important that you don't change the table view's dataSource
or delegate
. The DataSource
needs to be those so it can handle events correctly. The purpose of Static
is to abstract all of that away from you.
There is a provided TableViewController
that sets up a DataSource
for you. Here's a short example:
class SomeViewController: TableViewController {
override func viewDidLoad() {
super.viewDidLoad()
dataSource.sections = [
Section(rows: [
Row(text: "Hi")
]),
// ...
]
}
}
Enjoy.