How to add Custom UIViews to an iOS FlexGrid using NativeControlGridCellFactory
Xuni 2016 v2.5 brings a lot of small changes and enhancements. One of the less documented (but still important) features in this release is a new mechanism for easily adding custom UIViews to an iOS FlexGrid. FlexGrid has always supported custom cell content, but it now has a much easier mechanism for embedding other fully functional controls into cells directly inline. The new NativeControlGridCellFactory makes adding this type of custom cell content easy.
NativeControlGridCellFactory
The NativeControlGridCellFactory allows you to embed your custom UIViews into your FlexGrid. To use it, all you need to do is change your default cell factory to the NativeControlGridCellFactory and implement the createViewForCell event. For instance, if we were to modify the CustomCells sample that's part of FlexGrid101 to use this mechanism rather than formatItem, it would look like this:
NativeControlGridCellFactory *factory = [[NativeControlGridCellFactory alloc] initWithGrid:self.flex];
[factory.createViewForCell addHandler:^(XuniEventContainer<GridCreateViewForCellEventArgs *> *eventContainer) {
GridColumn *col = self.flex.columns[eventContainer.eventArgs.col];
if([col.binding isEqual: @"orderTotal"] && eventContainer.eventArgs.panel.cellType == GridCellTypeCell){
NSObject *v = [eventContainer.eventArgs.panel getCellDataForRow:eventContainer.eventArgs.row inColumn:eventContainer.eventArgs.col formatted:false];
XuniRadialGauge *radialGauge = [[XuniRadialGauge alloc] init];
XuniGaugeRange *lower = [[XuniGaugeRange alloc] initWithGauge:radialGauge];
lower.min = 0;
lower.max = 40;
lower.color = [UIColor colorWithRed:0.133 green:0.694 blue:0.298 alpha:1];
XuniGaugeRange *middle = [[XuniGaugeRange alloc] initWithGauge:radialGauge];
middle.min = 40;
middle.max = 80;
middle.color = [UIColor colorWithRed:1 green:0.502 blue:0.502 alpha:1];
XuniGaugeRange *upper = [[XuniGaugeRange alloc] initWithGauge:radialGauge];
upper.min = 80;
upper.max = 100;
upper.color = [UIColor colorWithRed:0 green:0.635 blue:0.91 alpha:1];
[radialGauge.ranges addObject:lower];
[radialGauge.ranges addObject:middle];
[radialGauge.ranges addObject:upper];
radialGauge.backgroundColor = [UIColor clearColor];
radialGauge.showText = XuniShowTextNone;
radialGauge.thickness = 0.6;
radialGauge.min = 0;
radialGauge.max = 100;
radialGauge.loadAnimation = nil;
radialGauge.value = [v.description doubleValue]*(100.0/90000.0);
radialGauge.showRanges = false;
radialGauge.isReadOnly = false;
eventContainer.eventArgs.view = radialGauge;
}
} forObject:self];
self.flex.cellFactory = factory;
The code is very similar, but the effect is much different since this embeds a fully functional radial gauge rather than a static image of one. This allows animations, user interactions, and any other control behaviors you might wish to add.
Another use case
A common case for large lists of data is to contain email addresses or hyperlinks. We can support this scenario using the NativeControlGridCellFactory. The following sample code demonstrates how you can embed a UIButton into your cells that opens up the Mail client with a pre-populated email address when the user clicks the email address contained in the grid. Use the NativeControlGridCellFactory to add the UIButtons into the FlexGrid:
NativeControlGridCellFactory *factory = [[NativeControlGridCellFactory alloc] initWithGrid:self.flex];
[factory.createViewForCell addHandler:^(XuniEventContainer<GridCreateViewForCellEventArgs *> *eventContainer) {
GridColumn *col = self.flex.columns[eventContainer.eventArgs.col];
if([col.binding isEqual: @"email"] && eventContainer.eventArgs.panel.cellType == GridCellTypeCell){
NSObject *v = [eventContainer.eventArgs.panel getCellDataForRow:eventContainer.eventArgs.row inColumn:eventContainer.eventArgs.col formatted:false];
UIButton *emailButton = [UIButton buttonWithType:UIButtonTypeSystem];
[emailButton setTitle:v.description forState:UIControlStateNormal];
emailButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
emailButton.titleEdgeInsets = UIEdgeInsetsMake(0, 5, 0, 5);
[emailButton addTarget:self action:@selector(emailButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
eventContainer.eventArgs.view = emailButton;
}
} forObject:self];
self.flex.cellFactory = factory;
The emailButtonClicked method will take the email address string from the emailButton and use it to create a URL link for the pre-populated email:
-(void)emailButtonClicked: (id) sender{
UIButton \*emailButton = (UIButton \*)sender;
NSString *url = [[[@"mailto:" stringByAppendingString:emailButton.titleLabel.text] stringByAppendingString:@"?subject=title&body=content"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: url]];
}
That's all you need to do to implement this behavior. You can use the same approach for hyperlinks to websites.
Closing Thoughts
The NativeControlGridCellFactory makes it much easier to enhance your FlexGrid. Embedding different controls into your grids can make your grid more interactive and functional, and you can also use it to make your grids more aesthetically appealing by embedding more advanced data visualization controls.