Skip to content
This repository has been archived by the owner on Jun 5, 2019. It is now read-only.

Archived documentation

Sahand Nayebaziz edited this page May 19, 2016 · 11 revisions

This page contains the documentation provided with past releases of StateView.

Contents

1.2

Getting Started

Initializing a StateView

StateView is simply a subclass of UIView. You can add a StateView to any UIView as a subview or set a StateView to be the view of any UIViewController.

You can create a new view controller with a root view set to your StateView subclass by creating a subclass of StateViewController. StateViewController is a simple subclass of UIViewController that adds the StateView subclass you provide to its view property.

To create a new view controller with your StateView subclass as the root view, create a subclass of StateViewController and provide your StateView subclass in the viewDidLoad method:

class HomeViewController: StateViewController {
	
	override func viewDidLoad() {
		super.viewDidLoad()
		rootView = HomeView(parentViewController: self)
	}
}

To add your StateView subclass to an existing UIView, simply add your subclass with addSubview() and call the renderDeep() instance method on the new instance of your subclass:

let homeView = HomeView(parentViewController: self)
view.addSubview(homeView)
homeView.renderDeep()

The call to renderDeep() will run the first pass of render() and set the view up to update itself when your data changes. All children of this StateViewwill update themselves without any further setup. Calling renderDeep is only required on the first StateView subclass in a view hierarchy if that instance was added manually.

Adding subviews

You can use place() to add subviews to your StateView. You can add any UIView or StateView as a subview.

On initial render and anytime your data changes, render() is called. Any view placed with place() in a run of render() will be added as a subview. If the previous run of render() also placed that view, the existing view will be preserved in the view hierarchy and passed new props to update itself with. If a view was placed in the previous run of render() and then not placed in the following run of render(), the following run of render() will cause that view to be removed from the view hierarchy.

Summary: Simply place() a view to add it as a subview. Simply don't place() that view in a following run of render() to automatically remove that view. Decide which views to place and which not to place with conditional operators, functions, and anything else you can imagine in your render() method.

You can call place() with three parameters.

If you are placing another StateView, place() takes the type of the StateView, the unique key you'd like to give that view as a String, and AutoLayout constraints in the form of a SnapKit ConstraintMaker.

let shippingSummary = place(ShippingSummary.self, "shipping summary") { make in
  make.size.equalTo(self)
  make.center.equalTo(self)
}

If you are placing a UIView subclass that isn't a StateView, place() takes the initialized view as a UIView, the unique key you'd like to give that view as a String, and AutoLayout constraints in the form of a SnapKit ConstraintMaker.

let button = UIButton(type: .System)
button.setTitle("Select image", forState: .Normal)
place(button, key: "button") { make in
	make.size.equalTo(100)
	make.center.equalTo(self)
}

Your StateView subclass can initialize another StateView subclass automatically, so you can simply pass the type of that StateView to place(). Your StateView subclass can place an already initialized UIView subclass that isn't a StateView so you can configure that UIView subclass before placing it. This is helpful for doing something like setting the text of a UILabel. You can set the properties of your UILabel inside the run of render() and place() that UILabel once it's ready to go.

Using State and Props

You can decide which views to place with place() in render() and which values to pass to placed views by looking at state and props.

You can keep values that describe the state of a view in state:

self.state["selectedSong"] = Song(title: "This Must Be My Dream", artist: "The 1975")

self.state["displayingThankYou"] = true

self.state["selectedShipping"] = nil

You are encouraged to keep values in state. The more descriptive you can be with keys and values in state that work for you, the better. You are also encouraged to store objects of any types that work for you in state, as state is a dictionary with type [String: AnyObject].

You can use state as a dependable place you can look to see how your view looks onscreen without actually having to see it there. Anything in state that is used to configure your StateView or any of its children is always up-to-date in your view hierarchy. Simply update your state and any view that depends on state will update itself.

You can pass a value from state or elsewhere to one of your subviews by passing the value as a prop:

let nextButton = place(NextButton.self, "next button") { make in
  make.center.equalTo(self)
  make.height.equalTo(44)
  make.width.equalTo(120)
}
nextButton.prop(forKey: "enabled", is: true)
nextButton.prop(forKey: "title", is: "Continue")



