Skip to content
Michael edited this page Apr 25, 2016 · 34 revisions

View Controllers and Binding Behaviors

Using Binding Behaviors

Beacon offers two entry points to activate binding support.

The default mechanism is to add the Beacon binding behavior to your view controller. You do that in viewDidLoad

- (void)viewDidLoad {
  [super viewDidLoad];
  [AKABindingBehavior addToViewController:self];
}

if your view controller also serves as view model (and maybe delegate) or

- (void)viewDidLoad {
  [super viewDidLoad];
  [AKABindingBehavior addToViewController:self
                          withDataContext:self.myViewModel
                                 delegate:myBindingBehaviorDelegate];
}

to explicitly specify the view model and binding delegate.

This is all you have to do to enable binding support for your view controller and from then on, you can just assign binding expressions to views from the properties inspector of Xcode's interface builder.

Using Static Table Views as Layout Device

It's very convenient to use sub classes of UITableViewController in conjunction with forms. Beacon provides an implementation of table view controllers that provides binding support and also adds some really convenient features such as dynamic cell place holders.

To use this, just inherit from AKAFormTableViewController instead of UITableViewController. Documentation for this class is pending ;-)

Supported Bindings and Binding Types

Beacon provides bindings specific for the binding target, for example for the text of a label or text field. The most important categories of such specialized bindings are view bindings. Binding extensions for existing views are integrated as text properties defined in categories (named AKAIBBindingProperties). You use them typically by setting these properties in the properties inspector of Xcode's interface builder.

Beacon also provides some binding types which are specific to the bound value, such as formatter-, font- or predicate bindings. These bindings are typically property bindings. Property bindings are primarily used by view bindings to customize the binding configuration or the binding target.

View Bindings

Property Bindings

Binding expressions explained

Binding expressions consist of a primary expression which defines the binding source (where the data is coming from) and attributes which provide additional information which is either required by the binding or which customizes the behavior of the binding. Both are optional, if neither is specified (empty binding expression) no binding will be created.

<bindingExpression> ::= <primaryExpression>? ( '{' <attributeList>? '}' )?

<attributeList> ::= <attribute> | <attributeList> ',' <attribute>

<attribute> ::= <identifier> | <identifier> ':' <bindingExpression>

As you can see, attribute values are also binding expressions, so they can have their own attributes.

For some primary types (enumerations, options and structures) the constant values are specified as parameters in the attribute part of the expression. For these types, attribute values can be specified in addition to parameters, for example: $CGPoint { x:0, y:0, someAttribute:"value" }, where x and y are parameters for the CGPoint and "someAttribute" is an attribute.

Primary expression types

There are two categories of primary expressions, constant expressions and key path expressions. Key path expressions need a binding context to provide their value (arrays may be constant or not, depending on their content).

Key path expressions use a slightly extended syntax, you can prefix them with "$data." or "$root.". $data is the current data context, "$root" is the top-level (form-) data context. See the section about "Data contexts". These are called scopes. If no scope is specified, $data is assumed (unless otherwise specified).

Beacon currently supports the following types

Beacon type Examples Objective-C type
KeyPath name, model.value, $data.name, $root.top id (any type)
Boolean $true, $false NSNumber (BOOL)
Integer 123, -4 NSNumber (long long)
Double .0, 1., .3e-5 NSNumber (double)
String "a\nb\tc" NSString
Class <NSNumberFormatter> Class
Enum $enum.Value, $enum.Type.Value id (any type)
Options $options{Value1,V2,V3} $options.Type{Value} NSNumber (long long)
Array [ v1{ A1:4 }, v2, $true, "Hey!" ] NSArray
CGPoint $CGPoint { x:1, y:2 } NSValue (CGPoint)
CGSize $CGSize { w:10, h:20 }, $CGSize{width:1,h2} NSValue (CGSize)
CGRect $CGRect { x:1, y:2, w:3, h:4 } NSValue (CGRect)
CGColor $CGColor { r:255, g:255, b:255, a:255 } CGColor
UIColor $UIColor { r:255, g:255, b:255, a:255 } UIColor
UIFont $UIFont { name:"Courier new", size:15.0 } UIFont

Binding Contexts, Data Contexts

Binding contexts are used to evaluate a key path to a concrete binding source value.

Specifying the root data context

The Beacon binding behavior determines the root binding context used for top level view bindings. If you add the binding behavior like this:

- (void)viewDidLoad {
  [super viewDidLoad];
  [AKABindingBehavior addToViewController:self];
}

then the view controller serves as root data context (or in MVVM terms as view model) and if it implements the protocol AKABindingBehaviorDelegate also as binding delegate.

You can also specify a separate view model like this:

- (void)viewDidLoad {
  [super viewDidLoad];
  [AKABindingBehavior addToViewController:self
                          withDataContext:self.viewModel
                                 delegate:self.bindingDelegate];
}

The binding delegate allows you to monitor control many aspect of the binding process and AKAControl instances involved in bindings. Please take a look at the corresponding delegate methods to learn about the details.

Nested Data Contexts

Some view types, such as UITableView provide a nested data context for bindings declared in cells. If a table view section is bound to an array, then cells in that section will use the corresponding array item as data context. Bindings can however still access the root data context using the "$root" prefix.