Skinning Custom Controls

The MAF provides simple skinnable controls, such as Button, Label, Table View; and more complex controls, such as Tree View or Calendar Control. If you build your own complex control from the simple skinnable MAF controls, you can fully skin your control using the MAF skinning engine. To do so, your component logic must apply the runtime information from the MAF skinning engine.

This section demonstrates how to skin a custom control with one simple example. These tasks build a control that uses the iOS provided Quartz 2D library to draw a rectangle, and skin its fill color and border brush color.

The Rectangle Control

To implement the simple control, create an Objective-C class and extend the UIView, then implement the drawRect: method of the UIView superclass:
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextMoveToPoint(context, 100, 100);
    CGContextAddLineToPoint(context, 150, 150);
    CGContextAddLineToPoint(context, 100, 200);
    CGContextAddLineToPoint(context, 50, 150);
    CGContextAddLineToPoint(context, 100, 100);
    
    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
    CGContextFillPath(context);
}

This simple Quartz 2D code presents the following rectangle on your screen:
Quartz 2D Rectangle

Implementing MAFStyling Protocol

The control must implement the MAFStyling protocol, which implements basic skinning capabilities for all MAF skinnable controls. This is the Objective-C header file of a custom skinnable control:
#import <UIKit/UIKit.h>
#import "MAFStyling.h"

@interface MyCustomControl : UIView <MAFStyling>

@end
This is the public API provided by this protocol:
/**
 MAFStyling declares the methods and properties what a styleable control should support.
 */
@protocol MAFStyling

/** 
 The style name of the control defined in the styling xml.
 Default is nil which means to use the default style for the control.
 Setting this property automatically calls mafApplyStyle.
 */
@property (copy) NSString *mafStyleName;

/**
 Applies the style on the control. Should be called if a new styling xml loaded in runtime.
 */
- (void) mafApplyStyle;

By implementing this protocol, you enable your own control to store the mafStyleName property. Use mafStyleName to set your custom skin’s key value when initializing an instance of your control. The other capability this protocol provides is the mafApplyStyle selector. MAF skinnable controls call this selector when a new instance is created from the control, thus applying the skin to itself.

To make the fill color skinnable, create a UIColor property in the private interface of the class. Do not allow developers reusing your component to change this property directly, because it is intended to be set at runtime with the help of the MAF skinning engine:
#import "MyCustomControl.h"
#import "MAFExtensions.h"
#import <ObjC/runtime.h>
#import "MAFUIStyleCatalog.h"

@interface MyCustomControl()

@property (strong) UIColor *rectFillColor;

@end
This example also imports library classes, which allow you to set the style name and read the styles parsed from the Style.xml in your project. To use the value of the rectFillColor property as the fill color for the rectangle, change the onDraw method:
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetStrokeColorWithColor(context, self.rectBorderColor.CGColor);
    
    CGContextMoveToPoint(context, 100, 100);
    CGContextAddLineToPoint(context, 150, 150);
    CGContextAddLineToPoint(context, 100, 200);
    CGContextAddLineToPoint(context, 50, 150);
    CGContextAddLineToPoint(context, 100, 100);
    
    CGContextSetFillColorWithColor(context, self.rectFillColor.CGColor);
    CGContextFillPath(context);
}

To set the color value, define it in the styling XML:
    <Style Key="CustomControlBlueStyle" TargetType="Control" platform="ios">
        <Setter Property="Background" Value="#000000"/>
    </Style>

The next step is to read out the style from the MAFStyleCatalog. You can do this by implementing the mafApplyStyle method of the MAFStylingProtocol.

The following code reads an MAFUIStyle instance from the singleton MAFUIStyleCatalog. The catalog already contains the style, because it was parsed when the style XML file was loaded into the MAFStyleParser. The MAFUIStyle parser has a set of properties, including the Background property, that you can use programatically. Use the control's mafStyleName to read the corresponding MAFUIStyle from the MAFUIStyleCatalog.
- (void) mafApplyStyle{
    MAFUIStyleCatalog*   catalog  = [MAFUIStyleCatalog uiStyleCatalog];
    MAFUIStyle *customStyle = [catalog styleByName: self.mafStyleName];
    self.rectFillColor = customStyle.getBorderColor;
}
To set and get the value of the mafStyleName, implement the following getter and setter:
#pragma mark - MAFStyling implementation

- (NSString *)mafStyleName {
    //return [super mafStyleName];
	return objc_getAssociatedObject(self, mafStyleNameKey);
}

- (void)setMafStyleName:(NSString *)mafStyleName{
    //[super setMAFStyleName:mafStyleName];
    objc_setAssociatedObject(self, mafStyleNameKey, mafStyleName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self mafApplyStyle];
    [self setNeedsDisplay];
}

The above code creates a public getter and setter method. The getter method returns the current value, and the setter method calls the mafApplyStyle method to change the internal value of the rectFillColor property, then notifies the system to redraw the view.

Presenting Skinned Controls

Present your custom control in the same way you present a MAF skinnable control. Create an instance of the control and set its style name via the mafStyleName property:
MyCustomControl *myControl = [[MyCustomControl alloc] initWithFrame:CGRectMake(0, 0, 320, 300)];
    myControl.mafStyleName = @"CustomControlBlueStyle";
    [self.view addSubview:myControl];
    [myControl release];