diff --git a/Example/Base.lproj/Main.storyboard b/Example/Base.lproj/Main.storyboard index 5ee3384..ec36fc4 100644 --- a/Example/Base.lproj/Main.storyboard +++ b/Example/Base.lproj/Main.storyboard @@ -1,63 +1,47 @@ - + - + + - - + + - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + @@ -70,7 +54,7 @@ - + diff --git a/Example/PlaceholderViews/BasicPlaceholderView.swift b/Example/PlaceholderViews/BasicPlaceholderView.swift index c33d339..3b351bf 100644 --- a/Example/PlaceholderViews/BasicPlaceholderView.swift +++ b/Example/PlaceholderViews/BasicPlaceholderView.swift @@ -30,17 +30,10 @@ class BasicPlaceholderView: UIView { centerView.translatesAutoresizingMaskIntoConstraints = false self.addSubview(centerView) - let views = ["centerView": centerView, "superview": self] - let vConstraints = NSLayoutConstraint.constraintsWithVisualFormat("V:[superview]-(<=1)-[centerView]", - options: .AlignAllCenterX, - metrics: nil, - views: views) - let hConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:[superview]-(<=1)-[centerView]", - options: .AlignAllCenterY, - metrics: nil, - views: views) - self.addConstraints(vConstraints) - self.addConstraints(hConstraints) + let vConstraint = NSLayoutConstraint(item: centerView, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1.0, constant: 0.0) + let hConstraint = NSLayoutConstraint(item: centerView, attribute: .CenterY, relatedBy: .Equal, toItem: self, attribute: .CenterY, multiplier: 1.0, constant: 0.0) + self.addConstraint(vConstraint) + self.addConstraint(hConstraint) } } diff --git a/Example/PlaceholderViews/EmptyView.swift b/Example/PlaceholderViews/EmptyView.swift index 376199d..4b76d30 100644 --- a/Example/PlaceholderViews/EmptyView.swift +++ b/Example/PlaceholderViews/EmptyView.swift @@ -15,7 +15,7 @@ class EmptyView: BasicPlaceholderView { override func setupView() { super.setupView() - backgroundColor = UIColor.whiteColor() + backgroundColor = UIColor.blueColor() label.text = "No Content." label.translatesAutoresizingMaskIntoConstraints = false @@ -26,7 +26,7 @@ class EmptyView: BasicPlaceholderView { let vConstraints = NSLayoutConstraint.constraintsWithVisualFormat("V:|-[label]-|", options: .AlignAllCenterX, metrics: nil, views: views) centerView.addConstraints(hConstraints) - centerView.addConstraints(vConstraints) + centerView.addConstraints(vConstraints) } } diff --git a/Example/PlaceholderViews/LoadingView.swift b/Example/PlaceholderViews/LoadingView.swift index f8ed028..71fbdc8 100644 --- a/Example/PlaceholderViews/LoadingView.swift +++ b/Example/PlaceholderViews/LoadingView.swift @@ -34,7 +34,7 @@ class LoadingView: BasicPlaceholderView, StatefulPlaceholderView { centerView.addConstraints(hConstraints) centerView.addConstraints(vConstraintsLabel) - centerView.addConstraints(vConstraintsActivity) + centerView.addConstraints(vConstraintsActivity) } func placeholderViewInsets() -> UIEdgeInsets { diff --git a/Example/ViewController.swift b/Example/ViewController.swift index 8eb34fd..09e1c48 100644 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -9,17 +9,15 @@ import UIKit import StatefulViewController -class ViewController: UIViewController, StatefulViewController { +class ViewController: UITableViewController, StatefulViewController { var dataArray = [String]() - let refreshControl = UIRefreshControl() - @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() // Setup refresh control - refreshControl.addTarget(self, action: #selector(refresh), forControlEvents: .ValueChanged) - tableView.addSubview(refreshControl) + refreshControl = UIRefreshControl() + refreshControl?.addTarget(self, action: #selector(refresh), forControlEvents: .ValueChanged) // Setup placeholder views loadingView = LoadingView(frame: view.frame) @@ -40,27 +38,27 @@ class ViewController: UIViewController, StatefulViewController { if (lastState == .Loading) { return } startLoading(completion: { - print("completaion startLoading -> loadingState: \(self.currentState.rawValue)") + print("completion startLoading -> loadingState: \(self.currentState.rawValue)") }) print("startLoading -> loadingState: \(self.lastState.rawValue)") // Fake network call dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(3 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { // Success - self.dataArray = ["Merlot", "Sauvignon Blanc", "Blaufränkisch", "Pinot Nior"] - self.tableView.reloadData() - self.endLoading(error: nil, completion: { - print("completion endLoading -> loadingState: \(self.currentState.rawValue)") - }) - print("endLoading -> loadingState: \(self.lastState.rawValue)") +// self.dataArray = ["Merlot", "Sauvignon Blanc", "Blaufränkisch", "Pinot Nior"] +// self.tableView.reloadData() +// self.endLoading(error: nil, completion: { +// print("completion endLoading -> loadingState: \(self.currentState.rawValue)") +// }) +// print("endLoading -> loadingState: \(self.lastState.rawValue)") // Error - //self.endLoading(error: NSError(domain: "foo", code: -1, userInfo: nil)) +// self.endLoading(error: NSError(domain: "foo", code: -1, userInfo: nil)) // No Content - //self.endLoading(error: nil) + self.endLoading(error: nil) - self.refreshControl.endRefreshing() + self.refreshControl?.endRefreshing() } } @@ -82,16 +80,21 @@ extension ViewController { } -extension ViewController: UITableViewDataSource { +extension ViewController { - func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return dataArray.count + override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return dataArray.count + 1 } - func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("textCell", forIndexPath: indexPath) - cell.textLabel?.text = dataArray[indexPath.row] + cell.textLabel?.text = "HELLO!" +// dataArray[indexPath.row] return cell } + override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { + print("CLICKED ROW!") + } + } diff --git a/StatefulViewController/StatefulViewControllerImplementation.swift b/StatefulViewController/StatefulViewControllerImplementation.swift index a204029..3da9e59 100644 --- a/StatefulViewController/StatefulViewControllerImplementation.swift +++ b/StatefulViewController/StatefulViewControllerImplementation.swift @@ -15,7 +15,6 @@ extension BackingViewProvider where Self: UIView { } } - // MARK: Default Implementation StatefulViewController /// Default implementation of StatefulViewController for UIViewController @@ -120,10 +119,21 @@ extension StatefulViewController { } } +extension StatefulViewController where Self: UITableViewController { + + public var stateMachine: ViewStateMachine { + return associatedObject(self, key: &stateMachineKey) { [unowned self] in + return ContainerViewStateMachine(view: self.view) + } + } + +} + // MARK: Association private var stateMachineKey: UInt8 = 0 +private var tableViewControllerStateContainerViewKey: UInt8 = 1 private func associatedObject(host: AnyObject, key: UnsafePointer, initial: () -> T) -> T { var value = objc_getAssociatedObject(host, key) as? T diff --git a/StatefulViewController/ViewStateMachine.swift b/StatefulViewController/ViewStateMachine.swift index 1e83fab..832dea4 100644 --- a/StatefulViewController/ViewStateMachine.swift +++ b/StatefulViewController/ViewStateMachine.swift @@ -149,7 +149,7 @@ public class ViewStateMachine { newView.alpha = animated ? 0.0 : 1.0 newView.translatesAutoresizingMaskIntoConstraints = false self.view.addSubview(newView) - + let insets = (newView as? StatefulPlaceholderView)?.placeholderViewInsets() ?? UIEdgeInsets() let metrics = ["top": insets.top, "bottom": insets.bottom, "left": insets.left, "right": insets.right] let views = ["view": newView] @@ -205,3 +205,54 @@ public class ViewStateMachine { } } } + +/// +/// A state machine that manages a set of views by adding the state's view to a managed container view. +/// +/// There are two possible states: +/// * Show a specific placeholder view, represented by a key +/// * Hide all managed views +/// +public class ContainerViewStateMachine: ViewStateMachine { + + private let containerSuperview: UIView + + public override init(view: UIView, states: [String : UIView]?) { + self.containerSuperview = view + + let containerView = StateViewContainerView(frame: self.containerSuperview.frame) + containerView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] + containerView.backgroundColor = UIColor.clearColor() + containerView.layer.zPosition = self.containerSuperview.layer.zPosition + 1.0 + + super.init(view: containerView, states: states) + } + + private override func showViewWithKey(state: String, animated: Bool, completion: (() -> ())?) { + self.view.frame = self.containerSuperview.frame + self.containerSuperview.addSubview(self.view) + + super.showViewWithKey(state, animated: animated, completion: completion) + } + + private override func hideAllViews(animated animated: Bool, completion: (() -> ())?) { + super.hideAllViews(animated: animated) { + completion?() + self.view.removeFromSuperview() + } + } +} + +private class StateViewContainerView: UIView { + + private override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { + for view in self.subviews { + if !view.hidden && view.alpha > 0 && view.userInteractionEnabled && + view.pointInside(self.convertPoint(point, toView:view), withEvent:event) { + return true + } + } + return false + } + +}