Skip to content

Instantly share code, notes, and snippets.

@xtravar
Last active January 21, 2017 21:17
Show Gist options
  • Save xtravar/8425d0401e1ed30532415543577b14d4 to your computer and use it in GitHub Desktop.
Save xtravar/8425d0401e1ed30532415543577b14d4 to your computer and use it in GitHub Desktop.
import Foundation
import UIKit
// emulates table view sticky headers and demonstrates content inset usage
public class FakeTableViewController: UIViewController, UIScrollViewDelegate {
// set up views neatly using anonymous closures
let scrollView: UIScrollView = {
let v = UIScrollView()
v.backgroundColor = UIColor.lightGray
return v
}()
// this will be the header that gets 'scrolled over', kind of like a refresh control
let headerView: UIView = {
let v = UIView()
v.backgroundColor = UIColor.blue.withAlphaComponent(0.5)
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
// this will be a sticky section header ala UITableView plain
let sectionHeaderView: UIView = {
let v = UIView()
v.backgroundColor = UIColor.red.withAlphaComponent(0.5)
v.translatesAutoresizingMaskIntoConstraints = false;
return v
}()
// some content to take up the screen
let fakeContentView: UIView = {
let v = UIView()
v.backgroundColor = UIColor.white
v.translatesAutoresizingMaskIntoConstraints = false;
return v
}()
// the constraints we need to modify later
var headerTopConstraint: NSLayoutConstraint!
var contentWidthConstraint: NSLayoutConstraint!
// we don't want no nibs
public override func loadView() {
// these lines happen automatically in UITableViewController/UICollectionViewController subclasses
self.scrollView.delegate = self;
self.scrollView.alwaysBounceVertical = true
// cause it's a fake table
self.scrollView.isDirectionalLockEnabled = true
// add views and fix to edges
// order matters for layering
[self.headerView, self.fakeContentView, self.sectionHeaderView].forEach {
self.scrollView.addSubview($0)
$0.leftAnchor.constraint(equalTo: self.scrollView.leftAnchor).isActive = true
$0.rightAnchor.constraint(equalTo: self.scrollView.rightAnchor).isActive = true
}
// fixed constraints - heights
self.headerView.heightAnchor.constraint(equalToConstant: 50).isActive = true
self.sectionHeaderView.heightAnchor.constraint(equalToConstant: 20).isActive = true
self.fakeContentView.heightAnchor.constraint(equalToConstant: 1000).isActive = true
// fix content to top and bottom
self.fakeContentView.topAnchor.constraint(equalTo: self.scrollView.topAnchor).isActive = true
self.fakeContentView.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor).isActive = true
// keep the section bottom fixed to the top of the content *if it can* be
do {
let c = self.sectionHeaderView.bottomAnchor.constraint(equalTo: self.fakeContentView.topAnchor)
c.priority = UILayoutPriorityDefaultLow
c.isActive = true
}
// require that it's no higher than the top of the background header (which is the top of the content window)
self.sectionHeaderView.topAnchor.constraint(greaterThanOrEqualTo: self.headerView.topAnchor).isActive = true
// edge insets
self.scrollView.contentInset = UIEdgeInsets(top: 70, left: 0, bottom: 0, right: 0)
self.scrollView.scrollIndicatorInsets = UIEdgeInsets(top: 70, left: 0, bottom: 0, right: 0)
// mutable constraints
// make sure width of content exists, otherwise size = 0,0 - table views do not have this problem
self.contentWidthConstraint = {
let c = self.headerView.widthAnchor.constraint(equalToConstant: 0)
c.isActive = true
return c
}()
// keep the 'behind' header fixed
self.headerTopConstraint = {
let c = self.headerView.topAnchor.constraint(equalTo: self.scrollView.topAnchor)
c.isActive = true
return c
}()
// always set self.view last and don't use it until then
self.view = self.scrollView
}
public override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
// this forces the scrolling content to fill the space (width) given to the view controller
self.contentWidthConstraint.constant = self.view.frame.width
// TODO: take into account the edge insets of the VC w/r/t status bar and UINavigationBar
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.headerTopConstraint.constant = scrollView.contentOffset.y
// adjust scroll indicator offsets here to match content bouncing
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment