iOS layouts for web developers #3 - managing the appearance
This is the third part of the iOS layouts for web developers series. The first part was about the controls, the second about its positioning. Now I’m going to tackle how to approach managing the controls appearance - something that we have CSS for in the web.
CSS was first standardised back in 1996, it was around early 2000s when it became ubiquitous. We’ve then left behind the times when the
<center> tag was used to align content and view specific definitions like fonts or colors were scattered all over through the HTML structure. For almost two decades, we clearly feel it wasn’t the good way of doing things and we now strive to maintain the separation of structure vs. presentation. CSS is now an inseparable part of the web development, there’s virtually no way to do anything without it. It’s like a skin for the human body, we don’t rather meet anyone without one and even if we meet, we’d probably be frightened and disgusted by the raw and bloody flesh visible.
Now, it’s 2015 and we’re looking at the iOS layouts. How do we maintain the content vs. presentation separation in iOS? The short answer is - we don’t. There is no direct replacement for CSS concept in iOS. I was seriously shocked when I first learned that fact. But the fact is, a lot of iOS community doesn’t care. Most iOS developers go “the Apple way” here and use Interface Builder to design the layout. They define the whole presentation layer by clicking and dragging, not even feeling the need of any code for that purpose.
content vs. presentation separation and styles reuse using CSS <—> N/A
One might say that less code means less bugs, but of course, Interface Builder does nothing different than generating the code underneath. It’s in XIB format, nothing easily “consumable” by anything other than its native generator. Trying to touch it from the outside will probably hurt. Working with the code generator like Interface Builder can often encourage bad practices - it creates tight coupling between view and it’s logic behind, it creates merge hell when touched by more than one person, it destroys the ability to reuse style definitions and the relation between what we did in the IDE and what we got in the final app is too implicit.
Also, maybe it’s only my personal delusion, but I think we in the web world feel a lot of disgust for WYSIWYGs in general and often even for using the mouse at all. CSS is not an imperative programming language (fortunately), but it’s still the code. We treat it as such, we edit it as a raw text files, we understand it and we feel comfortable with that fact. Whenever something automated messes with my CSS or HTML, I’m losing the confidence in what’s going on and how it works.
How to handle style definitions then?
When we decide not to go “the Apple way” and abandon Interface Builder striving for something that allows style definitions reuse and separation, our simplest alternative is to create everything directly in the code, instantiating the controls “manually”, setting its properties like colors, setting its frame or Auto Layout constraints and finally adding it to the superview, like this:
For complex screens, it quickly grows to hundreds of lines of imperative and repetitive code that is inherently very declarative in its real nature. It hurts me every time I assign properties like
backgroundColor etc. It feels just like I’d be putting
<font face=“Arial” color=“red”> in my HTML markup and it’s already more than 10 years ago when I did so the last time. It’s like that human flesh visible outside.
One simple strategy we can employ is to enclose our repeatable patterns of style and layout definitions, very remotely equivalent to our CSS classes, as a static method somewhere or as a category on
Now we can use it whenever we want our new control to look like the other:
Apple proposal - appearance proxies
The issue of style definitions reuse was also approached by Apple within UIKit, albeit quite differently than in CSS. There’s a concept called appearance. It allows us to define selected style properties of the controls once per the application, ensuring any customization we do is applied consistently across the app. We can define that all the buttons (
UIButton instances) that we use in our app should have gray background:
We can restrict our customization scope to controls contained within another specific container control, for example to all
Also, we can optionally make our appearance definitions dependent on the screen dimensions and orientations, using
UITraitCollections we’ve discussed in the previous part of the series:
Appearance might be compared to CSS limited to elements only (no per-class or per-id definitions), with basic hierarchies and basic media queries support. However, the distance to the full-blown CSS equivalent is quite large. There is no way to apply appearance definitions to the selected subset of elements of a given type, like we do using CSS classes. There is no way to represent other types of relationships between controls than ancestor-descendant (
whenContainedIn) - we need to forget about the selectors we know from CSS like siblings, direct children, n-th of type and so on. Moreover, when we have multiple ancestor-descendant relationships defined, the first one encountered (most generic) is used, while in CSS the most specific one is used, allowing us to specify our styles from the general definitions to the specialized ones going down the elements tree.
CSS definitions on HTML elements <—> UIAppearance definitions
CSS definitions on HTML element hierarchies <—> UIAppearance definitions with
CSS definitions with screen size-related media queries <—> UIAppearance definitions with
CSS features other than above <—> N/A
Last but not least, customization via appearance is by default limited only to several properties of the controls exposed by iOS with the
UI_APPEARANCE_SELECTOR tag. For example, generic
UIView only allows us to set
backgroundColor through appearance and such a common control like
UILabel doesn’t add anything more on its own. This means there is no out-of-the-box solution for setting text color, font properties, margins etc. for all the labels once across the application. That limitation can be overcome by defining an
UI_APPEARANCE_SELECTOR for custom properties and for custom controls, but that is far from the elegance of plain old declarative CSS.
Prospects for truly declarative solution
There are multiple projects out there in the open source space that aims to bring the real declarativeness, style reuse possibility and overall CSS feel to iOS views. Both UISS and NUI offer the syntax loosely inspired by CSS. The former is just a wrapper for UIAppearance with all its limitations. The latter goes much further, actually taking care of virtually all the styling exposed by UIKit controls. There are also Pixate Freestyle and nativeCSS that offer direct usage of CSS within iOS apps. In Bright we’re also doing some in-house experiments in Cero, albeit it aims more to provide the declarative view hierarchy and the styling comes a bit as a side effect.
I believe that all of these ideas and frameworks are shaping the future of iOS layouts and are worth keeping an eye on it with the hope that someday the native offering of iOS platform in terms of managing the appearance would not be so limited as today.
Next time - more on how to achieve particular visual effects known from CSS within iOS views.