Applying Rule-Based Conditional Formatting (Example 4)

Operating system requirements: iOS 5.0 or later.

Mobile device: iPad.

Description

This example describes conditional formatting setup for table cells based on the rule "Value between A and B". The conditional formatting setting pool is used to apply rule settings.

Source Code

Executing the example requires to place the following code instead of the executeExample method of the ViewController class (see the Displaying of Express Report section):

-(void)executeExample {
    MAExpressAnalysisReportViewController *contr = (MAExpressAnalysisReportViewController *)m_controller;
    // Get array of data view controllers
    NSArray *controllers =[contr dataViewControllers];
    // Determine values of A and B
    double valueA = 7000;
    double valueB = 8000;
    
    // Parse data view controllers in cycle
    for(NSObject *controller in controllers)
    {
        // Check if the current controller is a table data view controller
        if([controller isMemberOfClass:[MAGridDataViewController class]])
        {
            // Get table data view controller object
            MAGridDataViewController *gridController = (MAGridDataViewController *)controller;
            // Get controller view
            UIView *view = [gridController view];
            // Get array of subviews
            NSArray *subviews = [view subviews];
            // Parse subviews in cycle
            for(NSObject *subview in subviews)
            {
                // Check if the current view is a table view
                if([subview isMemberOfClass:[NuGridView class]])
                {
                    // Get table view object
                    NuGridView *gridView = (NuGridView *)subview;
                    // Get table delegate
                    MAGridDelegate *delegate = (MAGridDelegate*)[gridView gridDelegate];
                    // Get proxy data source
                    MAGridProxyDataSource *proxyDatasource = (MAGridProxyDataSource*)[delegate proxyDataSource];
                    
                    // Determine rule-based conditional formatting settings
                    MAConditionalFormattingRules *rules = [[MAConditionalFormattingRules new] autorelease];
                    // Apply rules to the whole table
                    [rules setArea: kCFAEntireTable];
                    
                    // Get conditional formatting rule
                    MAConditionalFormattingRule *formattingRule = [rules ruleByType: kCFRTBetween];
                    // Mark created rule as active
                    [formattingRule setActive: YES];
                    // Set fill color for cells that satisfy the rule
                    [formattingRule setColor: [UIColor colorWithRed:1 green:0.77 blue:0.27 alpha:1]];
                    // Use absolute numeric values
                    [formattingRule setValueFormat: kCFRVFNumber];
                    // Enable data sorting
                    [formattingRule setApplyOrder: YES];
                    
                    // Create a conditional formatting setting pool
                    MAConditionalFormattingPool *formattingPool = [[MAConditionalFormattingPool new] autorelease];
                    // Set conditional formatting setting pool
                    [formattingRule setConditionalFormattingPool: formattingPool];
                    
                    // Initialize object with predefined data
                    MAConditionalFormattingRulePreparedData *preparedData = [[MAConditionalFormattingRulePreparedData new] autorelease];
                    NSMutableArray *dataArray = nil; // Data array
                    double total = 0.0; // Total sum of data values
                    
                    // Check if it is required to prepare settings for conditional formatting rule
                    bool isNeedPrepareData = [formattingRule needPrepareDataForCellInRow:nil andColumn:nil withArea:[rules area]];
                    if (isNeedPrepareData) {
                        // Prepare data
                        [preparedData setPreparedValueA: [NSNumber numberWithDouble: valueA]];
                        [preparedData setPreparedValueB: [NSNumber numberWithDouble: valueB]];
                        // Write prepared data into conditional formatting setting pool
                        [formattingPool putPreparedData: preparedData forRuleInRow:nil andColumn:nil withType: [formattingRule type] withArea:kCFAEntireTable];
                        
                        // Create a sorted data array for the whole table
                        dataArray = [self dataArrayForEntireTableForDataSource: proxyDatasource];
                        [dataArray removeObjectIdenticalTo:[NSNull null]];
                        [dataArray sortUsingSelector:@selector(compare:)];
                        // Calculate total sum of array data
                        total = [self calculateDataTotal:dataArray];
                        
                        if ([formattingRule applyOrder]) {
                            // Prepare data for conditional formatting rule
                            [formattingRule prepareWithSortedDataArray:dataArray
                            andDataTotal:total
                            inRow:nil
                            andColumn:nil
                            withArea: [rules area]];
                        }
                    }
                    // Create a helper for working with conditional formatting settings
                    MAConditionalFormattingHelper *formattingHelper = [[MAConditionalFormattingHelper alloc] initWithProxyDataSource: proxyDatasource];
                    // Apply conditional formatting settings to the whole table
                    [formattingHelper applyGlobalConditionalFormat: rules];
                    
                    // Get average value in the range (A; B)
                    double valueA = [[preparedData preparedValueA] doubleValue];
                    double valueB = [[preparedData preparedValueB] doubleValue];
                    double averageValue = (valueA + valueB) / 2;
                    // Determine whether obtained value belongs to this range
                    bool conformsToValue = [formattingRule conformsToValue: [NSNumber numberWithDouble: averageValue]];
                    NSLog(@"Value %f %@belongs to interval (%f; %f)", averageValue, (conformsToValue? @"": @"not "),
                    valueA, valueB);
                    // Determine color corresponding to this range
                    UIColor *color = [rules colorForValue: [NSNumber numberWithDouble: averageValue]];
                    const CGFloat *components = CGColorGetComponents([color CGColor]);
                    if (components != NULL) {
                        CGFloat r = components[0];
                        CGFloat g = components[1];
                        CGFloat b = components[2];
                        NSString *hexString=[NSString stringWithFormat:@"%02X%02X%02X", (int)(r * 255), (int)(g * 255), (int)(b * 255)];
                        NSLog(@"Color corresponding to value %f: #%@", averageValue, hexString);
                    }
                }
            }
        }
    }
}
// Creates a data array for the whole table
- (NSMutableArray *)dataArrayForEntireTableForDataSource:(MAGridProxyDataSource *)dataSource {
    // Get object used to work with table
    SPPLPivotTable pivotTable = dataSource.dataSource.pivotTable;
    // Get number of rows
    int rowCount = pivotTable->leftHeader()->elements()->elementsCount();
    // Get number of columns
    int columnCount = pivotTable->topHeader()->elements()->elementsCount();
    // Create a data array
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:rowCount * columnCount];
    for (int r = 0; r < rowCount; ++r) {
        SPPLPivotTableHeaderElement leftElement = pivotTable->leftHeader()->elements()->getElementByIndex(r);
        BOOL isRowInTotal = pivotTable->leftHeader()->elements()->isElementInTotals(leftElement);
        if (isRowInTotal) {
            continue;
        }
        for (int c = 0; c < columnCount; ++c) {
            SPPLPivotTableHeaderElement topElement = pivotTable->topHeader()->elements()->getElementByIndex(c);
            BOOL isColumnInTotal = pivotTable->topHeader()->elements()->isElementInTotals(topElement);
            if (isColumnInTotal) {
                continue;
            }
            // Get table cell data
            SNID data = pivotTable->getData(r, c);
            NSObject *value = (data != NULL) ? (NSObject *)data->nsObject() : nil;
            if ([value isKindOfClass:NSNumber.class]) {
                // Add cell value to array
                [result addObject:value];
            }
        }
    }
    
    return result;
}
// Calculates sum of data values from specified array
- (double)calculateDataTotal:(NSArray *)dataArray {
    double total = 0.0; // Sum of all data
    for (NSNumber *number in dataArray) {
        if (![number isEqual:nil]) {
            double doubleValue = [number doubleValue];
            total += doubleValue;
        }
    }
    return total;
}

After executing the example the conditional formatting with the use of the rule "Cells between A and B" is applied to the express report whole table. All the cells containing values from 7000 to 8000 are filled with yellow color:

The development environment console displays notifications whether the 7500 value belongs to the range (7000; 8000), also fill color of the cell corresponding to this value:

Value 7500.000000 belongs to range (7000.000000; 8000.000000)

Color corresponding to value 7500.000000: #FFC444

Identical result is obtained if the code fragment

[formattingRule prepareWithSortedDataArray:dataArray
andDataTotal:total
inRow:nil
andColumn:nil
withArea: [rules area]];

is replaced with the following script:

MAConditionalFormattingRulePreparedData *data =
[formattingPool preparedDataForRuleInRow:nil andColumn:nil
withType:[formattingRule type] withArea: [rules area]];
[formattingRule setValue:data forKey:@"preparedData"];
[formattingRule setValue:[NSNumber numberWithBool: NO]
forKey:@"empty"];

After executing the example conditional formatting is applied with the same settings:

See also:

Example of Component Use