Filtering content from Tableview

Asked

Viewed 236 times

1

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

propertyList

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

  • As I commented on another question from you, you are sure that medicamentos should be a dictionary instead of vector? As you have modeled, there is no way to find out the name of an individual medicine.

  • It doesn’t have to be necessarily dictionary, if you tell me another way to get what I need, ball show!

  • Honestly I am not wanting to do with BD, as it is a fixed content, if it gave to me to do with Propertylist, it would be very good, thanks for the attention @Bavarious

  • I do not know if there are functions for this, but if I make the name of the products in Vector, as I will rescue their respective contents in the next View (when it touches the product) (Risk, Pregnancy and Lactacao), that was the reason to make a Dictionary, because the product is the key to its content, I do not know if my thinking is correct, thank you

1 answer

1


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

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

Search Bar and Search Display Controller

This will hitch a search display controller at the controller master and will present the search bar. The search display controller presents the filtered data in a table, so you can reuse the data source methods of controller, taking care to discern when showing all data or only filtered data.

Let’s start with the extension of the master class, which will follow the protocols UISearchBarDelegate and UISearchDisplayDelegate. In addition, the class stores, in addition to the vector with all the data, a second vector with the filtered data:

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

For filtering, let’s define a method that takes the search criteria, filters the vector medicamentos based on that criterion and stores the result in the vector medicamentosFiltrados:

- (void)filtrarMedicamentosComTexto:(NSString *)texto escopo:(NSString *)escopo
{
    NSPredicate *predicado = [NSPredicate predicateWithFormat:@"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 medicine. The [cd] indicates that the contains should ignore both box (uppercase or lowercase) and diacritical (accents). The scope is ignored.

This method is used in protocol methods UISearchDisplayDelegate 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 YES, indicating that the search display controller need to reload your data.

It now remains to provide the data for the search display controller. How the master serves both unfiltered data (the master table) when filtered (the master table search display controller), we change the master’s methods so that he decides whether to use filtered data or not:

- (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 above two methods check whether the table that is requesting data is the _search display controller table. If so, these methods use the vector medicamentosFiltrados; otherwise, use the vector medicamentos.

Only the method is missing -prepareForSegue:sender:, responsible for configuring 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, -prepareForSegue:sender: does not receive a parameter UITableView *, then we check if the search display controller is active, i.e., visible. If so, the user touched a drug in the search results table, then we obtain the vector drug medicamentosFiltrados; otherwise we use the vector medicamentos.

No change is required in controller detail. Remember that he 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 list of medicines is a collection of medicines, i.e., a vector of dictionaries where each dictionary is an individual medicine.

In the specific case of the display of medicinal products in a UITableView, tables, by definition, are positional: there are a number of rows in the table and each row has a number between 0 and the total of rows minus one. An array works exactly the same way, so the mapping of element from vector to table row is direct.

If we were to write a solution in which medicines were a dictionary as described in the question, we would need to implement a mapping of the key-value pairs from the dictionary to the table. Since the table works with positions (e. g. indexPath.row), this mapping needs to link a numerical position to each key-value pair of the dictionary. An alternative is to get the dictionary’s key list with -[NSDicionary allKeys], that returns... an array. After all, we end up using vector again.

Also, see that, as designed in the question, the name of the drug 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 fetch it from the parent dictionary or it is necessary to pass the name of the medicine along with the dictionary.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.