The iOS framework that grows only as fast as its documentation
Nimbus CSS

Classes

class  NIChameleonObserver
 An observer for the Chameleon server. More...
 
class  NICSSParser
 An Objective-C wrapper for the flex CSS parser. More...
 
class  NICSSRuleset
 A simple translator from raw CSS rulesets to Objective-C values. More...
 
class  NIDOM
 A leight-weight DOM-like object to which you attach views and stylesheets. More...
 
protocol  <NIStyleable>
 The protocol used by the NIStylesheet to apply NICSSRuleSets to views. More...
 
class  NIStylesheet
 Loads and caches information regarding a specific stylesheet. More...
 
class  NIStylesheetCache
 A simple in-memory cache for stylesheets. More...
 

Variables

NSString *const NIStylesheetDidChangeNotification
 
NSString *const NIStringsDidChangeNotification
 

Overview

Nimbus CSS allows you to use cascading stylesheets to theme your native iOS application. Stylesheets provide a number of advantages over Interface Builder and native code.

How to Create a Stylesheet

Start by creating a .css file. Add it to your project, ensuring that you include it in the copy resources phase. Place all of your CSS files in a subdirectory and add the folder to your project by creating a folder reference. Creating a folder reference will ensure that your subdirectories are maintained when the CSS files are copied to the device.

You can then load the stylesheet:

// In this example all of the app's css files are in a "css" folder. The "css" folder would be
// dragged into the Xcode project with the "Create folder reference" option selected.
NSString* pathPrefix = NIPathForBundleResource(nil, @"css");
NIStylesheet* stylesheet = [[[NIStylesheet alloc] init] autorelease];
if ([stylesheet loadFromPath:"common.css"
pathPrefix:pathPrefix]) {
// Successfully loaded <bundlePath>/css/common.css
}

Recommended Procedure for Storing Stylesheets

Use a global NIStylesheetCache object to store your stylesheets in memory. Parsing a stylesheet is fast but care should be taken to avoid loading a stylesheet more often than necessary. By no means should you be allocating new stylesheets in tight loops. Using a global NIStylesheetCache will make it easy to ensure that your stylesheets are cached in memory and easily accessible.

Another advantage to using a global NIStylesheetCache is that it allows you to easily use Chameleon. Chameleon will post notifications on the stylesheet objects registered in the NIStylesheetCache. Because the observer and you will use the same cache, you can register for notifications on the same stylesheet objects.

The above example would look like this if you used a stylesheet cache:

// In your app initialization code, create a global stylesheet cache:
NSString* pathPrefix = NIPathForBundleResource(nil, @"resources/css");
_stylesheetCache = [[NIStylesheetCache alloc] initWithPathPrefix:pathPrefix];
// Elsewhere in your app, when you need access to any stylesheet:
NIStylesheetCache* stylesheetCache =
[(AppDelegate *)[UIApplication sharedApplication].delegate stylesheetCache];
NIStylesheet* stylesheet = [stylesheetCache stylesheetWithPath:@"common.css"];

Reduce the dependencies on your application delegate by defining a global method somewhere:

NIStylesheetCache* StylesheetCache(void);
#import "AppDelegate.h"
NIStylesheetCache* StylesheetCache(void) {
return [(AppDelegate *)[UIApplication sharedApplication].delegate stylesheetCache];
}

Using a Stylesheet

The easiest way to apply a stylesheet to a set of views is by using a NIDOM object. Once you attach a stylesheet to an NIDOM object, the stylesheet will be applied to any views you attach to the NIDOM object.

Linking to Other Stylesheets

You can link to one stylesheet from another using @import url('url') in the .css file.

For example, let's say you have a common CSS file, common.css, and a CSS file for a specific view controller, profile.css. You can import common.css in profile.css by adding the following line:

@import url('common.css')

Files are imported relative to the pathPrefix given to NIStylesheet.

CSS Import Ordering Gotcha

One might expect that placing an @import in the middle of a CSS file would import the file at that exact location. This is not currently the case, i.e. the parser does not insert the imported CSS where the @import is. Instead, all of the CSS within the imported stylesheet will be processed before the importer's CSS.

For example, even if @import url('common.css') is placed at the bottom of the profile.css file, common.css will be processed first, followed by profile.css.

This is a known limitation and will ideally be fixed in a later release.

Relative ordering of @imports is respected.

Supported CSS Properties

