Using Xuni Block Based Events for iOS Event Handling
Last week, we touched on some changes to the Xuni iOS control delegates. We've also added a different mechanism altogether for triggering event based behavior without the use of delegates. In this article we'll cover how to use this new style of delegate-less iOS event handlers via block based events.
Old Style With Delegates
Let's quickly establish what using the delegates looks like by taking a look at a FlexGrid using formatItem. You need to declare that your ViewController adopts the FlexGridDelegate protocol:
@interface ViewController : UIViewController<FlexGridDelegate>
Now, in your implementation, you need to have the FlexGrid object use the ViewController as its delegate:
grid.delegate = self;
Finally, you need your own implementation of formatItem. So you'll add a method similar to the below:
-(bool)formatItem:(FlexGrid *)sender panel:(FlexGridPanel *)panel forRange:(FlexCellRange *)range inContext:(CGContextRef)context{
bool result = NO;
FlexColumn *col = [sender.columns objectAtIndex:range.col];
if ([col.binding isEqualToString:@"orderTotal"]) {
NSObject *v = [panel getCellDataForRow:range.row inColumn:range.col formatted:false];
if (v != nil) {
if (![v.description isEqual: NSLocalizedString(@"Total Orders", nil)]){
CGRect r = [panel getCellRectForRow:range.row inColumn:range.col];
CGContextSetFillColorWithColor(context, [UIColor darkGrayColor].CGColor);
CGContextFillRect(context, r);
result = NO;
}
}
}
return result;
}
This is a very traditional implementation of event-based behavior on iOS. This style works fine, but it requires placing code into many different parts of your ViewController.
New Block Based Events
With 2016v1 we've added a second, cleaner mechanism for configuring event based behavior. Rather than use delegates, this method uses function blocks as iOS event handlers. They can be easier to read and use compared to delegates. There's only two elements to consider in this mechanism. The first is the function block itself which is similar to the formatItem method from the above delegate example. You need to cast the XuniEventArgs to the specific EventArgs for your event (FlexFormatEventArgs for the formatItem event). The FlexFormatArgs object has the properties we need (row, col, panel, context, etc.) that we need when using this method. Otherwise it's relatively straightforward:
IXuniEventHandler eventWithoutDelegate = ^(NSObject \*sender, XuniEventArgs \*args)
{
bool result = NO;
FlexGrid \*g = (FlexGrid\*)sender;
FlexFormatItemEventArgs \*formattedArgs = (FlexFormatItemEventArgs \*)args;
FlexColumn *col = [g.columns objectAtIndex:formattedArgs.col];
if ([col.binding isEqualToString:@"orderTotal"]) {
NSObject *v = [formattedArgs.panel getCellDataForRow:formattedArgs.row inColumn:formattedArgs.col formatted:false];
if (v != nil) {
if (![v.description isEqual: NSLocalizedString(@"Total Orders", nil)]){
CGRect r = [formattedArgs.panel getCellRectForRow:formattedArgs.row inColumn:formattedArgs.col];
CGContextSetFillColorWithColor(formattedArgs.context, [UIColor darkGrayColor].CGColor);
CGContextFillRect(formattedArgs.context, r);
result = NO;
}
}
}
formattedArgs.cancel = result;
};
If you're not familiar with function blocks in Objective-C Apple has the further documentation to walk you through how they work. The ^(arguments) syntax will be new if you haven't used blocks before. Our block takes two arguments (NSObject *sender, XuniEventArgs *args) and doesn't return any values. After completing a block, we need to specify that the FlexGrid object use this handler for a specific event. That's also pretty straightforward:
[grid.flexGridFormatItem addHandler:eventWithoutDelegate forObject:grid];
Using this style can make your code much more readable. You can place your code all together in the viewDidLoad method rather than in a number of different locations as required by the delegates. This style is also more explicit which can be helpful if you're working with multiple FlexGrid controls. Since you're explicitly adding the handlers on a per-object basis, it makes it easier to tell which handler is working with a given control. That removes some of the vagueness of the delegates.
Event Handling Wrap-Up
Both styles of event handling with or without delegate are available so it's up to you which one you'd prefer to use. While the traditional delegate style may be the most familiar to long term iOS developers, the new block style is more direct and often easier to read.