简体   繁体   中英

Further clarification of “How to implement UISearchController with objective c”

I have been trying to implement UISearchController by following stackoverflow thread:

How to implement UISearchController with objective c

and Apples's documentation, but can't make it working. When the initial controller ( EVTSearchViewController which is defined as UIViewController ) that complies with UISearchBarDelegate, UISearchControllerDelegate, UISearchResultsUpdating, UITableViewDelegate (I needed to make it compliant with UITableViewDelegate too, since I am making it a delegate of the other UITableViewController type EVTSearchResultsViewController *resultsController s tableView) delegates is presented, I see my .xib with the UISearchBar and UITableView , I click the search bar and start typing:

在此处输入图片说明

When I type one letter, search bar disappears and nothing is shown:

在此处输入图片说明

First, I do not want the search bar to disappear. Second, updateSearchResultsForSearchController seems not to be called at all since NSLog() set up there does not produce any output when I am typing in the search bar.

I have .xib for the EVTSearchViewController and it has a UISearchBar that I am connecting to the respective property: IBOutlet UISearchBar *searchBar which is then made to point to the UISearchControllers' searchBar:

self.searchBar = self.searchController.searchBar

There is also UITableView that I put below the UISearchBar in the .xib . Another controller that I am using inside EVTSearchViewController is EVTSearchResultsViewController , it is UITableViewController and it does not have its .xib .

Below is the code for viewDidLoad and updateSearchResultsForSearchController methods:

- (void)viewDidLoad
{
_resultsController = [[EVTSearchResultsViewController alloc] init];
_searchController = [[UISearchController alloc] initWithSearchResultsController:_resultsController];

self.searchController.searchResultsUpdater = self;
self.searchController.searchBar.placeholder = nil;
[self.searchController.searchBar sizeToFit];
self.searchBar = self.searchController.searchBar;


// we want to be the delegate for our filtered table so didSelectRowAtIndexPath is called for both tables
self.resultsController.tableView.delegate = self;
self.searchController.delegate = self;
self.searchController.dimsBackgroundDuringPresentation = YES; // default is YES
self.searchController.searchBar.delegate = self; // so we can monitor text changes + others

// Search is now just presenting a view controller. As such, normal view controller
// presentation semantics apply. Namely that presentation will walk up the view controller
// hierarchy until it finds the root view controller or one that defines a presentation context.
//
self.definesPresentationContext = YES;  // know where you want UISearchController to be displayed
}

- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {

// update the filtered array based on the search text
NSString *searchText = searchController.searchBar.text;
NSLog(@"searchText: %@", searchText);
if (searchText == nil) {

    // If empty the search results are the same as the original data
    self.searchResults = [[[EVTItemStore sharedStore] allItems] mutableCopy];

} else {

    NSMutableArray *searchResults = [[NSMutableArray alloc] init];

    NSArray *allEvents = [[EVTItemStore sharedStore] allItems];
    NSLog(@"allEvents: %@", allEvents);
    for (EVTItem *event in allEvents) {

        /*if ([event.number containsString:searchText] || [[phoneMO.closrr_id filteredId] containsString:searchText] || [[phoneMO.contact.fullname lowercaseString] containsString:[searchText lowercaseString]]) {
            [searchResults addObject:phoneMO];
        }*/
        if ([event.eventName containsString:searchText]) {
            [searchResults addObject:event];
        }
    }

    self.searchResults = searchResults;

}

// hand over the filtered results to our search results table
EVTSearchResultsViewController *resultsController = (EVTSearchResultsViewController *)self.searchController.searchResultsController;
resultsController.filteredEvents = self.searchResults;
[resultsController.tableView reloadData];
}

The respective @properties defined in EVTSearchViewController :

@interface EVTSearchViewController ()
@property (weak, nonatomic) IBOutlet UISearchBar *searchBar;

@property (nonatomic, strong) UISearchController *searchController;
@property (nonatomic, strong) EVTSearchResultsViewController *resultsController;
@property (nonatomic, strong) NSMutableArray *searchResults;

// For state restoration
@property BOOL searchControllerWasActive;
@property BOOL searchControllerSearchFieldWasFirstResponder;

@end

Then, here is the code for EVTSearchResultsViewController :

#import "EVTSearchResultsViewController.h"

@implementation EVTSearchResultsViewController

- (instancetype)init
{
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStylePlain];

if (self) {
}
return self;
}

