Filtering TableView content

1

I have a Property List, with the following structure with a total of 616 records. (Aspirin and Dipirone are examples)

I need to insert only the name of the product in a table and have the option to filter with the Search Bar and Search Display Controller, after the filter made by the user he can click on some product that will be sent to a new < in> view with the remaining content of each Products ex: (Risk, Lactation, Pregnancy)

    
asked by anonymous 12.05.2014 / 15:31

1 answer

1

I will describe a solution that is based on the architecture described in this answer , which already contains a master table and a view detail to show medicines. In particular, medication is a vector containing dictionaries where each dictionary represents a medicine. In addition, the drug dictionary contains a "name" key.

The first step is to edit your storyboard and drag a Search Bar Object Search Controller object to the view master as shown below.

Thiswilllinkasearchdisplaycontrollertothecontrollermasteranddisplaythesearchbar.Thesearchdisplaycontrollerdisplaysthefiltereddatainatable,soyoucanreusethedatasourcemethodsofthecontroller,beingcarefultodiscernwhentodisplayalldataoronlythefiltereddata.

Let'sstartwiththeextensionofthemasterclass,whichwillfollowtheprotocolsUISearchBarDelegateandUISearchDisplayDelegate.Inaddition,theclassstores,inadditiontothevectorwithallthedata,asecondvectorwiththefiltereddata:

@interfaceBAVMasterViewController()<UISearchBarDelegate,UISearchDisplayDelegate>@property(nonatomic,copy)NSArray*medicamentos;@property(nonatomic,copy)NSArray*medicamentosFiltrados;@end

Forfiltering,let'sdefineamethodthatreceivesthesearchcriteria,filtersthemedicamentosvectorbasedonthatcriterionandstorestheresultinthemedicamentosFiltradosvector:

-(void)filtrarMedicamentosComTexto:(NSString*)textoescopo:(NSString*)escopo{NSPredicate*predicado=[NSPredicatepredicateWithFormat:@"self.nome contains[cd] %@", texto];
    self.medicamentosFiltrados = [self.medicamentos filteredArrayUsingPredicate:predicado];
}

Note that the predicate makes use of the name attribute, which is stored in each dictionary representing a drug. % W /% indicates that% w / o% should ignore both box (uppercase and lowercase) and diacritics (accents). The scope is ignored.

This method is used in the [cd] protocol methods that ask whether the data should be reloaded when the user changes the search criteria:

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
    NSString *escopo = [[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]];
    [self filtrarMedicamentosComTexto:searchString escopo:escopo];
    return YES;
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
    NSString *escopo = [[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption];
    [self filtrarMedicamentosComTexto:self.searchDisplayController.searchBar.text escopo:escopo];
    return YES;
}

Both methods return contains , indicating that the search display controller needs to reload your data.

Now you have to provide the data for the search display controller . Because the master serves both unfiltered data (the master table) and filtered (the search display controller table), we change the master's methods so that it decides whether to use filtered data or not: p>

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSArray *medicamentos = (tableView == self.searchDisplayController.searchResultsTableView ? self.medicamentosFiltrados : self.medicamentos);
    return medicamentos.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    NSArray *medicamentos = (tableView == self.searchDisplayController.searchResultsTableView ? self.medicamentosFiltrados : self.medicamentos);
    NSDictionary *medicamento = medicamentos[indexPath.row];
    cell.textLabel.text = medicamento[@"nome"];
    return cell;
}

Note that the two above methods check whether the table that is requesting data is the _search display controller table. If so, these methods use the UISearchDisplayDelegate ; otherwise, they use the YES vector.

Only the medicamentosFiltrados method is responsible for setting the detail:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"showDetail"]) {
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        NSArray *medicamentos = (self.searchDisplayController.active ? self.medicamentosFiltrados : self.medicamentos);
        NSDictionary *medicamento = medicamentos[indexPath.row];
        [[segue destinationViewController] setMedicamento:medicamento];
    }
}

Unlike the two other methods above, medicamentos does not get a -prepareForSegue:sender: parameter, so we check if the search display controller is active, i.e. visible. If it is, the user touched a drug in the search results table, then we get the drug from the -prepareForSegue:sender: vector; otherwise, we use the UITableView * vector.

No changes are needed in the controller detail. Remember that it receives a medicine in the form of a dictionary and that dictionary contains all the data of the medicine.

medicines: vector or dictionary?

In general, a dictionary represents an object and a vector represents a (sequential) collection of objects. A drug list is a collection of medicines, that is, a vector of dictionaries where each dictionary is an individual drug.

In the specific case of displaying drugs in a medicamentosFiltrados , tables by definition are positional: there are a number of rows in the table, and each row has a number between 0 and total rows minus one. A vector works exactly the same way, so the vector element mapping to table row is straightforward.

If we were to write a solution where drugs were a dictionary as described in the question, we would need to implement a mapping of the key-value pairs of the dictionary to the table. Since the table works with positions (e.g. medicamentos ), this mapping needs to link a numeric position to each key-value pair of the dictionary. An alternative is to get the dictionary key list with UITableView , which returns ... a vector. At the end of the day, we ended up using vector again.

In addition, you see that, as designed in the question, the drug name serves as the key, and the value associated with that key is a dictionary containing the risks only, without the name. This means that with this dictionary alone it is not possible to know the name of the medicine: it is necessary to search for it within the parent dictionary or it is necessary to pass the name of the medicine together with the dictionary.

    
14.05.2014 / 12:08