Active Filters: Table Views

We're continuing our look at lesser-known UIKit functionality today with UITableView & UITableViewCell. Let's see what we can find:

Blur & Vibrancy Visual Effects

We can set UIVisualEffectView backgroundView, and a vibrancy separatorEffect so our table view really shines:

let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .Dark))

tableView.backgroundView = blurView
tableView.separatorEffect = UIVibrancyEffect(forBlurEffect: blurView.effect as! UIBlurEffect)

Row Actions

We can get those awesome "swipe-to-reveal" actions from Mail.app in our own table views. We just need to implement one delegate function and return an array of UITableViewRowActions.

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
  let deployAction = UITableViewRowAction(
    style: .Default,
    title: "Deploy"
  ) { (action, indexPath) in
    // TODO: Deploy the troop at this indexPath.row
  }

  return [deployAction]
}

Adjusting to State Transitions

We can override the willTransitionToState(state:) and didTransitionToState(state:) functions in our UITableViewCell subclasses to run code when the cell begins or finishes showing the edit control or delete confirmation:

class StormtrooperCell : UITableViewCell {
  override func didTransitionToState(state: UITableViewCellStateMask) {
    super.didTransitionToState(state)

    if state == .ShowingEditControlMask { print("began editing!") }
  }
}

Multiple Selection Background View

We can set a special background view on cells that will be shown only when we our table view supports multiple selection:

multipleSelectionBackgroundView = EditingView()

Topics

#74: Static 📺

Topics

Static is a library from Venmo that makes it very simple to create simple static-content table views. Let's take a look at how to use it:

The basic idea of Static is that we'll configure what is displayed in our table view by supplying Section and Row objects to a DataSource object:

class SettingsViewController: TableViewController {
    override func viewDidLoad() {
    super.viewDidLoad()

    dataSource.sections = [
      Section(rows: [
        Row(text: "Jake Marsh", detailText: "@jakemarsh",
          cellClass: SubtitleCell.self,
          selection: { [unowned self] in
            // edit profile
          }
        ),
        Row(
          text: "Cellular Downloads",
          accessory: .View(cellularDownloadsSwitch)
        )],
        footer: .Title("Version: \(appVersion)")
      )
    ]
  }
}

Custom accessories are simple to implement with Static:

Row(text: "Enable Hyperdrive", accessory: .DetailButton({
  // configure hyperdrive settings
})),

Note how we not only give the cell a detail button, but we pass in a closure to be run when the detail button is tapped. We can even use a custom UIView as an accessory like we did with the switch in our first example.

We used a simple string as our footer here, but we can easily use a view instead:

Section(rows: [], footer: .View(footerView)).

Static does a great job separating concerns. Rows don't know anything about the actual UITableViewCells that eventually get shown to represent them. Static uses the .Value1 cell type by default and comes with a few other standard cell types. We can easily add our own by implementing the CellType protocol on any of our own UITableViewCell subclasses.

More info about Static can be found at git.io/statictv

Let's say you have a table view with some cells showing a list of crew members. It'd be great if when the user taps a cell, it would “expand” to reveal some actions that can be performed on that crew member. Let's dive in.

Setup View Hierarchy

As you can see, we lean on UIStackView pretty heavily. The constraint shown here is the one we'll be animating.

Animate the Constraint

In our cell's setSelected(animated:) function, we animate the constraint just like we covered back in Bite #9. After the animation's done, we hide/show the toolbar. This triggers the top-level stack view to recalculate it's height.

let constant: CGFloat = selected ? 30.0 : 0.0
let options = [.AllowUserInteraction, .BeginFromCurrentState]

UIView.animateWithDuration(0.3, delay: 0.0, options: options, animations: {
  self.toolbarStackViewHeightConstraint.constant = constant
  self.layoutIfNeeded()
}, completion: { completed in
  self.toolbarStackView.hidden = !selected
})

Final Product

Our view controller takes care of resizing our cells by updating the 'expanded' state of each crew member when the selection changes and a pair of calls to tableView.beginUpdates/endUpdates inside the tableView:didSelectRowAtIndexPath: and tableView:didDeselectRowAtIndexPath:.

Download the complete working project here: j.mp/bite050