Search code examples
iosobjective-cuitableviewsearchbar

Image in a Table View Cell when using SearchBar


I'm developing an app in Xcode in Objective-C. The app has a TableView with an array of restaurants. My problem is that when I try to use the SearchBar I use the Title (name of the restaurant as a filter) but when I show the rows after the filter search, only the Title is right. The other label and the image in the cell is wrong (it shows me the correct title but the image and the other label (description label) is the same as the first row in the original tableview).

I have to change my cellForRowAtIndexPath method but I don't now how to change it.

This is TableViewController called MainTableViewController.h

#import <UIKit/UIKit.h>

@interface MainTableViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>

@property (weak, nonatomic) IBOutlet UIBarButtonItem *barButton;

//@property (nonatomic, strong) NSArray *Images;
//@property (nonatomic, strong) NSArray *Description;
//@property (nonatomic, strong) NSArray *Title;
@property (nonatomic, strong) NSMutableArray *Title;
@property (nonatomic, strong) NSMutableArray *Images;
@property (nonatomic, strong) NSMutableArray *Description;
@property (nonatomic, strong) NSMutableArray *filteredRest;
@property BOOL isFiltered;

@property (strong, nonatomic) IBOutlet UITableView *RestTableView;

@property (strong, nonatomic) IBOutlet UITableView *mySearchBar;

@end  

This is my MainTableViewController.c

#import "MainTableViewController.h"
#import "SWRevealViewController.h"
#import "RestTableViewCell.h"
#import "RestViewController.h"

@interface MainTableViewController ()

@end

@implementation MainTableViewController

@synthesize mySearchBar, filteredRest, isFiltered;

- (void)viewDidLoad {
    [super viewDidLoad];

    _barButton.target = self.revealViewController;
    _barButton.action = @selector(revealToggle:);

    [self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];

    [self.navigationItem setTitle:@"MadEat"]; /*Cambia el titulo del navigation controller*/

    [self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]}]; /*Cambia el color de las letras del navigation controller bar del menu principal*/

    [self.navigationController.navigationBar setBarTintColor:[UIColor colorWithRed:27/255.0f green:101/255.0f blue:163/255.0f alpha:1.0f]];

    self.navigationController.navigationBar.tintColor = [UIColor whiteColor]; /*Cambia el color del boton de la izquierda*/

    self.RestTableView.tableFooterView = [[UIView alloc] init]; /*Esta linea hace que en la tabla solo aparezcan el numero de filas que tienes establecidas, es decir, que las vacias no aparezcan*/

    /*Alerta que se muestra solo la primera vez. Está desactivada*/
    if (![@"1" isEqualToString:[[NSUserDefaults standardUserDefaults] objectForKey:@"alert"]]) {
        [[NSUserDefaults standardUserDefaults] setValue:@"1" forKey:@"alert"];
        [[NSUserDefaults standardUserDefaults] synchronize];

        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Terms of use" message:@"The brands mentioned have no relationship with MadEat and the app has no any liability on that content." preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction* ok = [UIAlertAction actionWithTitle:@"Accept" style:UIAlertActionStyleDefault handler:nil];
        [alertController addAction:ok];

        [self presentViewController:alertController animated:YES completion:nil];
    }

    _Title = @[@"80 Grados",
               @"90 Grados",
               @"B&B Babel",
               @"Babelia",
               @"Bacira",
               @"Bar Galleta",
               @"Bar Tomate",
               @"Barra Atlantica",
               @"BaRRa de Pintxos",
               @"BaRRa de Pintxos",];

    _Description = @[@"Barrio Malasaña",
                     @"Barrio Retiro",
                     @"Barrio Chueca",
                     @"Barrio de Salamanca",
                     @"Barrio Chamberí",
                     @"Barrio Malasaña",
                     @"Barrio Chamberí",
                     @"Barrio Malasaña",
                     @"Barrio del Pilar",
                     @"Barrio Retiro",];

    _Images = @[@"80_grados.png",
                @"90_grados",
                @"babel.png",
                @"babelia.png",
                @"bacira.png",
                @"bar_galleta.png",
                @"bar_tomate.png",
                @"barra_atlantica.png",
                @"barra_de_pintxos.png",
                @"barra_de_pintxos.png",];
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{   
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    //return _Title.count;

    if (isFiltered == YES) {
        return filteredRest.count;
    } else {
        return _Title.count;
    }
}    

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"TableCell";
    RestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Configure the cell...
    if (isFiltered == YES) {
        cell.TitleLabel.text = [filteredRest objectAtIndex:indexPath.row];

    } else {
        int row = [indexPath row];
        cell.TitleLabel.text = _Title[row];
        cell.DescriptionLabel.text = _Description[row];
        cell.RestImage.image = [UIImage imageNamed:_Images[row]];
    }

    cell.RestImage.layer.cornerRadius = 6;
    cell.RestImage.clipsToBounds = YES;
    cell.RestImage.layer.borderWidth = 1;

    return cell;
}

