Search in Grouped UITableView

2019-01-12 12:25发布

问题:

I am new in Objective-C development, and I am trying to filter the content of a grouped UITableView.

This is what I did but still dosen't work :

- (void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope
{
     NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS[cd] %@", searchText];;


    //activities is table that contains my objects that I display their contents in the table
    self.filteredData = [activities filteredArrayUsingPredicate:resultPredicate];
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchScope:(NSInteger)searchOption
{
    [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:
     [[self.searchDisplayController.searchBar scopeButtonTitles]
      objectAtIndex:searchOption]];

    // Return YES to cause the search result table view to be reloaded.
    return YES;
}
//activite is my object that I use to desplay its attribut in table
IPADActivity *activite ;
    if (ThetableView == self.searchDisplayController.searchResultsTableView) {
        activite = [self.filteredData objectAtIndex:indexPath.row];


    } 
    else{
        activite = [[objects
                      objectForKey:[objectsIndex objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
    }

Any suggestion please

回答1:

//
//  ViewController.m
//  SearchTut
//


#import "ViewController.h"
#import "SearchTableCell.h"
#import "XMLReader.h"

@interface ViewController ()<UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate>

@property (weak, nonatomic) IBOutlet UITableView *tablView;
@property (weak, nonatomic) IBOutlet UISearchBar *searchBr;

@property (nonatomic, strong) NSMutableArray *arrayList; //main table array
@property (nonatomic, strong) NSMutableArray *arrayFilteredList; //array with search results while user types in search bar
@property (nonatomic, strong) NSMutableDictionary *dictList; //need to manufacture an equivalent dictionary like below from the json array
/*
 @{
     @"B" : @[@"Bear", @"Black Swan", @"Buffalo"],
     @"C" : @[@"Camel", @"Cockatoo"],
     @"D" : @[@"Dog", @"Donkey"],
     @"E" : @[@"Emu"],
     @"G" : @[@"Giraffe", @"Greater Rhea"],
     @"H" : @[@"Hippopotamus", @"Horse"],
     @"K" : @[@"Koala"],
     @"L" : @[@"Lion", @"Llama"],
     @"M" : @[@"Manatus", @"Meerkat"],
     @"P" : @[@"Panda", @"Peacock", @"Pig", @"Platypus", @"Polar Bear"],
     @"R" : @[@"Rhinoceros"],
     @"S" : @[@"Seagull"],
     @"T" : @[@"Tasmania Devil"],
     @"W" : @[@"Whale", @"Whale Shark", @"Wombat"]
 };
 Refer: https://www.appcoda.com/ios-programming-index-list-uitableview/
*/

@property (nonatomic, strong) NSMutableDictionary *dictListFiltered; //dictionary when searched
@property (nonatomic, assign) BOOL isFiltered; //If user starts entering text in search bar, this flag is YES. If there is no text in search bar, this flag is NO.

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Initialisers
    self.tablView.dataSource = self;
    self.tablView.delegate = self;
    self.searchBr.delegate = self;
    self.arrayList = [NSMutableArray array];
    self.dictList = [NSMutableDictionary dictionary];
    self.dictListFiltered = [NSMutableDictionary dictionary];
    self.isFiltered = NO;

    //Get and parse XML from file and load the result in array
    NSString *strXMLPath = [[NSBundle mainBundle] pathForResource:@"List" ofType:@"xml"];
    NSError *error;
    NSString *strXML = [NSString stringWithContentsOfFile:strXMLPath encoding:NSUTF8StringEncoding error:&error];
    NSDictionary *dict = [XMLReader dictionaryForXMLString:strXML error:&error];
    NSDictionary *dictPriceList = [dict objectForKey:@"PRICELIST"];
    NSArray *arrayLine = [dictPriceList objectForKey:@"LINE"];
    NSMutableArray *arrayTempPriceList = [NSMutableArray array];
    for (NSDictionary *dict in arrayLine) {
        [arrayTempPriceList addObject:dict];
    }
    self.arrayList = arrayTempPriceList;
    NSLog(@"%@", self.arrayList);

    //Create dictionary for sectioned table grouped with respect to PRICELISTCATEGORY value. PRICELISTCATEGORY value will be set in the section title. Logic to group table in sections is below
    [self groupXMLinSectionsWithArray:arrayTempPriceList];
}

- (void)groupXMLinSectionsWithArray:(NSMutableArray *)arrayJson {
    if (arrayJson.count > 0) { //added this condition to reset json during search.
        for (NSDictionary *dict in arrayJson ) {
            NSString *strPriceListCategory = [[dict objectForKey:@"PRICELISTCATEGORY"] objectForKey:@"text"];
            if (self.isFiltered) {
                if ([[self.dictListFiltered allKeys] containsObject:strPriceListCategory]) {
                    NSMutableArray *arrayTemp = [self.dictListFiltered objectForKey:strPriceListCategory];
                    [arrayTemp addObject:dict];
                    [self.dictListFiltered setObject:arrayTemp forKey:strPriceListCategory];
                } else {
                    NSMutableArray *arrayTemp = [[NSMutableArray alloc] initWithObjects:dict, nil];
                    [self.dictListFiltered setObject:arrayTemp forKey:strPriceListCategory];
                }
            } else {
                if ([[self.dictList allKeys] containsObject:strPriceListCategory]) {
                    NSMutableArray *arrayTemp = [self.dictList objectForKey:strPriceListCategory];
                    [arrayTemp addObject:dict];
                    [self.dictList setObject:arrayTemp forKey:strPriceListCategory];
                } else {
                    NSMutableArray *arrayTemp = [[NSMutableArray alloc] initWithObjects:dict, nil];
                    [self.dictList setObject:arrayTemp forKey:strPriceListCategory];
                }
            }

        }
    } else { //if search results yield no json array, then remove all objects from dictionary
        [self.dictList removeAllObjects];
        [self.dictListFiltered removeAllObjects];
    }
    [self.tablView reloadData];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - TableView Datasource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSArray *arrayTemp = [NSArray array];
    if (self.isFiltered) {
        NSArray *arrayListAllKeys = [self.dictListFiltered allKeys];
        arrayTemp = [self.dictListFiltered objectForKey:[arrayListAllKeys objectAtIndex:section]];
    } else {
        NSArray *arrayListAllKeys = [self.dictList allKeys];
        arrayTemp = [self.dictList objectForKey:[arrayListAllKeys objectAtIndex:section]];
    }
    return [arrayTemp count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    SearchTableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SearchTableCellId" forIndexPath:indexPath];
    NSDictionary *dict;
    if (self.isFiltered) {
        NSArray *arrayPriceListAllKeys = [self.dictListFiltered allKeys];
        NSArray *arrayPrice = [self.dictListFiltered objectForKey:[arrayPriceListAllKeys objectAtIndex:indexPath.section]];
        dict = [arrayPrice objectAtIndex:indexPath.row];
    } else {
        NSArray *arrayPriceListAllKeys = [self.dictList allKeys];
        NSArray *arrayPrice = [self.dictList objectForKey:[arrayPriceListAllKeys objectAtIndex:indexPath.section]];
        dict = [arrayPrice objectAtIndex:indexPath.row];
    }
    cell.lblBarCode.text = [[dict objectForKey:@"APNBARCODE"] objectForKey:@"text"];
    cell.lblPackDescription.text = [[dict objectForKey:@"PACKDESCRIPTION"] objectForKey:@"text"];
    cell.lblProduct.text = [[dict objectForKey:@"PRODUCT"] objectForKey:@"text"];
    cell.lblProductName.text = [[dict objectForKey:@"PRODUCTNAME"] objectForKey:@"text"];

    return cell;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    if (self.isFiltered) {
        return [[self.dictListFiltered allKeys] count];
    } else {
        return [[self.dictList allKeys] count];
    }
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    NSString *price = @"";
    if (self.isFiltered) {
        price = [[self.dictListFiltered allKeys] objectAtIndex:section];
    } else {
        price = [[self.dictList allKeys] objectAtIndex:section];
    }
    return price;
}

- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 50.0;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    NSString *sectionTitle = [self tableView:tableView titleForHeaderInSection:section];
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(8, 8, self.tablView.frame.size.width - 16, 30)];
    //If you add a bit to x and decrease y, it will be more in line with the tableView cell (that is in iPad and landscape)
    label.backgroundColor = [UIColor clearColor];
    label.textColor = [UIColor whiteColor];
    label.shadowColor = [UIColor whiteColor];
    label.shadowOffset = CGSizeMake(0.5, 0.5);
    label.font = [UIFont boldSystemFontOfSize:18];
    label.text = sectionTitle;

    // Create header view and add label as a subview
    UIView *viewHeader = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tablView.frame.size.width, 50)];
    viewHeader.backgroundColor = [UIColor colorWithRed:0.4 green:0.4 blue:0.4 alpha:1];
    [viewHeader addSubview:label];
    return viewHeader;
}

#pragma mark - SearchBar Delegates

- (void)updateSearchResults
{
    //You can use the below commented code in case you need an exact match of the PRODUCT value text you entered in search bar
    /*
     NSMutableArray *searchResults = [self.arrayList mutableCopy];
     NSString *strippedString = [searchText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
     NSPredicate *predicate = [NSPredicate predicateWithFormat:@"PRODUCT.text==[c] %@", strippedString];
     searchResults = [[searchResults filteredArrayUsingPredicate:predicate] mutableCopy];
     // hand over the filtered results to our search results table
     self.arrayFilteredList = searchResults;
     //NSLog(@"%@", self.arrayFilteredPriceList);
     [self groupXMLinSectionsWithArray:self.arrayFilteredList];
     */


    //Below code searches depending on Product / Product Name / APNBarCode values
    NSString *searchText = self.searchBr.text;
    NSMutableArray *searchResults = [self.arrayList mutableCopy];
    NSString *strippedString = [searchText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    NSArray *searchItems = nil;
    if (strippedString.length > 0) {
        searchItems = [strippedString componentsSeparatedByString:@" "];
    }

    NSMutableArray *andMatchPredicates = [NSMutableArray array];

    for (NSString *searchString in searchItems) {
        NSMutableArray *searchItemsPredicate = [NSMutableArray array];

        // Below we use NSExpression represent expressions in our predicates.
        // NSPredicate is made up of smaller, atomic parts: two NSExpressions (a left-hand value and a right-hand value)

        // Product
        NSExpression *lhsProduct = [NSExpression expressionForKeyPath:@"PRODUCT.text"];
        NSExpression *rhsProduct = [NSExpression expressionForConstantValue:searchString];
        NSPredicate *finalPredicateProduct = [NSComparisonPredicate
                                              predicateWithLeftExpression:lhsProduct
                                              rightExpression:rhsProduct
                                              modifier:NSDirectPredicateModifier
                                              type:NSContainsPredicateOperatorType
                                              options:NSCaseInsensitivePredicateOption];
        [searchItemsPredicate addObject:finalPredicateProduct];

        // Product Name
        NSExpression *lhsProductName = [NSExpression expressionForKeyPath:@"PRODUCTNAME.text"];
        NSExpression *rhsProductName = [NSExpression expressionForConstantValue:searchString];
        NSPredicate *finalPredicateProductName = [NSComparisonPredicate
                                                  predicateWithLeftExpression:lhsProductName
                                                  rightExpression:rhsProductName
                                                  modifier:NSDirectPredicateModifier
                                                  type:NSContainsPredicateOperatorType
                                                  options:NSCaseInsensitivePredicateOption];
        [searchItemsPredicate addObject:finalPredicateProductName];


        // APN Bar Code
        NSExpression *lhsAPNBarCode = [NSExpression expressionForKeyPath:@"APNBARCODE.text"];
        NSExpression *rhsAPNBarCode = [NSExpression expressionForConstantValue:searchString];
        NSPredicate *finalPredicateAPNBarCode= [NSComparisonPredicate
                                                predicateWithLeftExpression:lhsAPNBarCode
                                                rightExpression:rhsAPNBarCode
                                                modifier:NSDirectPredicateModifier
                                                type:NSContainsPredicateOperatorType
                                                options:NSCaseInsensitivePredicateOption];
        [searchItemsPredicate addObject:finalPredicateAPNBarCode];

        // AVERAGECOST
        NSExpression *lhsPACKDESCRIPTION = [NSExpression expressionForKeyPath:@"PACKDESCRIPTION.text"];
        NSExpression *rhsPACKDESCRIPTION = [NSExpression expressionForConstantValue:searchString];
        NSPredicate *finalPredicatePACKDESCRIPTIONt = [NSComparisonPredicate
                                                predicateWithLeftExpression:lhsPACKDESCRIPTION
                                                rightExpression:rhsPACKDESCRIPTION
                                                modifier:NSDirectPredicateModifier
                                                type:NSContainsPredicateOperatorType
                                                options:NSCaseInsensitivePredicateOption];
        [searchItemsPredicate addObject:finalPredicatePACKDESCRIPTIONt];

        // at this OR predicate to our master AND predicate
        NSCompoundPredicate *orMatchPredicates = [NSCompoundPredicate orPredicateWithSubpredicates:searchItemsPredicate];
        [andMatchPredicates addObject:orMatchPredicates];

    }

    // match up the fields of the Product object
    NSCompoundPredicate *finalCompoundPredicate =
    [NSCompoundPredicate andPredicateWithSubpredicates:andMatchPredicates];
    searchResults = [[searchResults filteredArrayUsingPredicate:finalCompoundPredicate] mutableCopy];
    // hand over the filtered results to our search results table
    self.arrayFilteredList = searchResults;
    //NSLog(@"%@", self.arrayFilteredPriceList);

    [self groupXMLinSectionsWithArray:self.arrayFilteredList];
}

//This method gets called when user starts entering text in search bar
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
    if (searchText.length == 0) {
        self.isFiltered = NO;
        [self.dictList removeAllObjects];
        [self groupXMLinSectionsWithArray:self.arrayList];
        [self.tablView reloadData];
    } else {
        self.isFiltered = YES;
        self.arrayFilteredList = [NSMutableArray new];
        [self.dictListFiltered removeAllObjects];
        [self updateSearchResults];
    }
}

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
    [searchBar resignFirstResponder];
    [self.dictList removeAllObjects];
    [self groupXMLinSectionsWithArray:self.arrayList];
    [self.tablView reloadData];
}

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
    [searchBar resignFirstResponder];
    [self.tablView reloadData];
}


@end

Above code logic is based on the json with dictionaries contained in an array and we create sections depending on PRICELISTCATEGORY value

 [
    { 
       "PRODUCT": {
          "text": "GELDCMFRAME"
       },
       "PRODUCTMAJORGROUP": {
          "text": "IC"
       },
       "PRICELISTCATEGORY": {
          "text": "GELDISP"
       },
       "APNBARCODE": {
          "text": "931000"
       },
       "PACKDESCRIPTION": {
          "text": "Some Description"
       },
       "PRODUCTNAME": {
          "text": "Description"
       }
       and so on
    }, and so on
 ]

Refer screenshot below for search results