let shippingSummary = place(ShippingSummary.self, "shipping summary") { make in
  make.size.equalTo(self)
  make.center.equalTo(self)
}
let selectedShipping = self.state["selectedShipping"]
shippingSummary.prop(forKey: "shipping", is: selectedShipping)

You can then access any of the values passed in props in that subview by accessing self.props. You are encouraged to pass objects of any types that work for you in props, as props is a dictionary with type [String: AnyObject].

You can use prop(forKey: String, isLinkedToKeyInState: String) -> Void as a shortcut to pass a value in state as a prop .

let shippingSummary = place(ShippingSummary.self, "shipping summary") { make in
  make.width.equalTo(self)
  make.height.equalTo(200)
  make.centerX.equalTo(self)
  make.bottom.equalTo(self)
}
shippingSummary.prop(forKey: "shipping", isLinkedToKeyInState: "selectedShipping")

This way you can easily connect one view's use of a value in state with its subview's use of that same value.

You can communicate in the opposite direction, from subview to parent view, by passing the subview a function in props. You are encouraged to pass objects of any types that work for you, as a subview can receive a function prop with type ([String: AnyObject]->Void). You can pass a function that can be called to say that something was done, or one that can be given a selection from a group of items, or one that can do anything else you can imagine.

To pass values from a subview back to its parent view, pass a function as a prop:

let canvas = place(CanvasView.self, key: "canvas") { make in
	make.size.equalTo(self)
	make.center.equalTo(self)
}
canvas.prop(forKey: "image", isLinkedToKeyInState: "selectedImage")
canvas.prop(forKey: "didPickImage") { values in
	if let image = values["image"] as? UIImage {
		self.state["selectedImage"] = image
	}
}

You can then access and call that function from your subview by accessing self.props.

You can easily accept a value in your StateView coming back from a function prop, set that value to be the value of a relevant key in state, and watch your views update themselves at the arrival of the new value.

Summary: Simply update your state, or a computed value outside of state that you are passing as a prop in your render() method, and your view and its subviews will update themselves. render() is a pure function of state and props. This means that given the same state and props, render() will always produce the same result.

Documentation

StateView

A UIView subclass that uses a ShadowView and a collection of methods, including its own render() and resolveProps() to update its subviews when its state property is set to a new value.

StateView class
A subclass of UIView.

shadow property
An instance of ShadowView that is initialized automatically. shadow keeps an always up-to-date record of views that have been added to this instance of StateView and orchestrates the addition, removal, and updating of subviews in an instance of StateView.

props property
A dictionary with type [String: AnyObject] that contains any values passed in from the parent view of this instance of StateView.

state property
A dictionary with type [String: AnyObject] that contains values set by this instance of StateView. Values saved in state should describe the current state of this view as completely as possible and be used in the render() method of this instance of StateView to influence the content and inclusion of subviews.

parentViewController property
The UIViewController that is displaying the hierarchy of views containing this instance of StateView. Use this property to make calls to methods like presentViewController().

getInitialState method
Returns a dictionary with type [String: AnyObject] that can be used in place of the default empty dictionary used as initial state. Use this method to set values in state that are inherently true of the state of view until something changes, or values that describe the initial state of a view well and will be changed throughout the view's lifetime.

renderDeep method
An internal method that calls render() and then shadow once render() has completed. Call this method once on an instance of StateView if you are adding a StateView to a UIView.

render method
A method that is called anytime there is a change to state. Override this method to place subviews in this instance of StateView. You are encouraged to include conditional statements and calls to your own synchronous functions to decide which views to place as subviews each time render() is called.

resolveProps method
An internal method that resolves any StateViewPropWithStateLink that came from a call to prop(forKey: StateKey, isLinkedToKeyInState: String) into a StateViewProp with a value by getting the value in state for the linked key.

place method
A method that includes a subview to be added (or kept, if placed in the previous run of render()) after the current run of render() is completed.

This method differs slightly depending on whether you would like to place a StateView or a UIView. If you are placing a StateView, this method is place(type: StateView.Type, key: String, constraints: ((make: ConstraintMaker -> Void)) -> ShadowViewElement). If you are placing a UIView, initialize the UIView first and then use place(view: UIView, key: String, constraints: ((make: ConstraintMaker) -> Void)) -> ShadowViewElement).