-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    /*Cambia el nombre del boton de la izquierda sin afectar al titulo del navigation controller*/
    self.navigationItem.backBarButtonItem=[[UIBarButtonItem alloc] initWithTitle: NSLocalizedString (@"Back", nil) style:UIBarButtonItemStylePlain target:nil action:nil];

    if ([[segue identifier] isEqualToString:@"ShowDetails"]){
        RestViewController *restviewcontroller = [segue destinationViewController];

        NSIndexPath *myIndexPath = [self.tableView indexPathForSelectedRow];

        int row = [myIndexPath row];
        restviewcontroller.DetailModal = @[_Title[row],_Description[row],_Images[row]];

    }
}

-(void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {

    if (searchText.length == 0) {
        //Set our boolean flag
        isFiltered = NO;
    } else {
        //Set our boolean flag
        isFiltered = YES;
    }
    //Alloc and init our filteredData
    filteredRest = [[NSMutableArray alloc] init];

    for (NSString * restTitle in _Title) {
        NSRange restTitleRange = [restTitle rangeOfString:searchText options:NSCaseInsensitiveSearch];

        if (restTitleRange.location != NSNotFound) {
            [filteredRest addObject:restTitle];
        }
    }

    for (NSString * restDescription in _Description) {
        NSRange restDescriptionRange = [restDescription rangeOfString:searchText options:NSCaseInsensitiveSearch];

        if (restDescriptionRange.location != NSNotFound) {
            //[filteredRest addObject:restDescription];
        }
    }

    for (NSString * restImages in _Images) {
        NSRange restImagesRange = [restImages rangeOfString:searchText options:NSCaseInsensitiveSearch];

        if (restImagesRange.location != NSNotFound) {
            //[filteredRest addObject:restImages];
        }
    }

    //Reload our table view
    [_RestTableView reloadData];
}

-(void) searchBarSearchButtonClicked:(UISearchBar *)searchBar {
    [mySearchBar resignFirstResponder];
}

@end

Finally, this is my TableViewCell.h called RestTableViewCell.h

#import <UIKit/UIKit.h>

@interface RestTableViewCell : UITableViewCell

@property (strong, nonatomic) IBOutlet UILabel *TitleLabel;
@property (strong, nonatomic) IBOutlet UILabel *DescriptionLabel;
@property (strong, nonatomic) IBOutlet UIImageView *RestImage;

@end

This is my problem graphically:

The original TableView The problem filtering by Title


Solution

  • Obviously, you just apply the filter condition to the Title array, and not filtering the Images and Description array.

    So you need to two more NSMutableArray to hold the filtering results for Images and Description.

    But I recommend you to build a new Model for your results. Sample code for your reference:

    Model layer: Restaurant.h

    @interface Restaurant : NSObject
    
    @property (nonatomic, copy) NSString *title;
    @property (nonatomic, copy) NSString *desc;
    @property (nonatomic, copy) NSString *image;
    
    - (instancetype)init:(NSString *)title descripiton:(NSString *)description image:(NSString *)image;
    
    @end
    

    Restaurant.m

    @implementation Restaurant
    
    - (instancetype)init:(NSString *)title descripiton:(NSString *)description image:(NSString *)image {
        self = [super init];
        if (self != nil) {
            self.title = title;
            self.desc = description;
            self.image = image;
        }
        return self;
    }
    
    @end
    

    RestViewController.h

    @interface RestViewController : UIViewController
    
    @property (nonatomic, strong) Restaurant *DetailModal;
    
    @end
    

    MainTableViewController.m

    @interface MainTableViewController ()
    
    @property (nonatomic, strong) NSArray<Restaurant *> *originData;
    @property (nonatomic, strong) NSMutableArray<Restaurant *> *filteredRest;
    @property (nonatomic, assign) Boolean isFiltered;
    
    @end
    
    @implementation MainTableViewController
    
    @synthesize mySearchBar, filteredRest, isFiltered, originData;
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        originData = @[
                        [[Restaurant alloc] init:@"80 Grados" descripiton:@"Barrio Malasaña" image:@"80_grados.png"],
                        [[Restaurant alloc] init:@"90 Grados" descripiton:@"Barrio Retiro" image:@"90_grados"]
                        ];
        filteredRest = [NSMutableArray new];
        isFiltered = NO;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        // Return the number of rows in the section.
        //return _Title.count;
    
        if (isFiltered == YES) {
            return filteredRest.count;
        } else {
            return originData.count;
        }
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *CellIdentifier = @"TableCell";
        RestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    
        // Configure the cell...
        if (isFiltered == YES) {
            cell.TitleLabel.text = [filteredRest objectAtIndex:indexPath.row].title;
            cell.DescriptionLabel.text = [filteredRest objectAtIndex:indexPath.row].desc;
            cell.RestImage.image = [UIImage imageNamed:[filteredRest objectAtIndex:indexPath.row].image];
        } else {
            cell.TitleLabel.text = [originData objectAtIndex:indexPath.row].title;
            cell.DescriptionLabel.text = [originData objectAtIndex:indexPath.row].desc;
            cell.RestImage.image = [UIImage imageNamed:[originData objectAtIndex:indexPath.row].image];
        }
    
        cell.RestImage.layer.cornerRadius = 6;
        cell.RestImage.clipsToBounds = YES;
        cell.RestImage.layer.borderWidth = 1;
    
        return cell;
    }
    
    -(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {
        /*Cambia el nombre del boton de la izquierda sin afectar al titulo del navigation controller*/
        self.navigationItem.backBarButtonItem=[[UIBarButtonItem alloc] initWithTitle: NSLocalizedString (@"Back", nil) style:UIBarButtonItemStylePlain target:nil action:nil];
    
        if ([[segue identifier] isEqualToString:@"ShowDetails"]){
            RestViewController *restviewcontroller = [segue destinationViewController];
    
            NSIndexPath *myIndexPath = [self.tableView indexPathForSelectedRow];
    
            if (isFiltered) {
                restviewcontroller.DetailModal = filteredRest[myIndexPath.row];
            } else {
                restviewcontroller.DetailModal = originData[myIndexPath.row];
            }
        }
    }
    
    -(void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
    
        if (searchText.length == 0) {
            //Set our boolean flag
            isFiltered = NO;
        } else {
            //Set our boolean flag
            isFiltered = YES;
        }
        //Alloc and init our filteredData
        filteredRest = [[NSMutableArray alloc] init];
    
        for (Restaurant *item in originData) {
            if ([item.title containsString:searchText]) {
                [filteredRest addObject:item];
            }
        }
    
        //Reload our table view
        [self.tableView reloadData];
    }
    
    -(void) searchBarSearchButtonClicked:(UISearchBar *)searchBar {
        [mySearchBar resignFirstResponder];
    }
    
    @end
    

    With building a new model object for your information is very efficient, so you don't need to apply there filter to three array, you just need to search once, and results will be kept kept in one array.

    PS: One more tips for your coding style, naming the variable, object instance with starting lowercase, and naming the class with starting uppercase is best practise when writing OC codes. e.g., TitleLabel should rename to titleLabel, etc...