- (void)viewDidLoad {
[super viewDidLoad];

[self.tableView registerClass:[UITableViewCell class]
       forCellReuseIdentifier:@"UISearchViewCell"];

}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (NSInteger)tableView:(UITableView *)tableView
 numberOfRowsInSection:(NSInteger)section {

return [self.filteredEvents count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView
     cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell =
    [tableView dequeueReusableCellWithIdentifier:@"UISearchViewCell"
                                forIndexPath:indexPath];

cell.textLabel.text = self.filteredEvents[indexPath.row];

return cell;
}

@end

The methods of EVTSearchResultsViewController above are not called at all which makes look very strange for me, why do we need it at all, then?

I tried to set UISearchBar the way Apple docs recommend:

self.resultsTableView.tableHeaderView = self.searchController.searchBar;

but it gives me a non-responsive search box, so when pressing nothing happens.

Could anyone, please, help with resolving the question. The other question linked above can also be clarified then. Thank you.

So, I solved the issue and implemented basic search with UISearchController . Here is what we need to do to implement the basic search:

  1. Create two UITableViewControllers classes with NO .xib files. Yes, there should be no .xib files and we are just creating two classes. In the code below their names are EVTSearchViewController and EVTSearchResultsViewController .
  2. Make one of the controllers to comply with the delegates: <UISearchBarDelegate, UISearchControllerDelegate, UISearchResultsUpdating>

Here is the code for the header file of EVTSearchViewController :

#import <UIKit/UIKit.h>

@interface EVTSearchViewController : UITableViewController <UISearchBarDelegate, UISearchControllerDelegate, UISearchResultsUpdating>

@end

Here is the EVTSearchResultsViewController 's header:

#import <UIKit/UIKit.h>

@interface EVTSearchResultsViewController : UITableViewController

@property (nonatomic, strong) NSMutableArray *filteredEvents;

@end

The NSMutableArray *filteredEvents will hold the results of the search. We should NOT implement any of the UITableViewController delegate methods in EVTSearchViewController.m , but should in EVTSearchResultsViewController.m .

Here is the top part of EVTSearchViewController :

#import "EVTSearchViewController.h"
#import "EVTSearchResultsViewController.h"

// Importing the class that stores `EVTItem` object classes
#import "EVTItemStore.h"
#import "EVTItem.h"

@interface EVTSearchViewController ()
@property (nonatomic, strong) UISearchController *searchController;
// We created this class
@property (nonatomic, strong) EVTSearchResultsViewController *resultsController;
// array to hold the results of the search
@property (nonatomic, strong) NSMutableArray *searchResults;

@end

Here is the code for EVTSearchViewController 's viewDidLoad: method:

- (void)viewDidLoad
{
[super viewDidLoad];
self.resultsController = [[EVTSearchResultsViewController alloc] init];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:self.resultsController];

self.searchController.searchResultsUpdater = self;
self.searchController.searchBar.placeholder = nil;
[self.searchController.searchBar sizeToFit];
// This line of code is very important. Here we are using apple docs'
// suggestion. UITableViewController has tableView property, so
// we are just setting tableView`s header to apples' UISearchController`s' `searchBar property
self.tableView.tableHeaderView = self.searchController.searchBar;

self.searchController.delegate = self;

// default is YES
self.searchController.dimsBackgroundDuringPresentation = YES;

// so we can monitor text changes + other changes
self.searchController.searchBar.delegate = self;

// know where you want UISearchController to be displayed
self.definesPresentationContext = YES;
}

Then we add to EVTSearchViewController the following method:

- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {

// update filtered array based on the search text
NSString *searchText = searchController.searchBar.text;
if (searchText == nil) {

    // If empty the search results should be the same as the original data
    self.searchResults = [[[EVTItemStore sharedStore] allItems] mutableCopy];

} else {

    NSMutableArray *searchResults = [[NSMutableArray alloc] init];

// [[EVTItemStore sharedStore] allItems] message retrieves
// all of the objects that I have in datastore EVTItemStore
    NSArray *allEvents = [[EVTItemStore sharedStore] allItems];

// EVTItem class has a property eventName which we are using
// for searching, then adding it to our searchResults array
    for (EVTItem *event in allEvents) {
        if ([event.eventName containsString:searchText]) {
            [searchResults addObject:event];
        }
    }

    self.searchResults = searchResults;
}

// hand over the filtered results to our search results table
EVTSearchResultsViewController *resultsController = (EVTSearchResultsViewController *)self.searchController.searchResultsController;
resultsController.filteredEvents = self.searchResults;
[resultsController.tableView reloadData];
}

Another controller's EVTSearchResultsViewController @implementation part looks like this:

@implementation EVTSearchResultsViewController

- (instancetype)init
{
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStylePlain];

if (self) {
}
return self;
}

- (NSInteger)tableView:(UITableView *)tableView
 numberOfRowsInSection:(NSInteger)section {
return [self.filteredEvents count];
}


- (UITableViewCell *)tableView:(UITableView *)tableView
     cellForRowAtIndexPath:(NSIndexPath *)indexPath {

UITableViewCell *cell =
    [tableView dequeueReusableCellWithIdentifier:@"UISearchViewCell"
                                forIndexPath:indexPath];
EVTItem *event = self.filteredEvents[indexPath.row];
cell.textLabel.text = event.eventName;
return cell;
}

- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class]
                         forCellReuseIdentifier:@"UISearchViewCell"];
}

@end

That's it. If we need to further customize our cells, we should be able to do it by making UISearchViewCell.xib for EVTSearchResultsViewController , and using the following viewDidLoad instead:

- (void)viewDidLoad
{
[super viewDidLoad];

// Load the NIB file
UINib *nib = [UINib nibWithNibName:@"UISearchViewCell" bundle:nil];

// Register this NIB which contains the cell
[self.tableView registerNib:nib forCellReuseIdentifier:@"UISearchViewCell"];
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM