Simple static table views for iOS in Swift.

Related tags

Layout Static
Overview

Static Logo

Version Status Swift Version Carthage compatible

Simple static table views for iOS in Swift. Static's goal is to separate model data from presentation. Rows and Sections 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.

Version Compatibility

Swift Version Static Version
5.0+ 4.0.0
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

Installation

Carthage

Carthage is the recommended way to install Static. Add the following to your Cartfile:

github "venmo/Static"

CocoaPods

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.

Manual

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.

Usage

An example app is included demonstrating Static's functionality.

Getting Started

To use Static, you need to define Rows and Sections to describe your data. Here's a simple example:

import Static

Section(rows: [
    Row(text: "Hello")
])

You can configure Sections and Rows 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!

Customizing Appearance

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 plain UITableViewCell with the .Value1 style.
  • Value2Cell — Plain UITableViewCell with the .Value2 style.
  • SubtitleCell — Plain UITableViewCell with the .Subtitle style.
  • ButtonCell — Plain UITableViewCell with the .Default style. The textLabel's textColor is set to the cell's tintColor.

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.

Custom Row Accessories

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))

Custom Section Header & Footer Views

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.

Working with the Data Source

To hook up your Sections and Rows 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.

Wrapping Up

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.

Comments
  • Blank section headers are displayed under iOS 11

    Blank section headers are displayed under iOS 11

    It looks like blank section headers/footers are being shown on devices running iOS 11 (as of beta 2) when a Section is initialized with nil as a header/footer extremity.

    The below screenshots were taken with the example project after changing the value of the header parameter of the first section to nil.

    | iOS 10 | iOS 11 | | ------ | ------ | | screen shot 2017-06-29 at 4 10 41 pm | screen shot 2017-06-29 at 4 10 39 pm |

    opened by nealyoung 11
  • Add ability to receive UITableViewDelegate & UIScrollViewDelegate messages from UITableView

    Add ability to receive UITableViewDelegate & UIScrollViewDelegate messages from UITableView

    Using Static for your table view solutions previously caused some limitations for certain use cases. Since the DataSource object is both the dataSource & delegate of the UITableView instance, users of the Static library were unable to receive the UITableViewDelegate methods they might need to do things like reacting to scrolling, header displays or any further actions.

    This change introduces a new weak property on DataSource: tableViewDelegate. This needs to be set during init, due to the execution timing of forwardingTarget & respondsToSelector. Using those two methods, the unimplemented UITableViewDelegate methods can get forwarded to the delegate that the user specifies, unlocking this missing functionality, mentioned in #97, #78, #72.

    Two already implemented UITableViewDelegate functions will also forward the message to the tableViewDelegate property (if it implements those methods). The other remaining function's behavior is already fully exposed by Static, so it didn't need to be changed.

    Other notes about this change/PR:

    • This is backwards compatible. Updated version based on semantic versioning.
    • Unit test added, fully covers new code.
    • Example ViewController class modified to include example of this functionality.
    opened by jaredegan 8
  • Remove reference to functions as selections

    Remove reference to functions as selections

    This was causing memory leaks as the closure referenced self, without declaring it's ownership of self. From the Swift docs: "A strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure captures the instance...[this] strong reference cycle occurs because closures, like classes, are reference types"

    By adding the [unowned self] to each selection, we remove any retain cycles.

    opened by eliperkins 8
  • Simpler TableViewController

    Simpler TableViewController

    Converted to use plain UITableViewController instead of rolling our own. I totally get the value of using a plain UIViewController with its table view as a subview of view, but that's not a decision Static should be making for its consumers in my opinion. I think the system default should be Static's default, and if you want something custom, you're welcome to customize it.

    opened by soffes 6
  • Make Row be agnostic of its presentation

    Make Row be agnostic of its presentation

    Row shouldn't know anything about how its displayed (with the exception of specifying a class to display it). This was the original design of Static. I'm proposing going back to that mindset with a few small changes.

    Remove Height

    If you want a custom height for a cell, set tableView.estimatedRowHeight and then add a constraint to cell.contentView to set its height. This is a much more flexible way to go about custom heights if you need varying heights in the same table view.

    Remove Image Tint Color

    From the very beginning, you couldn't set a label's color. This was by design. If you wanted to change the presentation of a label, you had to make a custom cell. I think this same thinking applies to images. If you want to set the color of an image, either do it before it gets to the Row or do it in the cell. Having the Row tell the cell what color it is violates its lack of understanding of presentation.


    I tried to explain my reasoning. Hopefully this doesn't seem silly. It's awesome to see people contributing to Static, but I think these two additions are going the wrong direction. Thoughts? :heart:

    opened by soffes 6
  • Upgrade to Swift 3 compatibility

    Upgrade to Swift 3 compatibility

    Took a stab at running the Xcode Swift 3 migration for #83 ; everything seemed to carry over quite well. Test suite passes, and everything looks good in the sample project as well.

    opened by chrismanderson 5
  • More documentation

    More documentation

    The note about UIKit support is the main thing here. Originally, I was going to dispatch_async to the main queue for everything that touches UIKit, but that got complicated quick. I think it makes things harder to work with since everything is async. I'd rather consumers treat DataSource like any other UIKit class and only access it from the main queue.

    Thoughts? @ayanonagon @hyperspacemark

    opened by soffes 5
  • Bugfix: SectionFooterView AutoDimension Support

    Bugfix: SectionFooterView AutoDimension Support

    Problem:

    1. I noticed our app was manually laying out subviews in our sectionFooters when I didn't see the need to. (Given auto-layout/intrinsic size support)

    eg.. (Unnecessarily maintaining and calculation including insets/frame)

        override func layoutSubviews() {
            super.layoutSubviews()
            var frame = self.frame
            frame.size = CGSize(width: frame.size.width, height: signUpInstructionsLabel.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height + textInsets.top + textInsets.bottom)
            self.frame = frame
        }
    
    1. After recent changes, Group tableView's which had nil section extremity defaulted to zero, which drops the standard spacing between sections. (Using current master)
    screen shot 2017-08-28 at 9 39 07 pm

    Notable info: Statics currently has deploy target of iOS8, which did not yet support automatic sectionFooterView dimensioning.

    Solution:

    • Offer autosizing option which uses UITableViewAutomaticDimension rather than view's original bounds height. Retain original .view(_) case in the event some clients were already sizing beforehand limiting integration breaks.
    • Bump deploy target to iOS9 for clean integration.
    • Revert 2 previous PRs focused on same issue

    Testing: Tested with simulators

    • [x] iOS11 beta5 on iPhone 7 (Friendly)
    • [x] iOS 10.3 on iPhone 6. (Requires owner to set estimatedSectionHeader/FooterHeights to > 0

    Did not test on iOS 9

    opened by dmiluski 4
  • add possibility to react on will display header and footer

    add possibility to react on will display header and footer

    As reported in #78 I decided to make small PR with possibility to quickly access two methods of UITableViewDelegate:

    • tableView(_:, willDisplayHeaderView:, forSection:)
    • tableView(_:, willDisplayFooterView:, forSection:)

    The reason we need to have the simplest possibility to access those method is that UIKit somehow overrides header view appearance (for UITableViewHeaderFooterVIew) and those two methods can be used for configuring header/footer correctly.

    The solution here is to provide two closures:

    • dataSource.willDisplayHeader: ((UIView, Int) -> Void)?
    • dataSource.willDisplayFooter: ((UIView, Int) -> Void)?

    I wish we could have better architecture for that than just a properties with closures, but this solution fulfills two most important requirements:

    • provides expected feature
    • don't introduce any breaking change into Static
    opened by rad3ks 4
  • Version 1.0

    Version 1.0

    I'd love to get a release with #60 but that removes functionality. Should the next version be 1.0? There's a few other breaking changes I'd like to make before we 1.0 (order of initializer params, maybe remove UUID from Row).

    Anyway, 1.0 feels like such a big commitment. Are we okay making a breaking change before 1.0 (like CocoaPods :P) and just do 0.3.0 with what's in master and defer the big commitment of 1.0?

    What do you think @eliperkins @ayanonagon @hyperspacemark (and anyone else)?

    question 
    opened by soffes 4
  • Allow replacing the data source in TableViewController

    Allow replacing the data source in TableViewController

    Tested this in the demo app and it all works as expected.

    Built this to make subclassing a bit easier. I'm working on a thing that has a segmented control at the top. Changing the segment changes the data in the table view. This change makes implementing this really easy.

    opened by soffes 4
  • Add support for more modern (non-deprecated) table view swipe API

    Add support for more modern (non-deprecated) table view swipe API

    UITableViewRowAction and it’s associated methods have been deprecated in favor of the new UISwipeActionsConfiguration object which can be configured on the trailing and leading edges of a table view cell.

    This API is available on iOS 11.0 onwards. The cleanest way to support this is to bump the deployment target to iOS 11.

    opened by cgossain 0
  • Still maintained?

    Still maintained?

    There are a number of issues from a couple years ago plus PRs ready for review that haven't been looked at in awhile. Just wondering if this library is still being actively maintained.

    opened by colinhumber 0
  • [Question] Change detailText Without Storing Row

    [Question] Change detailText Without Storing Row

    Hi, I am looking for 1 of these things:

    • A way to set detailText inside selection

    • Another library with the amazing simplicity, but not just for static tables.

        yp.yearSelected.subscribe(onNext: { year in
            self.settings.year = year
            // Set Value Here!
        }).disposed(by: self.disposeBag)
      
        ...Row(text: "Change Year", detailText: year, selection: {
              self.present(yp, animated: true)
        }, accessory: .disclosureIndicator)...
      

    image

    opened by reteps 0
  • Row with SwitchAccessory loses its state after scrolling the table

    Row with SwitchAccessory loses its state after scrolling the table

    Hello!

    I am using a Row with UISwitch as seen in the example project here. However when scrolling down the table and then up again, the switch loses its on state.

    Tested straight from example project:

    Is this due to cell reusing? How can I preserve switch state on scroll?

    Thanks

    EDIT: This is the way I'm using it. Am I doing something wrong?

    var value: Bool = false

    Row(text: "UISwitch", accessory: .switchToggle(value: self.value) { [unowned self] newValue in
         self.value = newValue
    })
    
    opened by n3d1117 10
Releases(v4.0.0)
Swift-picker-views - inline single and multi picker views for UIKit. Without tableview! Easy and simple

swift-picker-views Inline single and multiple picker views for UIKit. No tablevi

IBRAHIM YILMAZ 2 Jan 31, 2022
Reusable GridView with excellent performance and customization that can be time table, spreadsheet, paging and more.

GridView GridView can tile the view while reusing it. It has an API like UIKit that works fast. Even when device rotates it smoothly relayout. Appetiz

Kyohei Ito 830 Dec 23, 2022
Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. [iOS/macOS/tvOS/CALayer]

Extremely Fast views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainabl

layoutBox 2.1k Dec 22, 2022
✂ Easy to use and flexible library for manually laying out views and layers for iOS and tvOS. Supports AsyncDisplayKit.

ManualLayout Table of Contents Installation Usage API Cheat Sheet Installation Carthage Add the following line to your Cartfile. github "isair/ManualL

Baris Sencan 280 Sep 29, 2022
Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast

Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. [iOS/macOS/tvOS/CALayer]

layoutBox 2.1k Jan 2, 2023
Powerful autolayout framework, that can manage UIView(NSView), CALayer and not rendered views. Not Apple Autolayout wrapper. Provides placeholders. Linux support.

CGLayout Powerful autolayout framework, that can manage UIView(NSView), CALayer and not rendered views. Has cross-hierarchy coordinate space. Implemen

Koryttsev Denis 45 Jun 28, 2022
The fast path to autolayout views in code

NorthLayout The fast path to autolayout views in code Talks https://speakerdeck.com/banjun/auto-layout-with-an-extended-visual-format-language at AltC

banjun 36 Jul 15, 2022
Compose is a library that helps you compose complex and dynamic views.

Compose is a data driven library that will help compose your complex and dynamic Views. It helps you create complex and dynamic Views using easy and s

OLX Brasil 123 Jun 9, 2021
Expose layout margins and readable content width to SwiftUI's Views

SwiftUI Layout Guides This micro-library exposes UIKit's layout margins and readable content guides to SwiftUI. Usage Make a view fit the readable con

Thomas Grapperon 26 Dec 23, 2022
Apple provides us two ways to use UIKit views in SwiftUI

RepresentableKit Apple provides us two ways to use UIKit views in SwiftUI: UIVie

YUMEMI Inc. 43 Dec 26, 2022
An experiment creating a particle emitter using the new TimelineView and Canvas views in SwiftUI

Particle Emitter An experiment creating a particle emitter using the new Timelin

Emilio Peláez 8 Nov 11, 2022
Instagram clone, the main focus of the app is the seamless swipe between views that we see on instagram

InstaSwipe Instagram clone, the main focus of the app is the seamless swipe betw

Stavros Pachoundakis 1 Dec 15, 2022
StoryboardUsingCustomViews - Storyboard Using Custom Views

Storyboard Using Custom Views Vista creada con: Storyboard + Constraints + Progr

Vanesa Giselle Korbenfeld 0 Jan 19, 2022
ResponderChainDemo - Learned how to use responder chain for communication between the views

ResponderChainDemo Learned how to use responder chain for communication between

Prashant Gaikwad 1 Sep 30, 2022
SwiftUI views that arrange their children in a flow layout.

SwiftUI Flow SwiftUI views that arrange their children in a flow layout. HFlow A view that arranges its children in a horizontal flow. Usage ScrollVie

Ciaran O'Brien 114 Jan 5, 2023
SwiftUI views that arrange their children in a Pinterest-like layout

SwiftUI Masonry SwiftUI views that arrange their children in a Pinterest-like layout. HMasonry A view that arranges its children in a horizontal mason

Ciaran O'Brien 88 Dec 27, 2022
Reframing SwiftUI Views. A collection of tools to help with layout.

Overview A Swift Package with a collection of SwiftUI framing views and tools to help with layout. Size readers like WidthReader, HeightReader, and on

Ryan Lintott 84 Dec 16, 2022
SwiftUI stack views with paged scrolling behaviour.

SwiftUI PageView SwiftUI stack views with paged scrolling behaviour. HPageView A view that arranges its children in a horizontal line, and provides pa

Ciaran O'Brien 41 Dec 21, 2022
Arrange views in your app’s interface using layout tools that SwiftUI provides.

Composing custom layouts with SwiftUI Arrange views in your app's interface using layout tools that SwiftUI provides. Overview This sample app demonst

Apple Sample Code 0 Jun 9, 2022