//
// 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
上記のコード ロジックは、辞書が配列に含まれている json に基づいており、PRICELISTCATEGORY 値に応じてセクションを作成します。
[
{
"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
]
検索結果については、以下のスクリーンショットを参照してください