1.0

Getting Started

Initializing a StateView

StateView is simply a subclass of UIView. You can add a StateView to any UIView as a subview or set a StateView to be the view of any UIViewController.

You can create a new view controller with a root view set to your StateView subclass by creating a subclass of StateViewController. StateViewController is a simple subclass of UIViewController that adds the StateView subclass you provide to its view property.

To create a new view controller with your StateView subclass as the root view, create a subclass of StateViewController and provide your StateView subclass in the viewDidLoad method:

class HomeViewController: StateViewController {
	
	override func viewDidLoad() {
		super.viewDidLoad()
		rootView = HomeView(parentViewController: self)
	}
}

To add your StateView subclass to an existing UIView, simply add your subclass with addSubview() and call the renderDeep() instance method on the new instance of your subclass:

let homeView = HomeView(parentViewController: self)
view.addSubview(homeView)
homeView.renderDeep()

The call to renderDeep() will run the first pass of render() and set the view up to update itself when your data changes. All children of this StateViewwill update themselves without any further setup. Calling renderDeep is only required on the first StateView subclass in a view hierarchy if that instance was added manually.

Adding subviews

You can use place() to add subviews to your StateView. You can add any UIView or StateView as a subview.

On initial render and anytime your data changes, render() is called. Any view placed with place() in a run of render() will be added as a subview. If the previous run of render() also placed that view, the existing view will be preserved in the view hierarchy and passed new props to update itself with. If a view was placed in the previous run of render() and then not placed in the following run of render(), the following run of render() will cause that view to be removed from the view hierarchy.

Summary: Simply place() a view to add it as a subview. Simply don't place() that view in a following run of render() to automatically remove that view. Decide which views to place and which not to place with conditional operators, functions, and anything else you can imagine in your render() method.

You can call place() with three parameters.

If you are placing another StateView, place() takes the type of the StateView, the unique key you'd like to give that view as a String, and AutoLayout constraints in the form of a SnapKit ConstraintMaker.

let shippingSummary = place(ShippingSummary.self, "shipping summary") { make in
  make.size.equalTo(self)
  make.center.equalTo(self)
}

If you are placing a UIView subclass that isn't a StateView, place() takes the initialized view as a UIView, the unique key you'd like to give that view as a String, and AutoLayout constraints in the form of a SnapKit ConstraintMaker.

let button = UIButton(type: .System)
button.setTitle("Select image", forState: .Normal)
place(button, key: "button") { make in
	make.size.equalTo(100)
	make.center.equalTo(self)
}

Your StateView subclass can initialize another StateView subclass automatically, so you can simply pass the type of that StateView to place(). Your StateView subclass can place an already initialized UIView subclass that isn't a StateView so you can configure that UIView subclass before placing it. This is helpful for doing something like setting the text of a UILabel. You can set the properties of your UILabel inside the run of render() and place() that UILabel once it's ready to go.

Using State and Props

You can decide which views to place with place() in render() and which values to pass to placed views by looking at state and props.

You can keep values that describe the state of a view in state:

self.state["selectedSong"] = Song(title: "This Must Be My Dream", artist: "The 1975")

self.state["displayingThankYou"] = true

self.state["selectedShipping"] = nil

You are encouraged to keep values in state. The more descriptive you can be with keys and values in state that work for you, the better. You are also encouraged to store objects of any types that work for you in state, as state is a dictionary with type [String: AnyObject].

You can use state as a dependable place you can look to see how your view looks onscreen without actually having to see it there. Anything in state that is used to configure your StateView or any of its children is always up-to-date in your view hierarchy. Simply update your state and any view that depends on state will update itself.

You can pass a value from state or elsewhere to one of your subviews by passing the value as a prop:

let nextButton = place(NextButton.self, "next button") { make in
  make.center.equalTo(self)
  make.height.equalTo(44)
  make.width.equalTo(120)
}
nextButton.prop(forKey: "enabled", is: true)
nextButton.prop(forKey: "title", is: "Continue")



