-
Notifications
You must be signed in to change notification settings - Fork 14
Archived documentation
This page contains the documentation provided with past releases of StateView.
Contents
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 StateView
will 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.
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.
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.
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)
.
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 StateView
will 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.
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.
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.
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)
.
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 StateView
will 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.
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.
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.
This document lists and describe the objects and methods you can use with 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)
.
Take a look at the known issues and help improve StateView.