How to Customize Cells and Custom Cell Editors in FlexGrid for iOS
FlexGrid gives developers a large amount of control over how their data is presented. There are many options here, and it can sometimes be a little overwhelming knowing where to start for those new to the controls and platform. In this article, I'll cover using formatItem() to customize cells and using prepareCellForEdit() to provide custom editors for the Xuni FlexGrid for iOS control.
Using formatItem() for Custom Cells
The formatItem() method is a powerful mechanism for controlling cell content in FlexGrid on iOS. It allows both simple customizations (changing font size, color, or background color) or more advanced customizations (custom views inside cells). This allows you to embed highly customized controls such as images or even other Xuni controls into a grid. To use the formatItem() method you must first implement the FlexGrid delegate. The delegate will send a notification whenever a new element representing a cell has been created at which point the formatItem() method will fire. Implementing the delegate should be relatively straightforward:
- First, add the delegate to the interface in your ViewController's header file:
@interface ViewController : UIViewController<FlexGridDelegate>
- Now you need to set your grid to use the delegate which you just set up:
grid.delegate = self;
With the delegate implemented, we can now start to use the formatItem() method to customize our cells. The FlexFormatItemEventArgs gives us access to several properties which we can use to determine a cell’s location in the grid (via the row and column properties) as well as get and set data in an associated cell (via the panel object). In the code below, I’ll use the formatItem() method to do some custom formatting of cells in a particular column based on the value of the cell:
- (void)formatItem:(FlexFormatItemEventArgs*)args{
FlexGrid \*g = (FlexGrid\*)[self.view viewWithTag:1];
FlexColumn *col = [g.columns objectAtIndex:args.col];
if ([col.header isEqualToString:@"performance"]) {
if (args.row != 0) {
NSNumber \*n = (NSNumber\*)[args.panel getCellDataForRow:args.row inColumn:args.col formatted:false];
if (n.integerValue > 200) {
//get the rect for the cell we're changing
CGRect r = [args.panel getCellRectForRow:args.row inColumn:args.col];
//change textattributes for text in the cell
[args.panel.textAttributes setValue:[UIFont fontWithName:@"Courier-Bold" size:14] forKey:NSFontAttributeName];
[args.panel.textAttributes setValue:[UIColor greenColor] forKey:NSForegroundColorAttributeName];
//set background color for rect
CGContextSetFillColorWithColor(args.context, [UIColor darkGrayColor].CGColor);
//color in background
CGContextFillRect(args.context, r);
}
else if (n.integerValue < 150){
//just set text attributes
[args.panel.textAttributes setValue:[UIFont fontWithName:@"ArialMT" size:18] forKey:NSFontAttributeName];
[args.panel.textAttributes setValue:[UIColor redColor] forKey:NSForegroundColorAttributeName];
}
}
}
else{
//use system font for other columns
[args.panel.textAttributes setValue:[UIFont systemFontOfSize:12] forKey:NSFontAttributeName];
}
}
If we run the application at this point, we’ll see some custom formatting based on the performance value of a cell: We can further customize cells by placing custom views inside. A common case would be to embed a UIImage inside our grid. Using the following code we can check for a country column and add an image of the countries flag to the grid based on the countryID:
if ([col.header isEqualToString:@"country"]) {
NSObject *v = [args.panel getCellDataForRow:args.row inColumn:args.col formatted:false];
if (v != nil) {
if (![v.description isEqual: @"country"]){
NSString *flag = [NSString stringWithFormat:@"%@.png", v];
UIImage *image = [UIImage imageNamed:flag];
CGRect r = [args.panel getCellRectForRow:args.row inColumn:args.col];
r.origin.x += (r.size.width - 16) / 2;
r.origin.y += (r.size.height - 16) / 2;
r.size.width = r.size.height = 16;
[image drawInRect:r];
args.cancel = true;
}
}
}
Combining these changes with our previous modifications to the formatItem() method will yield these results: Our documentation also provides a sample tutorial that demonstrates how you can add a Xuni Gauge control to your grid. There is a version of this implemented in our FlexGrid 101 sample (refer to the CustomCellsController) which you can find on our GitHub page.
Using prepareCellForEdit() for Custom Editors
Xuni also includes a method providing custom editors. For some data, it may feel more natural to edit the data using a specific control rather than generic keyboard entry. Specific examples for this could be editing a date (which feels best with UIDatePicker), or providing a slider for manipulating a value with a consistent minimum and maximum. Our FlexGrid 101 sample already covers adding a UIDatePicker in the EditingController, so I'll focus on my second example of adding a slider for editing numerical value—in this case, our performance column. To this end we'll use the prepareCellForEdit() method, which also requires that we implement the FlexGrid delegate. From there, we capture the active inline Editor, create our own slider, and feed input from the slider to the FlexGrid. Additionally, the slider will exist on a toolbar that automatically reveals itself on edit and hide after the edit has ended. First we need to set up prepareCellForEdit:
- (void)prepareCellForEdit:(FlexCellRangeEventArgs*)args {
FlexGrid \*flex = (FlexGrid\*)[self.view viewWithTag:1];
FlexColumn *col = [flex.columns objectAtIndex:args.col];
if ([col.header isEqualToString:@"performance"]) {
//retrieve flexgrid editor
UITextField \*editor = (UITextField\*)flex.activeEditor;
UISlider *slider = [[UISlider alloc] init];
//retive cell value as NSNumber
NSNumber \*n = (NSNumber\*)[flex.cells getCellDataForRow:args.row inColumn:args.col formatted:false];
slider.minimumValue = 0;
slider.maximumValue = 300;
slider.value = n.integerValue;
[slider addTarget:self action:@selector(onSliderChanged:) forControlEvents:UIControlEventValueChanged];
editor.inputView = slider;
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, flex.frame.size.width, 44)];
toolbar.barStyle = UIBarStyleDefault;
//set up selector to handle completion
UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(endEditSlider:)];
UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
[toolbar setItems:@[space, done]];
editor.inputAccessoryView = toolbar;
}
}
We also need to create two methods (as the selectors in the code above indicate): one to handle when the slider value changes, and another to handle when editing reaches completion:
- (void)onSliderChanged:(UISlider*)sender {
//update value of textfield as slider changes
FlexGrid \*flex = (FlexGrid\*)[self.view viewWithTag:1];
UITextField \*editor = (UITextField\*)flex.activeEditor;
FlexColumn *c = [flex.columns objectAtIndex:flex.editRange.col];
editor.text = (NSString*)[c getFormattedValue:[NSNumber numberWithInt:sender.value]];
}
- (BOOL)endEditSlider:(UITextField *)textField {
//handle ending edit
FlexGrid \*flex = (FlexGrid\*)[self.view viewWithTag:1];
UITextField \*editor = (UITextField\*)flex.activeEditor;
UISlider \*slider = (UISlider\*)editor.inputView;
NSNumber *n = [NSNumber numberWithInt:slider.value];
[flex.cells setCellData:n forRow:flex.editRange.row inColumn:flex.editRange.col];
[flex finishEditing:true];
return true;
}
Now if we run the application, we can use the slider to edit cells in the performance column: Once again, we have another implementation of this available on GitHub in our EditingController sample where we implement a DatePicker as a custom editor.
FlexGrid allows cell customization across platforms
FlexGrid is a powerful tool with many customization options. On iOS, using the above two options for customizing your grid unleashes a broad range of new possibilities in terms of how your data is presented and how users interact with it. We've also recently covered cell customization using Xamarin.Forms, which is worth checking out if you're interested in Xamarin or cross platform development. Regardless of platform, Xuni provides many options for enhancing your apps.