let shippingSummary = place(ShippingSummary.self, "shipping summary") { make in
  make.size.equalTo(self)
  make.center.equalTo(self)
}
let selectedShipping = self.state["selectedShipping"]
shippingSummary.prop(forKey: "shipping", is: selectedShipping)

You can then access any of the values passed in props in that subview by accessing self.props. You are encouraged to pass objects of any types that work for you in props, as props is a dictionary with type [String: AnyObject].

You can use prop(forKey: String, isLinkedToKeyInState: String) -> Void as a shortcut to pass a value in state as a prop .

let shippingSummary = place(ShippingSummary.self, "shipping summary") { make in
  make.width.equalTo(self)
  make.height.equalTo(200)
  make.centerX.equalTo(self)
  make.bottom.equalTo(self)
}
shippingSummary.prop(forKey: "shipping", isLinkedToKeyInState: "selectedShipping")

This way you can easily connect one view's use of a value in state with its subview's use of that same value.

You can communicate in the opposite direction, from subview to parent view, by passing the subview a function in props. You are encouraged to pass objects of any types that work for you, as a subview can receive a function prop with type ([String: AnyObject]->Void). You can pass a function that can be called to say that something was done, or one that can be given a selection from a group of items, or one that can do anything else you can imagine.

To pass values from a subview back to its parent view, pass a function as a prop:

let canvas = place(CanvasView.self, key: "canvas") { make in
	make.size.equalTo(self)
	make.center.equalTo(self)
}
canvas.prop(forKey: "image", isLinkedToKeyInState: "selectedImage")
canvas.prop(forKey: "didPickImage") { values in
	if let image = values["image"] as? UIImage {
		self.state["selectedImage"] = image
	}
}

You can then access and call that function from your subview by accessing self.props.

You can easily accept a value in your StateView coming back from a function prop, set that value to be the value of a relevant key in state, and watch your views update themselves at the arrival of the new value.

Summary: Simply update your state, or a computed value outside of state that you are passing as a prop in your render() method, and your view and its subviews will update themselves. render() is a pure function of state and props. This means that given the same state and props, render() will always produce the same result.

Documentation

StateView

A UIView subclass that uses a ShadowView and a collection of methods, including its own render() and resolveProps() to update its subviews when its state property is set to a new value.

StateView class
A subclass of UIView.

shadow property
An instance of ShadowView that is initialized automatically. shadow keeps an always up-to-date record of views that have been added to this instance of StateView and orchestrates the addition, removal, and updating of subviews in an instance of StateView.

props property
A dictionary with type [String: AnyObject] that contains any values passed in from the parent view of this instance of StateView.

state property
A dictionary with type [String: AnyObject] that contains values set by this instance of StateView. Values saved in state should describe the current state of this view as completely as possible and be used in the render() method of this instance of StateView to influence the content and inclusion of subviews.

parentViewController property
The UIViewController that is displaying the hierarchy of views containing this instance of StateView. Use this property to make calls to methods like presentViewController().

getInitialState method
Returns a dictionary with type [String: AnyObject] that can be used in place of the default empty dictionary used as initial state. Use this method to set values in state that are inherently true of the state of view until something changes, or values that describe the initial state of a view well and will be changed throughout the view's lifetime.

renderDeep method
An internal method that calls render() and then shadow once render() has completed. Call this method once on an instance of StateView if you are adding a StateView to a UIView.

render method
A method that is called anytime there is a change to state. Override this method to place subviews in this instance of StateView. You are encouraged to include conditional statements and calls to your own synchronous functions to decide which views to place as subviews each time render() is called.

resolveProps method
An internal method that resolves any StateViewPropWithStateLink that came from a call to prop(forKey: StateKey, isLinkedToKeyInState: String) into a StateViewProp with a value by getting the value in state for the linked key.

place method
A method that includes a subview to be added (or kept, if placed in the previous run of render()) after the current run of render() is completed.

This method differs slightly depending on whether you would like to place a StateView or a UIView. If you are placing a StateView, this method is place(type: StateView.Type, key: String, constraints: ((make: ConstraintMaker -> Void)) -> ShadowViewElement). If you are placing a UIView, initialize the UIView first and then use place(view: UIView, key: String, constraints: ((make: ConstraintMaker) -> Void)) -> ShadowViewElement).

