Changes to Xuni Control Delegates and C# Event Signatures
The new 2016v1 release has made a few changes to the native iOS control delegates to provide some improved behaviors when dealing with multiple instantiations of the same control type, and to give the controls better support for the C# style events available in Xamarin.iOS. The changes are relatively minor, but worth detailing since they require some changes to existing code.
Changes to the iOS Xuni Control Delegates
The signatures for most Delegate methods have changed slightly on iOS. At the most basic level, a sender parameter has been added to the method calls. An example of one of these minor changes would be: 2015v3
- (BOOL)tapped:(XuniPoint*)point;
2016v1
- (BOOL)tapped:(FlexGrid *)sender point:(XuniPoint *)point;
This relatively minor change only requires that the method signature in existing code be updated to match the new signature. In the instance of a ViewController that contains multiple copies of a control (in this case multiple FlexGrids), the new signature makes it easier to distinguish between multiple controls with the sender parameter. This makes it easier to provide different behavior for each control. In some instances the changes have been more significant. Signatures that formerly only had one EventArgs parameter have been modified to return the constituent parts of the EventArgs object instead. Another example for this more complicated case: 2015v3
- (void)formatItem:(FlexFormatItemEventArgs*)args;
2016v1
- (bool)formatItem:(FlexGrid *)sender panel:(FlexGridPanel *)panel forRange:(FlexCellRange *)range inContext:(CGContextRef)context;
At first this may seem like a large departure, but the code actually behaves much the same as before (excluding the old FlexFormattedEventArgs object). While there is no change in functionality, this does require a more significant update to existing code. For example, let's look at an example that uses the formatItem method to change the background color of all cells in a particular column. 2015v3
- (void)formatItem:(FlexFormatItemEventArgs*)args{
FlexGrid \*g = (FlexGrid\*)[self.view viewWithTag:1];
FlexColumn *col = [g.columns objectAtIndex:args.col];
if ([col.binding isEqualToString:@"orderTotal"]) {
NSObject *v = [args.panel getCellDataForRow:args.row inColumn:args.col formatted:false];
if (v != nil) {
if (![v.description isEqual: NSLocalizedString(@"Total Orders", nil)]){
CGRect r = [args.panel getCellRectForRow:args.row inColumn:args.col];
CGContextSetFillColorWithColor(args.context, [UIColor darkGrayColor].CGColor);
CGContextFillRect(args.context, r);
args.cancel = NO;
}
}
}
}
2016v1
-(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;
}
The code is still pretty similar, but, instead of using args.panel or args.row, you now have to access the same information through different objects (panel or range.row). Also the method is no longer void and instead returns a boolean value which will determine whether the default cell content is displayed (thus providing similar functionality to the args.cancel property). This example is likely the most radical change you'll notice due to the changed method signatures.
C# Style Events in Xamarin.iOS
The changes in the native iOS control delegates also bring some significant benefits for Xamarin.iOS code. These changes should allow the use of C# style events in Xamarin.iOS code which should look significantly cleaner and more familiar than the old approach. 2015v3 As of 2015v3, to use the FlexGrid delegate for the formatItem method you would need to set a WeakDelegate:
grid.WeakDelegate = this;
And later implement the formatItem method using a pattern very similar to the native iOS code:
[Export("formatItem:")]
public void FormatItem(FlexFormatItemEventArgs args)
{
FlexGrid g = (FlexGrid)View.ViewWithTag(1);
nuint i = (nuint)args.Col;
Column col = g.Columns.GetItem<Column>(i);
if (col.Binding == "Money" && args.Panel.CellType == FlexCellType.FlexCellTypeCell)
{
CoreGraphics.CGRect r = args.Panel.GetCellRect(args.Row, args.Col);
args.Context.SetFillColor(UIColor.DarkGray.CGColor);
args.Context.FillRect(r);
args.Cancel = false;
}
}
2016v1 With the new changes, you can use a much more standard C# syntax to add the FormatItem event:
grid.FormatItem += grid_FormatItem;
And write your own method for handling this event:
private bool grid_FormatItem(FlexGrid sender, GridPanel panel, CellRange range, CGContext context)
{
bool result = false;
nuint i = (nuint)range.Col;
Column col = sender.Columns.GetItem<Column>(i);
if (col.Binding == "Money" && panel.CellType == FlexCellType.FlexCellTypeCell) {
CoreGraphics.CGRect r = panel.GetCellRect(range.Row, range.Col);
context.SetFillColor(UIColor.DarkGray.CGColor);
context.FillRect(r);
result = false;
}
return result;
}
This should be a lot easier for C# developers who don't have a great deal of experience with native iOS code. The syntax looks much more natural and is easier to use than it was in the past.
Future Changes
We'll continue to examine what future changes we can make to Xuni to improve the functionality where it makes sense. We've exposed a great deal more of the API through Xamarin.iOS for the 2016v1 release so there should be quite a few quality of life improvements for anyone looking to give it a try.