iDroid-Layout is a port of Androids layout system and its drawable and resources framework to iOS.
The main reason for this project is to learn more about the Android layout system and how it works. Another reason is the lack of a advanced layout system in iOS ( Update: this is not true anymore for iOS >= 6 because of the introduction of layout constraints). Currently it is a pain to build maintainable UI code in iOS. You have the choice between doing your layout in interface builder which is great for static, but not powerful enough for dynamic content, or doing all in code which is difficult to maintain. In Android layouts can be defined in XML. Views automatically adjust their size while taking into account their content requirements and their parents' size restrictions.
iDroidLayout can be installed using CocoaPods:
Defining and using a layout with iDroid-Layout can be done in two simple steps:
<LinearLayout layout_width="match_parent" layout_height="wrap_content" orientation="vertical" padding="10"> <LinearLayout layout_width="match_parent" layout_height="wrap_content" orientation="horizontal"> <TextView id="text" layout_width="wrap_content" layout_height="wrap_content" padding="10" text="Some text" textColor="#ffffff" background="#80000000"/> <TextView id="otherText" layout_width="match_parent" layout_height="match_parent" padding="10" textColor="#ffffff" background="#ff0000" gravity="right" text="Some other text"/> </LinearLayout> <UIButton id="button" background="#ff0000" layout_width="100" layout_height="30" layout_gravity="center_horizontal" layout_marginTop="10" text="Click me"/> </LinearLayout>
IDLLayoutViewController *vc = [[IDLLayoutViewController alloc] initWithLayoutName:@"myLayout" bundle:nil]; [self.navigationController pushViewController:vc animated:TRUE];
iDroid-Layout contains an advanced resource resolution framework. It allows you to reference resources like images, layouts, strings, colors and styles. Resources identifiers allow to (cross-)reference resources. E.g. it allows you to specify texts and images for views within layouts.
The syntax of a resource identifier is the following:
<bundle-identifier>identifier of the bundle which contains the resource. If the bunde-identifier is ommitted, the resource will be searched within the main bundle. To use bundles other than the main bundle, you have to load a bundle at least once before resource identifiers whith this bundle can be used.
<resource-type>resource type (one of
<resource-name>name of the resource file
<resource-subname>identifier of the specific resource within the resource file. This is only used for some resource types which act as a resource container.
A drawable is an abstract concept for something that can be drawn on the screen. This could be a simple raster graphics, a color, a shape or a combination of these. The following types of drawables are currently implemented:
A bitmap graphics (e.g. png, jpg) which can also be a 9-patch graphics
A Drawable that manages an array of other Drawables. These are drawn in array order, so the element with the largest index is be drawn on top
A Drawable that is references other drawables depending on the drawables state (e.g. to draw different images for different button states).
Drawable that insets another drawable
Drawable that clips another drawable depending on the drawables level
Draws a geometric shape which can be filles with a colors or a gradient
Drawables can be defined in XML and can be inflated into a drawable object tree. Here is an example:
<layer-list> <item left="12" top="12" right="8" bottom="8"> <shape shape="rectangle"> <solid color="#8000"/> <corners topLeftRadius="40" bottomRightRadius="10"/> </shape> </item> <item left="10" top="10" right="10" bottom="10"> <shape shape="rectangle" thicknessRatio="8" innerRadiusRatio="3"> <gradient centerX="-0.1" centerY="-0.1" gradientRadius="300%" type="radial" startColor="#ccc" centerColor="#0fff" endColor="#0f0"/> <corners topLeftRadius="40" bottomRightRadius="10"/> <stroke width="1" color="#8000"/> <padding left="5"/> </shape> </item> <item> <selector> <item state_pressed="true"> <inset insetLeft="10" insetTop="10" insetRight="10" insetBottom="10"> <shape shape="rectangle"> <gradient startColor="#f00" centerColor="#0fff" endColor="#0ff"/> <corners topLeftRadius="40" bottomRightRadius="10"/> </shape> </inset> </item> </selector </item> </layer-list>
Assuming you saved the drawable XML into the file
background.xml. Now you can define a button like this in your layout:
<Button textColor="#eee" layout_width="match_parent" layout_height="100" layout_gravity="center" background="@drawable/background" text="Button"/>
Fig1: Button with a custom drawable as background in normal state
Fig2: Button with a custom drawable as background in pressed state
IDLLayoutBridge is a UIView which acts as a bridge between the plain old view layout mechanism and the iDroid-Layout mechanism. First you have to create an IDLLayoutBridge object and add it to your view hierarchy. Now you can load the xml layout into the
IDLLayoutBridge view using
IDLLayoutBridge *bridge = [[IDLLayoutBridge alloc] initWithFrame:CGRectMake(100, 100, 120, 220)]; IDLLayoutInflater *inflater = [[IDLLayoutInflater alloc] init]; [inflater inflateURL:[[NSBundle mainBundle] URLForResource:@"myLayout" withExtension:@"xml"] intoRootView:bridge attachToRoot:TRUE]; [self.view addSubview:bridge];
Yes, you can use native views. Simply use the class name of the view as the xml tag name (e.g.
<UIButton/>). However, for some of the native views the
onMeasureWithWidthMeasureSpec:heightMeasureSpec: selector is not yet implemented, so you should not use
wrap_content for the view's width and height.
Yes, simply use the class name of the view as the xml tag name (e.g.
<MyCustomView/>). However, you should implement the
onMeasureWithWidthMeasureSpec:heightMeasureSpec: selector. Otherwise you should not use
wrap_content for the views' width and height.
IDLLayoutInflaterusually does). How can I implement a custom initialization?
IDLLayoutInflater creates view objects using a default implementation of the
IDLViewFactory protocol. You can implement a custom view factory by implementing the protocol and setting the
viewFactory property of the
Yes, you can either use
IDLTableViewCell or create a custom UITableViewCell where you inflate your layout into an
Yes, a layout xml file can be inflated into a view in interface builder. Simple add a plain view in interface builder, set
IDLLayoutBridge as custom class of the newly added view and define a user defined runtime attribute with the name
layout and the name of the xml file (without the file extension) as the value. Check out the example project for more details.
Similar to the android layouting system, you can embed other layouts within a layout XML file using the
<include /> and
<merge /> tags. Inside the layout to which you want to add the re-usable component, add the tag. Here's an example:
<LinearLayout layout_width="match_parent" layout_height="match_parent" orientation="vertical"> <include layout="@layout/layoutToInclude"/> <TextView layout_width="match_parent" layout_height="wrap_content" text="Some text"/> </LinearLayout>
You can also override all the layout parameters (any
layout_* attributes), the id and the visibility of the included layout's root view by specifying them in the
<include/> tag. For example:
<include id="title" layout_width="match_parent" layout_height="match_parent" layout="@layout/layoutToInclude" visibility="gone"/>
XML files always need a single root element. If you have to include multiple views from another single layout file, you need a container as a root element. This is where the
<merge /> tag comes into play. It allows you to include multiple views at once, without the need of an extra layout container:
<merge> <TextView layout_width="match_parent" layout_height="wrap_content" text="First text view"/> <TextView layout_width="match_parent" layout_height="wrap_content" text="Second text view"/> </merge>
Now, when you include this layout in another layout (using the
<include/> tag), the system ignores the
<merge /> element and places the two text views directly in the layout, in place of the