0.2 (Release Candidate 1)

Getting Started

Initializing a StateView

StateView is simply a subclass of UIView. You can add a StateView to any UIView as a subview or set a StateView to be the view of any UIViewController.

You can create a new view controller with a root view set to your StateView subclass by creating a subclass of StateViewController. StateViewController is a simple subclass of UIViewController that adds the StateView subclass you provide to its view property.

To create a new view controller with your StateView subclass as the root view, create a subclass of StateViewController and provide your StateView subclass in the viewDidLoad method:

class HomeViewController: StateViewController {
	
	override func viewDidLoad() {
		super.viewDidLoad()
		rootView = HomeView(parentViewController: self)
	}
}

To add your StateView subclass to an existing UIView, simply add your subclass with addSubview() and call the renderDeep() instance method on the new instance of your subclass:

let homeView = HomeView(parentViewController: self)
view.addSubview(homeView)
homeView.renderDeep()

The call to renderDeep() will run the first pass of render() and set the view up to update itself when your data changes. All children of this StateViewwill update themselves without any further setup. Calling renderDeep is only required on the first StateView subclass in a view hierarchy if that instance was added manually.

Adding subviews

You can use place() to add subviews to your StateView. You can add any UIView or StateView as a subview.

On initial render and anytime your data changes, render() is called. Any view placed with place() in a run of render() will be added as a subview. If the previous run of render() also placed that view, the existing view will be preserved in the view hierarchy and passed new props to update itself with. If a view was placed in the previous run of render() and then not placed in the following run of render(), the following run of render() will cause that view to be removed from the view hierarchy.

Summary: Simply place() a view to add it as a subview. Simply don't place() that view in a following run of render() to automatically remove that view. Decide which views to place and which not to place with conditional operators, functions, and anything else you can imagine in your render() method.

You can call place() with three parameters.

If you are placing another StateView, place() takes the type of the StateView, the unique key you'd like to give that view as a String, and AutoLayout constraints in the form of a SnapKit ConstraintMaker.

let shippingSummary = place(ShippingSummary.self, "shipping summary") { make in
  make.size.equalTo(self)
  make.center.equalTo(self)
}

If you are placing a UIView subclass that isn't a StateView, place() takes the initialized view as a UIView, the unique key you'd like to give that view as a String, and AutoLayout constraints in the form of a SnapKit ConstraintMaker.

let button = UIButton(type: .System)
button.setTitle("Select image", forState: .Normal)
place(button, key: "button") { make in
	make.size.equalTo(100)
	make.center.equalTo(self)
}

Your StateView subclass can initialize another StateView subclass automatically, so you can simply pass the type of that StateView to place(). Your StateView subclass can place an already initialized UIView subclass that isn't a StateView so you can configure that UIView subclass before placing it. This is helpful for doing something like setting the text of a UILabel. You can set the properties of your UILabel inside the run of render() and place() that UILabel once it's ready to go.

Using State and Props

You can decide which views to place with place() in render() and which values to pass to placed views by looking at state and props.

You can keep values that describe the state of a view in state:

self.state["selectedSong"] = Song(title: "This Must Be My Dream", artist: "The 1975")

self.state["displayingThankYou"] = true

self.state["selectedShipping"] = nil

You are encouraged to keep values in state. The more descriptive you can be with keys and values in state that work for you, the better. You are also encouraged to store objects of any types that work for you in state, as state is a dictionary with type [String: AnyObject].

You can use state as a dependable place you can look to see how your view looks onscreen without actually having to see it there. Anything in state that is used to configure your StateView or any of its children is always up-to-date in your view hierarchy. Simply update your state and any view that depends on state will update itself.

You can pass a value from state or elsewhere to one of your subviews by passing the value as a prop:

let nextButton = place(NextButton.self, "next button") { make in
  make.center.equalTo(self)
  make.height.equalTo(44)
  make.width.equalTo(120)
}
nextButton.prop(forKey: "enabled", is: true)
nextButton.prop(forKey: "title", is: "Continue")



let shippingSummary = place(ShippingSummary.self, "shipping summary") { make in
  make.size.equalTo(self)
  make.center.equalTo(self)
}
let selectedShipping = self.state["selectedShipping"]
shippingSummary.prop(forKey: "shipping", is: selectedShipping)

