The last time Hackerfall tried to access this page, it returned a not found error. A cached version of the page is below, or clickhereto continue anyway

Introducing MarkupKit | OK|Cancel

MarkupKit is an open-source framework for simplifying development of native iOS applications. It allows developers to construct user interfaces declaratively using a human-readable markup language, rather than programmatically in code or interactively using a visual modeling tool such as Interface Builder.

Building an interface in markup makes it easy to visualize the resulting output as well as recognize differences between revisions. It is also a metaphor that many developers are comfortable with, thanks to the ubiquity of HTML and the World Wide Web.

This article introduces the MarkupKit framework and provides an overview of some of its key features.

Document Structure

MarkupKit uses XML to define the structure of a user interface. In a MarkupKit document, XML elements represent instances of UIView subclasses, and XML attributes generally represent properties associated with those views. For example, the following markup declares an instance of UILabel and sets the value of its text property to “Hello, World!”:

<UILabel text="Hello, World!"/>

The output produced by this markup is identical to the output of the following Swift code:

let label = UILabel()
label.text = "Hello, World!"

MarkupKit uses key-value coding (KVC) to set property values. The name of the attribute represents the path of the property being set (such as the “text” attribute shown in the previous example). However, because the attribute name represents a key path, attributes can also be used to set the values of nested properties. For example, the following markup creates an instance of UITableViewCell whose text labels text property is set to “This is a table row”:

<UITableViewCell textLabel.text="This is a table row"/>

Type conversions for string, number, and boolean properties are handled automatically by KVC. Other types, such as enumerations, colors, fonts, and images, are handled specifically by MarkupKit. For example, the following markup sets the font of the text in the previous example to 24-point Helvetica Bold and the text color to red. MarkupKit translates the font name and hex-encoded color value to UIFont and UIColor instances, respectively, before setting the property values:

<UITableViewCell textLabel.text="This is a table row"
    textLabel.font="Helvetica-Bold 24" 
    textLabel.textColor="#ff0000"/>

Localization

If an attribute’s value begins with “@”, MarkupKit attempts to look up a localized version of the value before setting the property. For example, if an application has defined a localized greeting in Localizable.strings as follows:

"hello" = "Hello, World!";

the following markup will produce an instance of UILabel with the value of its text property set to “Hello, World!”:

<UILabel text="@hello"/>

In addition to the global values defined in Localizable.strings, the strings processing instruction (PI) can be used to define a set of local string values that are only visible to the current document. This can help simplify management of localized resources, since they can be split into a number of smaller context-specific pieces rather than one large application-wide collection of string values.

Reserved Attributes

Some attributes have special meaning in MarkupKit and do not represent view properties. These include style, class, and id. The “style” attribute is used to invoke a “factory method” for constructing a view instance. The “class” attribute specifies a set of template properties to apply to an instantiated view, similar to styles in CSS. The “id” attribute is used to define an “outlet” for a view, similar to outlets in Xcode.

Additionally, attributes whose name begins with “on” are reserved. With the exception of existing properties whose names begin with “on”, these attributes represent event handlers, or “actions”, similar to actions in Xcode.

Each of the reserved attribute types is discussed in more detail below.

Factory Methods

The “style” attribute is used to create view instances of a specific type, or “style”. Most UIKit types can be instantiated by invoking the new method on the type; however, some types require the use of a special construction method. For example, UIButton instances are created using the buttonWithType: method of the UIButton class, and UITableView instances are initialized via initWithFrame:style, not the no-arg init method that is invoked by new.

To handle these cases, MarkupKit supports a special attribute named “style”. The value of this attribute represents the name of a “factory method”, a zero-argument method that produces instances of a given type. MarkupKit adds factory methods to classes such as UIButton and UITableView to enable these types to be constructed in markup.

For example, the following markup creates an instance of a “system-style” UIButton:

<UIButton style="systemButton" normalTitle="Press Me!"/>

Note the name of the attribute that is used to set the buttons title. The standard UIButton class does not define a property named normalTitle, but instead defines a setTitle:forState: method. However, such methods are not KVC-compliant and cannot be invoked directly via markup. MarkupKit adds a number of properties to UIButton and other similar classes that delegate to these methods and allow their respective types to be configured in markup.

Template Properties

Often, when constructing a user interface, the same set of property values are applied repeatedly to instances of a given type. For example, an application designer may want all buttons to have a similar appearance. While it is possible to simply duplicate the property definitions for each button instance, this is repetitive and does not allow the design to be easily modified later each button instance must be located and modified individually, which can be time-consuming and error-prone.

MarkupKit allows developers to abstract common sets of property definitions into “templates”, which can then be applied by name to class instances. This makes it much easier to assign common property values as well as modify them later.

Property templates are defined in property list (or .plist) files. Each template is represented by a dictionary defined at the top level of the property list. The dictionarys contents represent the property values that will be set when the template is applied.

Templates are added to a MarkupKit document using the properties processing instruction (PI). For example, the following PI imports all templates defined by MyStyles.plist into the current document:

<?properties MyStyles?>

Templates are applied to class instances using the reserved “class” attribute. The value of this attribute refers to the name of a template defined by the property list. All property values defined by the template are applied to the class instance. Nested properties such as “titleLabel.font” are supported by property templates.

For example, if MyStyles.plist defines a dictionary named “label.greeting” that contains the following values (abbreviated for clarity):

"label.greeting": {
    "font": "Helvetica 24"
    "textAlignment": "center"
}

the following markup would produce a label reading “Hello, World!” in 24-point Helvetica with horizontally-centered text:

