Daniel Fernandez Daniel Fernandez - 1 month ago 9
iOS Question

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

Answer

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...