I have tried to implement a search bar but I have not had any luck dealing with this problem. I would really appreciate any help that can be provided. I've a big project in which I've a table view, and I want to implement a search bar over it and see the real time filtering. I do not use Storyboard but I'm using XIB. I've added the following protocols:
<UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate,UISearchDisplayDelegate>
I've declared 2 arrays in #interface , the first for the whole elements and the second one for the filtered ones:
NSArray* OldList;
NSArray* filteredList;
Then I've setted the number of rows and the number of sections and then:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
myClassCell *cell = [tableView dequeueReusableCellWithIdentifier:MYCELLCLASS];
if (cell == nil)
{
cell = [myClassCell newFromNib];
}
NSMutableDictionary* elem = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
elem = [filteredList objectAtIndex:indexPath.row];
if ([elem count]+1 > indexPath.row)
[cell showValues:elem];
else
[cell showValues:nil];
}
else
{
elem = [OldList objectAtIndex:indexPath.row];
if ([elem count]+1 > indexPath.row)
[cell showValues:elem];
else
[cell showValues:nil];
}
return cell;
}
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"name contains[c] %#", searchText];
filteredist = [OldList filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
At this point, I haven't done any changes to the xib, no links and no other stuffs. If I compile I get my table, but obviously if I try to search something, nothing works. Moreover if I scroll down to the end of the table the app crashes. The real problem is that I can't see the search bar working. Could someone help me please?
Well you're on the right track... it is exactly to do with the connection of your controller class to your controller xib.
When you want to initialise a Search Bar and Search Display Controller into a UITableView, you are effectively adding a second table view that, when activated, must be managed by code in your UITableViewController class in the same manner as any UITableView.
I have used these SO questions/answers to check my own answer - I recommend you take a look:
Creating a UISearchDisplayController programmatically
Gray UISearchBar w/matching scope bar programmatically
I have read the Apple Documentation. I recommend you do the same to help you understand this.
First Step:
You will need to set data source and delegate methods for both table views when you run your controller class.
Before you do any of this, include this property...
#property (nonatomic, strong) UISearchDisplayController *searchController;
The following code describes how to initialise and set the appropriate properties for a UISearchBar and a UISearchDisplayController. If you are programmatically creating a UITableViewController in code you will also need to set the data source and delegate for it (not shown to keep the code easy to read).
You have two options here - which one you choose depends on your code and what you wish to achieve - either set these in your init/awakeFromNib methods, or set these in one of your table view controller (TVC) lifecycle methods.
Option One - Init
(Note1: Paul Hegarty's extraordinary iTunesU lectures taught me to init/awake a class as follows - in this way you are covered for both scenarios - you call init or it can awakeFromNib.)
- (void)setup {
// Provide initialisation code here!!!
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
[searchBar sizeToFit];
[searchBar setDelegate:self];
[self setSearchController:[[UISearchDisplayController alloc] initWithSearchBar:searchBar
contentsController:self]];
[self.searchController setSearchResultsDataSource:self];
[self.searchController setSearchResultsDelegate:self];
[self.searchController setDelegate:self];
}
- (void)awakeFromNib {
[self setup];
}
- (id)initWithStyle:(UITableViewStyle)style {
self = [super initWithStyle:style];
if (self) {
[self setup];
}
return self;
}
OR
Option Two - TVC Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
[searchBar sizeToFit];
[searchBar setDelegate:self];
[self setSearchController:[[UISearchDisplayController alloc] initWithSearchBar:searchBar
contentsController:self]];
[self.searchController setSearchResultsDataSource:self];
[self.searchController setSearchResultsDelegate:self];
[self.searchController setDelegate:self];
[self.tableView setTableHeaderView:self.searchController.searchBar]; // see Note2
...< other code as required >...
}
Note2: Regardless of which of these options you choose, you will need to place the following line of code in your viewDidLoad method...
[self.tableView setTableHeaderView:self.searchController.searchBar]; // (or just searchBar)
Second Step:
Notes:
The table view that represents your complete data set (OldList) can be called using self.tableView (PS convention is to start each variable with lower case - so change your property name from OldList to oldList).
The table view that represents the filtered data set (filteredList) can be called using self.searchController.searchResultsTableView.
While you have prepared your tableView:cellForRowAtIndexPath: data source method, I suspect you have other data source (and maybe delegate) methods that need to be informed of which table view is the current table view, before they are able to function properly and provide you with a fully operational search results table view and search function.
For example:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (tableView == self.searchController.searchResultsTableView)
return 1;
return [[self.oldList sections] count];;
}
and:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.searchController.searchResultsTableView)
return [self.filteredList count];
return [self.oldList count];
}
Note that there may be other data source (and maybe delegate) methods that need to be informed of which table view is the current table view... I will leave it to you to determine which of these methods are to be modified, and the corresponding code necessary to adjust the table view.
Third Step:
You will be required to register a nib and reuse identifier for your search results table view.
I prefer to create a separate nib file (called "TableViewCellSearch.xib") that contains one table view cell, with the reuse identifier "SearchCell", and then place the code to register this nib and reuse identifier in the following UISearchDisplayController delegate method.
It is worth noting that this code is just as effective after the code block examples above in init/awakeFromNib/viewDidLoad.
- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView {
static NSString *cellIdentifierSearch = #"SearchCell";
UINib *nib = [UINib nibWithNibName:#"TableViewCellSearch" bundle:nil];
[self.searchController.searchResultsTableView registerNib:nib forCellReuseIdentifier:cellIdentifierSearch];
}
Try these suggestions.
Hope this helps.
Related
I have a tableView that displays data successfully and now what I want is to provide the search function for it. UISearchDisplayController is deprecated in iOS 9 and I'm new to iOS. So please tell me the way to do this.
If anyone can provide a code step by step,I appreciate it and it will also help others.This is my tableView code.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [airportList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ci"];
Details *newDetails = [airportList objectAtIndex:indexPath.row];
cell.textLabel.text = newDetails.airport;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Details *newDetails = [airportList objectAtIndex:indexPath.row];
NSString *selectedText = newDetails.airport;
[[NSUserDefaults standardUserDefaults] setObject:selectedText forKey:#"st"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self dismissViewControllerAnimated:YES completion:nil];
}
You can use UISearchController in iOS 9
First declare a property for UISearchController
#property (strong, nonatomic) UISearchController *searchController;
then, in viewDidLoad
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.searchBar.delegate = self;
When creating the UISearchController we do not need a separate search results controller as we will use the UITableViewController itself.
Likewise we will also use the UITableViewController to update the search results by having it implement the UISearchResultsUpdating protocol.
We do not want to dim the underlying content as we want to show the filtered results as the user types into the search bar.
The UISearchController takes care of creating the search bar for us.
The UITableViewController will also act as the search bar delegate for when the user changes the search scope.
Next, add searchBar to tableview header
self.tableView.tableHeaderView = self.searchController.searchBar;
Since the search view covers the table view when active we make the UITableViewController define the presentation context:
self.definesPresentationContext = YES;
We need to implement the UISearchResultsUpdating delegate to generate the new filtered results anytime the search text changes:
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
NSString *searchString = searchController.searchBar.text;
[self searchForText:searchString scope:searchController.searchBar.selectedScopeButtonIndex];
[self.tableView reloadData];
}
You can learn with Apple Sample Guide:Table Search with UISearchController.
“Table Search with UISearchController” is an iOS sample application that demonstrates how to use UISearchController. A search controller manages the presentation of a search bar (in concert with the results view controller’s content).
This one is driving me crazy - I don't know what am I missing.
here is my ViewController.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.tableView registerClass:[CurrentMatchCell class] forCellReuseIdentifier:#"CurrentMatchCell"];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
#pragma mark -
#pragma mark Table View Data Source Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"1");
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"2");
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"3");
CurrentMatchCell *cell = (CurrentMatchCell *)[tableView dequeueReusableCellWithIdentifier:#"CurrentMatchCell"];
if (cell == nil) {
NSLog(#"XXX");
}
[cell.matchDescription setText: #"Home Team vs Away Team"];
return cell;
}
Here is screenshots from the app.
delegate and datasource are set programmatically.
cell attributes :
And the .h file :
#interface CurrentMatchesViewController : UIViewController <NSFetchedResultsControllerDelegate,UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UITableView *tableView;
So, I can see logs 1,2,3 being printed out, cell is not nill but I do not see my content. Why is that?
I only see a number of empty white cells (even if I return 0 or whatever it does show the same every time).
Thanks
If you create your table view and your cell prototypes in a storyboard, the storyboard loader takes care of registering the cell prototypes that you defined in the storyboard. So:
You don't need to call registerClass:forCellReuseIdentifier: again in the code. This will actually mess up your storyboard settings.
You can also use dequeueReusableCellWithIdentifier:forIndexPath: instead of dequeueReusableCellWithIdentifier:. That method always returns a cell, so you don't have to have a nil check.
Edit: If that doesn't do the trick, try calling [self.tableView reloadData] after setting the delegate / data source, or set the delegate and data source in the storyboard.
It is because you're not loading your custom nib. Try this. (Make sure CurrentMatchCell is the name of your xib file).
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UINib *nib = [UINib nibWithNibName:#"CurrentMatchCell" bundle:nil];
[self.tableView registerNib:nib forCellReuseIdentifier:#"CurrentMatchCell"];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
Edit: based on your comment: Don't register the custom cell class when using a storyboard.... it does that for you and it also sets the delegate. So try removing those lines from the viewdidload.... and second I would try actually making CurrentMatchesViewController a subclass of UITableViewController
I have created .h and .m files for UITableView called mainTableViewgm.h and mainTableViewgm.m resp. and I am calling -initWithFrame: method from my main view controller to this mainTableViewgm.m implementation file
[[mainTableViewgm alloc]initWithFrame:tableViewOne.frame]
Note that this tableview is in my main view controller. But I have created separate files for the tableView and have also set the custom class to mainTableViewgm in storyboard.
the -initWithFrame: methods appears as follows
- (id)initWithFrame:(CGRect)frame
{
//NSLog(#"kource data");
self = [super initWithFrame:frame];
if (self)
{
[self setDelegate:self];
[self setDataSource:self];
[self tableView:self cellForRowAtIndexPath:0];
[self tableView:self numberOfRowsInSection:1];
// Initialization code
}
return self;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"kource data");
return 1;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"kource data2");
UITableViewCell*cellOne =[[UITableViewCell alloc]init];
cellOne.detailTextLabel.text=#"text did appear";
return cellOne;
}
the -initWithFrame: is being called fine along with the 'if (self)' block in this method. But the problem is numberOfRowsInSection: and cellForRowAtIndexPath: are not being automatically called here . kource data/kource data2 never appear in log. What do I do to load the table? Are the delegate/datasource being set incorrectly?
I must mention that I have also set the UITableViewDelegate and UITableviewDataSource protocols:
#interface mainTableViewgm : UITableView <UITableViewDelegate,UITableViewDataSource>
#end
Help will be much appreciated. Thank you.
Your tableview is not loaded when the controller is initializing, so you cannot do that in the init methods. You have to move your code to the viewDidLoad method.
Also you are not setting the delegate and datasource on the tableview object (probably a type, you are setting them on the view controller). It should look like this:
- (void)viewDidLoad:(BOOL)animated {
[super viewDidLoad:animated];
[self.tableView setDelegate:self];
[self.tableView setDataSource:self]; // <- This will trigger the tableview to (re)load it's data
}
Next thing is to implement the UITableViewDataSource methods correctly. UITableViewCell *cellOne =[[UITableViewCell alloc] init]; is not returning a valid cell object. You should use at least initWithStyle:. And take a look how to use dequeueReusableCellWithIdentifier:forIndexPath:. A typical implementation would look like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
// Reuse/create cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Update cell contents
cell.textLabel.text = #"Your text here";
cell.detailTextLabel.text=#"text did appear";
return cell;
}
I can't believe I've been doing XCode programming for two years, and still hit this issue.
I had the same problem with XCode 6.1 - I was setting my UITableView's delegate & dataSource in the viewWillAppear function, but none of the delegate functions were kicking in.
However, if I right-clicked on the UITableView on the Storyboard, the circles for delegate and dataSource were empty.
The solution, then, is to hold down the CTRL key, and drag from each of these circles up to the name of your UIView which contains your UITableView:
After doing this, my UITableView happily populated itself.
(So, we're upto v6.1 of XCode now are we ? Do you think Apple ever going to make this thing, you know, friendly...? I would quite like to add a Bookmark in my code... that'd be a nice feature.)
I've read all similar questions and tried all suggestions, still nothing. Maybe someone can spot my flaw.
My view controller is initiated from another view controller, by one of two buttons. Button taps send NSNotification (with attached arrays), and this view controller anticipates this notification and then calls this method:
- (void)addContentToArray:(NSNotification *)aNotification {
array = [NSArray arrayWithArray:[aNotification object]];
([array count] == 6) ? (category = YES) : (category = NO);
[myTableView reloadData];
NSLog(#"%d", [array count]);
NSLog(#"%#", myTableView);
}
The method gets called every time, I can see that from changing array count. Here notification object is the array passed from previous view controller, and I assign these objects to my local array property - this is my UITableView source. So what I do is I try to reuse the UITableView to display elements of whatever array is being passed. And it works nicely for the first array passed (whichever first).
When I tap the second button, the new array is passed successfully (as mentioned before, I know that from log of [array count] which is different: 3 vs 6 objects in different arrays). However, what is not happening is that UITableView does not refresh (although the values passed when I select a row in the table are from the correct arrays, even though wrong values are displayed).
Here are UITableView data source methods:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Identifier"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Identifier"];
}
cell.textLabel.text = [[array objectAtIndex:indexPath.row] objectForKey:#"name"];
if (category) {
cell.detailTextLabel.text = [[array objectAtIndex:indexPath.row] objectForKey:#"description"];
}
return cell;
}
So, what am I doing wrong?
A few other considerations that might help:
NSLog(#"%#", myTableView); returns (null), which is a bit worrying. myTableView here is UITableView from my nib file, which is correctly connected to the view controller, declared as property and synthesized
The view controller in question is a rightViewController of the PKRevealController, so when it is called repeatedly, viewWillAppear method is called, but not viewDidLoad (although, as I already mentioned, addContentToArray: method is being called every time as well)
Also, for those somewhat familiar with PKRevealController - when I try and log focusedController from my view controller, it says that frontViewController - the one that moves to reveal my view controller - is the one that is focused. Can that be the reason why myTableView is (null)?
I'd be grateful for any insight and help!
Need more code, that part where you created and call Myviewcontroller's addContentToArray method.
I think you used release code there for Myviewcontroller's object, try once with hide that part.
I managed to solve the issue by editing initWithNibName method (old line commented out)
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
//self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
self = [super initWithNibName:#"PurposeCategoryViewController" bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
Apparently, it did have something to do with the fact that my view controller (called PurposeCategoryViewController) was not the top/focused view controller in PKRevealController hierarchy. So, I just needed to specifically indicate my nib file.
What value are you returning in this delegate method:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
make sure it is 1
I have UITableViewController with a UISearchBar as the tableHeaderView of its tableView. I also have a UISearchDisplayController initialized with that UISearchBar as its searchBar and that UITableViewController as its contentsController. So far so good, everything almost works.
The problem is that the UITableView has cells which have their accessoryType set to UITableViewCellAccessoryDetailDisclosureButton. Here's what happens:
To start, everything looks as it should:
The user taps inside the UISearchBar.
The UISearchDisplayController creates the dark overlay on top of the main table, and makes the index (as in, sectionIndexTitlesForTableView) of the main table disappear.
Suppose the user at this point hides the keyboard (by pressing the iPad "hide keyboard" button on the standard keyboard)
Since the user hasn't typed anything into the UISearchBar yet, we can still see the main table, albeit underneath the dark overlay added by the UISearchDisplayController.
The hiding of the keyboard exposes more of the main table, causing the main table to load more cells.
Now here's the problem: Since these cells are loaded while the index of the main table is hidden, the disclosure button is shown too far too the right (at least, compared to the other cells).
Moreover, when the user now cancels the search, those cells may not be reloaded causing the disclosure button to be shown underneath the index (which is now visible again).
I'm at a loss on how to work around this; the only option I can think of is to find the UIView that corresponds to the disclosure button and manually move it, but that seems incredibly hacky, if only because even finding that UIView requires a nasty hack. Any suggestions on how to fix this in a nicer way would be much appreciated!
Minimal runnable example
Below is a minimal example. Just start a new XCode project, enable ARC, iPad only, and replace the contents of the AppDelegate with the below. Note that for the sake of the minimal example I force the main table to reload its cells in searchDisplayController:willShowSearchResultsTableView, otherwise the main table will cache its cells and the problem won't show (in my actual application the main table is reloading its cells for others reasons, I'm not completely sure why -- but of course it should be fine for the main table to reload cells at any time.)
To see the problem happening, run the code, type something in the search box (you will see "Search result 0 .. 5") and then cancel the search. The disclosure buttons of the main table are now shown underneath, rather than beside, the index.
Below is just the code:
#interface AppDelegate ()
#property (nonatomic, strong) UITableViewController* mainTableController;
#property (nonatomic, strong) UISearchDisplayController* searchDisplay;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UITableViewController* tableViewController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
UITableView* tableView = [tableViewController tableView];
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
[tableView setDataSource:self];
[self setMainTableController:tableViewController];
UISearchBar* searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 0, 44)]; // Width set automatically
[tableView setTableHeaderView:searchBar];
UISearchDisplayController* searchDisplay = [[UISearchDisplayController alloc] initWithSearchBar:searchBar
contentsController:tableViewController];
[searchDisplay setSearchResultsDataSource:self];
[searchDisplay setDelegate:self];
[self setSearchDisplay:searchDisplay];
[[self window] setRootViewController:tableViewController];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (tableView != [[self searchDisplay] searchResultsTableView]) {
return 26;
} else {
return 1;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView != [[self searchDisplay] searchResultsTableView]) {
return 10;
} else {
return 5;
}
}
- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView {
// The problem arises only if the main table view needs to reload its data
// In this minimal example, we force this to happen
[[[self mainTableController] tableView] reloadData];
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"SearchCell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView != [[self searchDisplay] searchResultsTableView]) {
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[[cell textLabel] setText:[NSString stringWithFormat:#"%c%d", 'A' + [indexPath section], [indexPath row]]];
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];
return cell;
} else {
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"SearchCell" forIndexPath:indexPath];
[[cell textLabel] setText:[NSString stringWithFormat:#"Search result %d", [indexPath row]]];
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];
return cell;
}
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
if (tableView != [[self searchDisplay] searchResultsTableView]) {
return [NSArray arrayWithObjects:#"A", #"B", #"C", #"D", #"E", #"F", #"G", #"H", #"I", #"J", #"K", #"L", #"M", #"N", #"O", #"P", #"Q", #"R", #"S", #"T", #"U", #"V", #"W", #"X", #"Y", #"Z", nil];
} else {
return nil;
}
}
If you are looking to mimic the way Apple's own apps seem to behave under these circumstances, then the correct course of action would be the cause the detail disclosure buttons to all move to the right when starting the search and then to all move back again once the search is complete.
I have achieved this myself in your example by calling reloadData on your main table view in two UISearchDisplayDelegate methods which I added to your code:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
[[[self mainTableController] tableView] reloadData];
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[[[self mainTableController] tableView] reloadData];
}
This will force your detail disclosure views to be repositioned to take account of the visibility of the table view index, keeping the position of all disclosure indicators consistent with each other whether in search mode or not.
Update
I've toyed around with reloading the table view in other UISearchDisplayDelegate methods including:
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView
- (void)searchDisplayController:(UISearchDisplayController *)controller willUnloadSearchResultsTableView:(UITableView *)tableView
But these produce a rather jarring effect with the positions of the detail disclosure buttons jumping around abruptly so I'd recommend the willBeginSearch and willEndSearch methods as previously stated.
The easiest, and possibly cleanest, way that I can think of is to tell your viewcontroller (or view) to listen for keyboard events. Then when the keyboard is minimized you resign the first responder of the search bar and reload your tableview(if it doesn't already reload it properly).
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//Your code here
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:self.window];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:self.window];
}
return self;
}
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name: UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name: UIKeyboardWillHideNotification object:nil];
}
And then add these two functions and do what you need in them.
- (void)keyboardWillHide:(NSNotification *)aNotification
{
//searchbar resignFirstResponder
//Tableview reload (if needed)
}
- (void)keyboardWillShow:(NSNotification *)aNotification
{
//Don't really need this one for your needs
}
Since you are resigning first responder in the keyboardwillhide function (before the keyboard starts moving) your tableview cells should reload properly without you having to reload them again.
The problem is not restricted to tables with section index titles. I had a similar problem with section header titles. If you add
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (tableView != [[self searchDisplay] searchResultsTableView]) {
return [NSString stringWithFormat:#"Section %c", 'A' + section];
} else {
return nil;
}
}
to your "Minimal runnable example" program, then you will observe that the section header titles appear also in the search table view as soon as the main table view is reloaded. (The same problem was reported here: Removing Header Titles from a UITableView in Search Mode.)
The only solution I know of is to avoid all updates to the main table view as long as the search display controller is active ([self.searchDisplay isActive] == YES) and reload the main table view only when the search table is unloaded:
- (void)searchDisplayController:(UISearchDisplayController *)controller willUnloadSearchResultsTableView:(UITableView *)tableView {
[[[self mainTableController] tableView] reloadData];
}