<UILabel class="label.greeting" text="Hello, World!"/>

Multiple properties PIs may be specified in a single document. Their contents are merged into a single collection of templates available to the document. If the same template is defined by multiple property lists, the contents of the templates are merged into a single template, and the most recently-defined values take precedence. This allows markup documents to “override” more globally-defined styles with local values.

Outlets

Views defined in markup are not particularly useful on their own. The reserved “id” attribute can be used to give a name to a view instance. Assigning a view an ID defines an “outlet” for the view and makes it accessible to calling code. Using KVC, MarkupKit “injects” the named view instance into the documents owner (generally either the view controller for the root view or the root view itself), allowing application code to interact with it.

For example, the following markup declares an instance of LMTableView containing a UITextField. LMTableView and LMTableViewCell are MarkupKit-provided subclasses of UITableView and UITableViewCell, respectively, that simplify the definition of static table view content:

<LMTableView>
    <LMTableViewCell>
        <UITextField id="textField" placeholder="Type something"/>
    </LMTableViewCell>
</LMTableView>

The text field is assigned an ID of “textField”. As with Interface Builder, the owning class might declare an outlet for the text field in Objective-C like this:

@property (weak, nonatomic) UITextField *textField;

or in Swift, like this:

weak var textField: UITextField!

In either case, when the document is loaded, the outlet will be populated with the text field instance, and the application can interact with it just as if it was created programmatically.

Actions

Most non-trivial applications need to respond in some way to user interaction. UIKit controls (subclasses of the UIControl class) fire events that notify an application when such interaction has occurred. For example, the UIButton class fires the UIControlEventTouchUpInside event when a button instance is tapped.

While it would be possible for an application to register for events programmatically using outlets, MarkupKit provides a more convenient alternative. An attribute whose name begins with “on” (but is not equal to the name of an existing property whose name is equal to or starts with “on”) is considered a control event. The value of the attribute represents the name of the action that will be triggered when the event is fired. The name of the attribute is simply the “on” prefix followed by the name of the event, minus the “UIControlEvent” prefix.

For example, the following markup creates a system-style UIButton that calls the handleButtonTouchUpInside: method of the documents owner when the button is tapped:

<UIButton style="systemButton" normalTitle="Press Me!" 
    onTouchUpInside="handleButtonTouchUpInside:"/>

Example

The following markup provides a simple example of many of the previously discussed features in action:

<?xml version="1.0" encoding="UTF-8"?>

<?properties Styles?>

<LMColumnView layoutMargins="16" backgroundColor="#ffffff">
    <LMRowView layoutMargins="12" backgroundColor="#eeeeee">
        <UITextField id="nameField" placeholder="@name"/>
    </LMRowView>

    <UIButton style="systemButton" normalTitle="@sayHello" onTouchUpInside="showGreeting"/>

    <UILabel id="greetingLabel" class="label.greeting"/>

    <LMSpacer/>
</LMColumnView>

The first line following the XML declaration imports a property list named Styles.plist, which is defined as follows (again, abbreviated for clarity):

"label.greeting": {
    "font": "System-Bold 24"
    "textColor": #00aa00
    "textAlignment": "center"
}

These properties are used to set the attributes of a label used to present a greeting to the user (the UILabel named “greetingLabel” in the markup document). The label will use a bold, 24-point system font and will display centered text using a light green color.

The document's root element is an instance of LMColumnView, a subclass of UIView that automatically arranges its subviews in a vertical line. It is one of several classes provided by MarkupKit that help simplify development of applications that automatically adapt to device orientation or content changes. In addition to LMColumnView, MarkupKit also provides LMRowView, which arranges subviews in a vertical line, and LMLayerView, which arranges subviews in layers, like a stack of transparencies.

The document next declares a row view containing a UITextField that allows a user to enter a name. The text field is assigned an ID of “nameField”, which creates an outlet for the field in the view's controller. The outlets for the both the name text field and the greeting label are declared in the controller as follows:

weak var nameField: UITextField!
weak var greetingLabel: UILabel!

The text field's “placeholder” attribute is specified as “@name”. This is a reference to the “name” key in the application's Localizable.strings file:

"name" = "Name";
"sayHello" = "Say Hello";
"unknownName" = "I don't know your name!";
"greetingFormat" = "Hello, %1$@!";

When the document is loaded, this value will be replaced by the actual string value for the key (i.e. “Name”).

Next, the document declares an instance of a system-style UIButton with a normal-state title that resolves to “Say Hello”. Tapping this button invokes the action handler for the button's UIControlEventTouchUpInside event, which sets the value of the greeting label using the text the user has entered in the name field:

func showGreeting() {
    let name = nameField.text
    let mainBundle = NSBundle.mainBundle()

    let greeting: String;
    if (name.isEmpty) {
        greeting = mainBundle.localizedStringForKey("unknownName", value: nil, table: nil)
    } else {
        greeting = String(format: mainBundle.localizedStringForKey("greetingFormat", value: nil, table: nil), name)
    }

    greetingLabel.text = greeting
}

Finally, the document declares an instance of LMSpacer. Spacers are empty views that grow to fill any remaining space in the parent view. Placing a spacer at the end of a column view ensures that the column's contents will be aligned to the top of the column.

Running the application produces output similar to the following:

Summary

This article introduced the MarkupKit framework and provided an overview of some of its key features. For more information, see the following articles or visit the MarkupKit project on GitHub:

Implementing Radio Button-Like Behavior in iOS Applications

Creating Custom Table View Cells in Markup

Using UIStackView with MarkupKit

Like this:

Like Loading...

Continue reading on gkbrown.wordpress.com