You can then access any of the values passed in props in that subview by accessing self.props. You are encouraged to pass objects of any types that work for you in props, as props is a dictionary with type [String: AnyObject].

You can use prop(forKey: String, isLinkedToKeyInState: String) -> Void as a shortcut to pass a value in state as a prop .

let shippingSummary = place(ShippingSummary.self, "shipping summary") { make in
  make.width.equalTo(self)
  make.height.equalTo(200)
  make.centerX.equalTo(self)
  make.bottom.equalTo(self)
}
shippingSummary.prop(forKey: "shipping", isLinkedToKeyInState: "selectedShipping")

This way you can easily connect one view's use of a value in state with its subview's use of that same value.

You can communicate in the opposite direction, from subview to parent view, by passing the subview a function in props. You are encouraged to pass objects of any types that work for you, as a subview can receive a function prop with type ([String: AnyObject]->Void). You can pass a function that can be called to say that something was done, or one that can be given a selection from a group of items, or one that can do anything else you can imagine.

To pass values from a subview back to its parent view, pass a function as a prop:

let canvas = place(CanvasView.self, key: "canvas") { make in
	make.size.equalTo(self)
	make.center.equalTo(self)
}
canvas.prop(forKey: "image", isLinkedToKeyInState: "selectedImage")
canvas.prop(forKey: "didPickImage") { values in
	if let image = values["image"] as? UIImage {
		self.state["selectedImage"] = image
	}
}

You can then access and call that function from your subview by accessing self.props.

You can easily accept a value in your StateView coming back from a function prop, set that value to be the value of a relevant key in state, and watch your views update themselves at the arrival of the new value.

Summary: Simply update your state, or a computed value outside of state that you are passing as a prop in your render() method, and your view and its subviews will update themselves. render() is a pure function of state and props. This means that given the same state and props, render() will always produce the same result.

Documentation

This document lists and describe the objects and methods you can use with StateView.

StateView

A UIView subclass that uses a ShadowView and a collection of methods, including its own render() and resolveProps() to update its subviews when its state property is set to a new value.

StateView class
A subclass of UIView.

shadow property
An instance of ShadowView that is initialized automatically. shadow keeps an always up-to-date record of views that have been added to this instance of StateView and orchestrates the addition, removal, and updating of subviews in an instance of StateView.

props property
A dictionary with type [String: AnyObject] that contains any values passed in from the parent view of this instance of StateView.

state property
A dictionary with type [String: AnyObject] that contains values set by this instance of StateView. Values saved in state should describe the current state of this view as completely as possible and be used in the render() method of this instance of StateView to influence the content and inclusion of subviews.

parentViewController property
The UIViewController that is displaying the hierarchy of views containing this instance of StateView. Use this property to make calls to methods like presentViewController().

getInitialState method
Returns a dictionary with type [String: AnyObject] that can be used in place of the default empty dictionary used as initial state. Use this method to set values in state that are inherently true of the state of view until something changes, or values that describe the initial state of a view well and will be changed throughout the view's lifetime.

renderDeep method
An internal method that calls render() and then shadow once render() has completed. Call this method once on an instance of StateView if you are adding a StateView to a UIView.

render method
A method that is called anytime there is a change to state. Override this method to place subviews in this instance of StateView. You are encouraged to include conditional statements and calls to your own synchronous functions to decide which views to place as subviews each time render() is called.

resolveProps method
An internal method that resolves any StateViewPropWithStateLink that came from a call to prop(forKey: String, isLinkedToKeyInState: String) into a StateViewProp with a value by getting the value in state for the linked key.

place method
A method that includes a subview to be added (or kept, if placed in the previous run of render()) after the current run of render() is completed.

This method differs slightly depending on whether you would like to place a StateView or a UIView. If you are placing a StateView, this method is place(type: StateView.Type, key: String, constraints: ((make: ConstraintMaker -> Void)) -> ShadowViewElement). If you are placing a UIView, initialize the UIView first and then use place(view: UIView, key: String, constraints: ((make: ConstraintMaker) -> Void)) -> ShadowViewElement).