UIView {
border: <dimension> <ignored> <color> {view.layer.borderWidth view.layer.borderColor}
border-color: <color> {view.layer.borderColor}
border-width: <dimension> {view.layer.borderWidth}
background-color: <color|image_name> {view.backgroundColor}
border-radius: <dimension> {view.layer.cornerRadius}
opacity: xx.xx {view.alpha}
-ios-autoresizing: [left|top|right|bottom|width|height|all|margins|dimensions] {view.autoresizingMask}
visibility: [hidden|visible] {view.hidden}
width: [x%,xpx,auto] {view.frameWidth}
height: [x%,xpx,auto] {view.frameHeight}
padding: <vertical unit> <horizontal unit> {used in auto height and width calculations}
-mobile-hpadding: <horizontal unit> {used in auto width}
-mobile-vpadding: <vertical unit> {used in auto height}
max-width: [x%,xpx] {view.frameWidth}
max-height: [x%,xps] {view.frameHeight}
min-width: [x%,xpx] {view.frameWidth}
min-height: [x%,xps] {view.frameHeight}
top: [x%,xpx] {view.frameMinY}
left: [x%,xpx] {view.frameMinX}
bottom: [x%,xpx] {view.frameMaxY}
right: [x%,xpx] {view.frameMaxX}
-mobile-halign: [left|right|center] {view.frameX}
-mobile-valign: [top|bottom|middle] {view.frameY}
-mobile-relative: [#id|.prev|.next|.first|.last] {controls the position of the view relative to another view}
margin-top: [x%,xpx,auto] {distance from view.frameMinY to relative.frameMaxY - % is relative to size of relative element, px is absolute, auto aligns the vertical centers}
margin-bottom: [x%,xpx,auto] {distance from view.frameMaxY to relative.frameMinY - % is relative to size of relative element, px is absolute, auto aligns the vertical centers}
margin-left: [x%,xpx,auto] {distance from view.frameMinX to relative.frameMaxX - % is relative to size of relative element, px is absolute, auto aligns the horizontal centers}
margin-right: [x%,xpx,auto] {distance from view.frameMaxX to relative.frameMinX - % is relative to size of relative element, px is absolute, auto aligns the horizontal centers}
}
UILabel {
color: <color> {label.textColor}
font: <font-size> <font-name> {label.font}
font-size: <font-size> {label.font}
font-family: <font-name> {label.font}
Can not be used in conjunction with font/font-family properties. Use the italic/bold font
name instead.
font-style: [italic|normal] {label.font}
font-weight: [bold|normal] {label.font}
text-align: [left|right|center] {label.textAlignment}
text-shadow: <color> <x-offset> <y-offset> {label.shadowColor label.shadowOffset}
-ios-highlighted-color: <color> {label.highlightedTextColor}
-ios-line-break-mode: [wrap|character-wrap|clip|head-truncate|tail-truncate|middle-truncate] [label.lineBreakMode]
-ios-number-of-lines: xx {label.numberOfLines}
-ios-minimum-font-size: <font-size> {label.minimumFontSize}
-ios-adjusts-font-size: [true|false] {label.adjustsFontSizeToFitWidth}
-ios-baseline-adjustment: [align-baselines|align-centers|none] {label.baselineAdjustment}
-mobile-text-key: "Key Name" {attaches a localized string (or the key name if not found) to this label}
}
UIButton {
-mobile-title-insets
-mobile-content-insets
-mobile-image-insets
font: <font-size> <font-name> {button.font}
Buttons also support pseudo selectors: :selected,:highlighted,:disabled with the following rules:
color: <color> {[button titleColorForState:]}
text-shadow: <color> {[button titleShadowColorForState:]}
-mobile-image: url(image_name)
-mobile-text-key: "Key Name" {attaches a localized string (or the key name if not found) to this button}
background-image: url(image_name)
-mobile-background-stretch: top left bottom right
-ios-button-adjust
}
UINavigationBar {
-ios-tint-color: <color> {navBar.tintColor}
}
UISearchBar {
-ios-tint-color: <color> {searchBar.tintColor}
}
UIToolbar {
-ios-tint-color: <color> {toolbar.tintColor}
}

Chameleon

Chameleon is a web server that serves changes to CSS files in real time.

You start Chameleon from the command line using node.js and tell it to watch a specific directory of CSS files for changes. This should ideally be the directory that contains all of your project's CSS files.

Note: ensure that when you add the css directory to your project that it is added as a folder reference. This will ensure that the complete folder hierarchy is maintained when the files are copied to the device.

To learn more about how to start up a Chameleon server, view the README file within nimbus/src/css/chameleon/. This README will walk you through the necessary steps to build and install node.js.

Once you've started the Chameleon server, you simply create a Chameleon observer in your application, give it access to your global stylesheet cache, and then tell it to start watching Chameleon for skin changes. This logic is summed up below:

_chameleonObserver = [[NIChameleonObserver alloc] initWithStylesheetCache:_stylesheetCache
host:host];
[_chameleonObserver watchSkinChanges];

You then simply register for NIStylesheetDidChangeNotification notifications on the stylesheets that you are interested in. You will get a notification when the stylesheet has been modified, at which point if you're using NIDOM you can tell the NIDOM object to refresh itself; this will reapply the stylesheet to all of its attached views.

Variable Documentation

NIStylesheetDidChangeNotification

The notification key for when a stylesheet has changed.

NSString*const NIStylesheetDidChangeNotification;
Discussion

This notification will be sent with the stylesheet as the object. Listeners should add themselves using the stylesheet object that they are interested in.

The NSNotification userInfo object will be nil.

NIStringsDidChangeNotification

The notification key for when a strings file has changed.

NSString*const NIStringsDidChangeNotification;
Discussion

This notification will be sent globally at the moment.

The NSNotification userInfo object will contain the local disk path of the strings file.