问题
I'm trying to set up a search display controller to handle async results from a web service. I've got the basic bits in place but have run into a really strange issue that I can't figure out.
Seems like to rig up the search display controller for async you really just need to do two things:
- return NO for searchDisplayController:shouldReloadTableForSearchString, and
- handle searchBarSearchButtonClicked and fire off the table reload myself.
I'm doing both of these but what I'm seeing is that the search display controller is reloading the table on the first character typed into the search bar even though I'm returning NO as per #1. It doesn't reload on subsequent characters entered.
So, my question is: how do I keep the search display controller from trying to reload the table while the user is typing? (specifically on that first character entered)
I've seen this issue mentioned as part of a couple of other questions but I have not seen a direct answer to the problem. I'd like to understand what's going on or what I'm doing wrong before I resort to a bunch of UI mangling to work around it.
Here's a quick distillation of my code to show the issue. When I run this and type "abcde" into the search bar, after I type "a" the results display as "a #0", "a #2", etc. They don't update again until I hit the search button then you see "abcde #0", "abcde #1", etc. Desired result is, of course, nothing happens until I hit the search button.
#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
return NO;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
return NO;
}
#pragma mark -
#pragma mark UISearchBarDelegate Methods
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[self.searchDisplayController.searchResultsTableView reloadData];
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 20;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [self.searchDisplayController.searchBar.text stringByAppendingFormat:@" #%d", indexPath.row];
return cell;
}
Thanks! (btw, this is my first question asked here—please let me know if I miss any points of etiquette :)
回答1:
This is just the way UISearchDisplayController (SDC) works. When the user enters the first character into the searchBar the searchTable is loaded and displayed for the first time causing it to load. The methods "...shouldReloadTableForSearchString" and "...shouldReloadTableForSearchScope" allow you to control whether the searchTable reloads automatically on subsequent chars or a scope change.
I've done both of the following to provide a good user experience on the first character. Slight disclaimer: I do have implementations of both of these that work but this is simply a framework for implementation from my memory. I may have missed a detail but this should get you pretty close.
Option 1: Present a "loading" cell in the searchTable when the first char is typed.
This option allows the SDC to display the searchResultsTableView when the user types the first char, display status as to the current search/filter operation
in the SDC delegate class definition
- add the iVar
BOOL isLoading
- add the iVar
UITableView *searchTableView
- add the iVar
in
searchDisplayController:didLoadSearchResultsTableView
- set
searchTableView = tableView
- set
in
shouldReloadTableForSearchString/Scope
- set
isLoading = YES
- call your method to load data in the background
- return NO
- set
when your background filter is complete:
- set
isLoading = NO
[searchTableView reloadData]
- set
in the various tableView delegate methods respond how you like to show status if there are current search results or results are loading in the background. What I did is:
- if there are current search results, show results (even if loading/filtering in the background)
- if there are no search results and
isLoading == NO
return 1 row and show 'No matches' in a cell - if there are no search results and
isLoading == YES
return 1 row and and show search activity in a cell (I typically use UIActivityIndicatorView)
Option 2: Hide the searchTableView and display an overlay view in it's place until search results are loaded
This option hides the searchTableView when it is first loaded and only redisplays it if when the search/filter is complete. I defined this as an add on to option 1 as they can be done together though to optimize things you may not care about showing search activity in the searchResultsTableView if you are hiding the table and showing the overlay.
in the SDC delegate class definition
- same as Option 1
- add the iVar
UIView *searchTableOverlayView
in
searchDisplayController:didLoadSearchResultsTableView
- same as Option 1
- create a UIView to use as an overlay in place of searchTableView containing whatever UI is appropriate for your app and set it to
searchTableOverlayView
in
searchDisplayController:didUnloadSearchResultsTableView
- release
searchTableOverlayView
- release
in 'searchDisplayController:didShowSearchResultsTableView
(may be able to do this in
searchDisplayController:willShowSearchResultsTableView`- if there are search results to display or
isLoading == NO
seachTableOverlayView.hidden == YES
- else (if
isLoading == YES
)searchTableOverlayView.frame == searchResultsTableView.frame
- add
seachTableOverlayView
as a subview ofsearchTableVIew.superview
searchTableView.hidden = YES
- if there are search results to display or
when your background filter is complete
- same as option 1
- if there are searchResults to display
searchTableCoverView.hidden = YES
searchResultsTableView.hidden = NO
- else
searchResultsTableView.hidden = YES
searchTableCoverView.hidden = NO
in the various tableView delegate methods respond how you like to show status if there are current search results or results are loading in the background. What I did is:
- same as option 1
回答2:
Unfortunately I believe this is just how the UISearchDisplayController
functions. You should file a bug report to Apple if you want to request different options for functionality.
The alternative would be to write your own UISearchBar
and UITableView
combination similar to how UISearchDisplayController
works, then you'll get better control.
Hope this helps!
回答3:
When I faced the same issue I tried to put an overlay view on search result view. That didn't help me. Looks like Apple change the behavior and I saw cell separators shone through my view. Finally I came up with a good approach.
At first my async search class had it's own overlay, progress and other views. UISearchDisplayController was used only for start, end delegate methods calls and sticking to default search flow. To show view I used any subview I can put a search views on (as an init call):
newView.frame = frame;
[view addSubview:newView];
and then use the subview when changing my search progress views:
if (view == currentView)
return;
view.frame = currentView.frame;
view.alpha = [self getAlphaValueForView:view];
UIView * superView = [currentView superview];
[currentView removeFromSuperview];
currentView = view;
[superView addSubview:currentView];
The problem with empty search result table view blinking appears when I start to use my search content controller in default navigation routine (navigation controller).
So to solve the problem I start using for navigation not a content controller (in this case the default search display controller activating) but an empty controller. And I used empty controllers view as a subview for search views.
So in total: DO NOT use UISearchDisplayController's content view controller in navigation. Use stub view controller. And then use it's view to put your views as subviews.
Worked great for me.
回答4:
Hide self.searchDisplayController.searchResultsTableView until user presse search button
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView;
{
tableView.hidden = YES;
}
After user press the search key, and the search results get ready, make the search result table visible
self.searchDisplayController.searchResultsTableView.hidden = NO;
[self.searchDisplayController reloadData]
This workaround works for me.
回答5:
Maybe you need to implement UISearchDisplayController
and then this function:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
来源:https://stackoverflow.com/questions/3903718/uisearchdisplaycontoller-cant-prevent-table-reload-on-typing-in-search-bar