10 May 2014

Constraints in Points

In iOS Views' coordinates system has its origin in the upper left corner and the units are "points", not pixels. Thus views' positions, sizes and frames are always expressed in points. Working with points allows us to develop code which works fine independently of the screen's pixel density.
So for example an iPad Retina has twice the number of pixels of a non-retina iPad but it has exactly the same number of points.

A "constraint" defines the relationships between two or more views, constraints are used by the system to layout the view hierarchy. "This view must always have a width of 120 points", "these three views must always have the same height", "horizontal space between these two views must always be 12 pixel", these are all examples of constraints.

In order to define constraints for a view we need to find the nearest neighbour, that is the closest sibling view in a direction. Sometimes a view doesn't have a sibling view in a particular direction, in that case the nearest neighbour is its superview. Constrains can be defined visually in the XIB files or programmatically in code. In both cases we use the nearest neighbour to define the spatial relationships of a view.

After defining constrains we can incur in an ambiguous layout, that is probably caused by a missing constraint, leading to more than one way of satisfying the defined constraints. We must add one or more constraints to solve that situation.

It can also happen to move or resize a view's frame in the XIB after constraints have been defined, in that case there will be a warning that at runtime that frame will not appear as shown in interface builder. We can solve this problem by updating the constraints or by updating the view.

If we put too many constraints on a view, we might face a "conflicting constraints" situation. In that case if we run the app we should see an "unable to simultaneously satisfy constraints" warning message in the Xcode console. The system is ignoring one constraint (maybe the right one) to solve the conflict, we have to manually pick and remove the wrong one.

To describe constraints in code we use a special syntax called VFL (Visual Format Language). In a single string we can describe multiple constraints, but we cannot define horizontal and vertical constraints both in the same string. Here's an example: @"V: | [myView]  - 5 - |", this translates to "there will be 0 points between myView top edge and its container view, and 5 points between myView bottom edge and its container view".

Before Auto Layout came in, apps used another technique to layout views, that is the "autoresizing mask", that can be translated into constraints. This system it's still there for backward compatibility, when using Auto Layout we might incur in a conflicting constraints situation because of that, so we might need to disable the "old school" layout system by assigning "NO" to the view's property translatesAutoresizingMaskIntoConstraints.

Off-topic: I finally find peace of mind about syntax colouring in Xcode, my current theme is here.