searchdisplaycontroller: change the text of the searchbar - iphone

SHORT DESCRIPTION OF PROBLEM:
I want to set the text of a searchbar without automatically triggering the search display controller that is bound to it.
LONG DESCRIPTION OF PROBLEM:
I have an iphone application with a search bar and a search display controller. The searchdisplaycontroller is used for autocomplete. For autocomplete i use an sqlite database. The user enters the first few letters of a keyword and the result are shown in the table of the searchdisplaycontroller. An sql select query is executed for every character typed. this part works ok, the letters have been entered and the results are visible.
The problem is the following: If the user selects a row from the table I want to change the text of the searchbar to the text that was selected in the autocomplete results table. I also want to hide the search display controller. This is not working. After the search display controller disappears the textbox in the search bar is empty. I have no idea what's wrong. I didn't think something so simple as changing the text of a textbox can get so complicated.
I have tried to change the text in 2 methods:
First in the didSelectRowAtIndexPath method (for the search results table of the search display controller), but that didn't help. The text was there while the search display controller was active (animating away) but after that the textbox was empty.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"didSelectRowAtIndexPath");
if (allKeywords.count > 0)
{
didSelectKeyword = TRUE;
self.selectedKeyword = (NSString *)[allKeywords objectAtIndex: indexPath.row];
NSLog(#"keyword didselectrow at idxp: %#", self.selectedKeyword);
shouldReloadResults = FALSE;
[[self.mainViewController keywordSearchBar] setText: selectedKeyword];
//shouldReloadResults = TRUE;
[[mainViewController searchDisplayController] setActive:NO animated: YES];
}
}
I also tried to change it in the searchDisplayControllerDidEndSearch method but that didn't help either. The textbox was...again.. empty.
edit: actually it wasnt empty the text was there but after the disappearing animation the results of the autocomplete table are still there. So it ends up getting worse.
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
NSLog(#"searchDisplayControllerDidEndSearch");
if (didSelectKeyword)
{
shouldReloadResults = FALSE;
NSLog(#"keyword sdc didendsearch: %#", selectedKeyword);
[[mainViewController keywordSearchBar] setText: selectedKeyword]; //
In this method i call another method which selects the data from sqlite. This part is working.
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
NSLog(#"shouldReloadResults : %i", shouldReloadResults);
if (shouldReloadResults)
{
NSLog(#"shouldReloadTableForSearchString: %#", searchString);
[self GetKeywords: searchString : #"GB"];
NSLog(#"shouldReloadTableForSearchString vege");
return YES;
}
return NO;
}
Please ask me if it's not clear what my problem is. I need your help. Thank you

Didn't you try :
mainViewController.searchDisplayController.searchBar.text=selectedKeyword;
You access the search bar field from the search controller and you update its text content.

Now it works i don't know what happened. I think I tried to deactivate the search display controller after changing the text. Anyway thanks for help. I think I also tried to change the original textfield not the one belonging to the search display controller and they are different (different adresses in memory as far as i observed)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSLog(#"didSelectRowAtIndexPath");
if (allKeywords.count > 0)
{
didSelectKeyword = TRUE;
self.selectedKeyword = (NSString *)[allKeywords objectAtIndex: indexPath.row];
NSLog(#"keyword didselectrow at idxp: %#", self.selectedKeyword);
//shouldReloadResults = FALSE;
[[mainViewController searchController] setActive:NO animated: YES];
[mainViewController.searchController.searchBar setText: self.selectedKeyword];
}
}

Related

uisearchdisplaycontroller preload search results and search bar

How can I preload a search query in a UISearchDisplayController. Currently I'm doing the following:
[self.searchDisplayController setActive: YES animated: YES];
self.searchDisplayController.searchBar.hidden = NO;
self.searchDisplayController.searchBar.text = self.pendingSearchTerm;
[self.searchDisplayController.searchBar becomeFirstResponder];
Although this shows the search results window and also searches the table, it doesn't show the search bar in navigation bar like it would if user had clicked the search bar. If the search bar doesn't show up, it might not be clear to the user that the search has been performed.
Is there a way to make the search bar appear?
So after a lot of brute force, I finally figured out a workable solution.
Here's my setup: I have two table views - T1, T2. T1's data is local and T2 fetches data from internet. When user searches in T1, and there are no results, user can tap on a button to perform the same search on T2. So now when T2 comes up, it'll show the navigation item by default - and this is something I couldn't change.
In -[T2 viewDidAppear:]
[self.searchDisplayController setActive: YES animated: NO];
self.searchDisplayController.searchBar.text = self.pendingSearchTerm;
self.searchDisplayController.searchResultsTableView.hidden = YES; // so that we don't see "no results"
When data for T2 is ready:
[self.tableView reloadData];
self.searchDisplayController.searchBar.text = self.searchDisplayController.searchBar.text; // to reload search
self.searchDisplayController.searchResultsTableView.hidden = NO;
Hopefully this helps someone. If you are able to figure out a way to hide the navigation bar initially, that'd be solve the rest of my problem.
What you can do is When the user clicks on search and the string is empty change the searchBartext to "\n" carriage return
self.searchDisplayController.searchBar.text = #"\n";
The carriage return is not displayed in the searchbar an also it will not be the same string, as if the user types "\n". so it works fine.
In the
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
method you can set the searchbar text back to #"". if the searchstring was #"\n"
self.searchDisplayController.searchBar.text = #"";
That's what worked for me!

iOS show a popover with tableview from search bar in navigation bar

I have a search bar in a navigation bar, and want it to behave as follows:
1) When the user starts typing in the search bar, a popover appears and shows the list of products in a table view in the popover as per the string entered in the search bar.
2) This data should be refreshed with every new letter entered.
3) Call a method when an item in the table view is selected
How can I accomplish this?
Thanks for your help, janusfidel.
I found a good source code example that describes what I need: ToolbarSearch
using a UISearchBar, you can accomplish this, it has delegate method that listens to text changes, you can use it like the code below
- (void)searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchText{
if([searchText length] > 0) {
[searchResultArray removeAllObjects];
searchResultArray = [self searchTableView:textFromSearchbar];
[popOverView presentData:searchResultArray];
}
}
-(NSMutableArray*)searchTableView:(NSString*)string{
NSMutableArray* result;
//do the searching now
return result;
}

UISearchDisplayContoller – can't prevent table reload on typing in search bar

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 :)
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
in searchDisplayController:didLoadSearchResultsTableView
set searchTableView = tableView
in shouldReloadTableForSearchString/Scope
set isLoading = YES
call your method to load data in the background
return NO
when your background filter is complete:
set isLoading = NO
[searchTableView reloadData]
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
in 'searchDisplayController:didShowSearchResultsTableView(may be able to do this insearchDisplayController: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 of searchTableVIew.superview
searchTableView.hidden = YES
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
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!
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.
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.
Maybe you need to implement UISearchDisplayController and then this function:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText

Properly setting up willSelectRowAtIndexPath and didSelectRowAtIndexPath to send cell selections

Feel like I'm going a bit nutty here. I have a detail view with a few stand-alone UITextFields, a few UITextFields in UITAbleViewCells, and one single UITableViewCell that will be used to hold notes, if there are any. I only want this cell selectable when I am in edit mode. When I am not in edit mode, I do not want to be able to select it. Selecting the cell (while in edit mode) will fire a method that will init a new view. I know this is very easy, but I am missing something somewhere.
Here are the current selection methods I am using:
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (!self.editing) {
NSLog(#"Returning nil, not in edit mode");
return nil;
}
NSLog(#"Cell will be selected, not in edit mode");
if (indexPath.section == 0) {
NSLog(#"Comments cell will be selected");
return indexPath;
}
return nil;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (!self.editing) {
NSLog(#"Not in edit mode. Should not have made it this far.");
return;
}
if (indexPath.section == 0)
[self pushCommentsView];
else
return;
}
My problem is really 2 fold;
1) Even when I'm not in edit mode, and I know I am returning nil (due to the NSLog message), I can still select the row (it flashes blue). From my understanding of the willSelectRowAtIndexPath method, this shouldn't be happening. Maybe I am wrong about this?
2) When I enter edit mode, I can't select anything at all. the willSelectRowAtIndexPath method never fires, and neither does the didSelectRowAtIndexPath. The only thing I am doing in the setEditing method, is hiding the back button while editing, and assigning firstResponder to the top textField to get the keyboard to pop up. I thought maybe the first responder was getting in the way of the click (which would be dumb), but even with that commented out, I cannot perform the cell selection during editing.
Good lord I am an idiot. I never added these lines:
self.tableView.allowsSelection = NO; // Keeps cells from being selectable while not editing. No more blue flash.
self.tableView.allowsSelectionDuringEditing = YES; // Allows cells to be selectable during edit mode.
Sorry for the garbage question.
The documentation notes that tableView:willSelectRowAtIndexPath: isn't called when in editing mode. In addition, the blue flash will happen even if you cancel the selection. From the documentation:
This method is not called until users touch a row and then lift their finger; the row isn't selected until then, although it is highlighted on touch-down. You can use UITableViewCellSelectionStyleNone to disable the appearance of the cell highlight on touch-down. This method isn’t called when the editing property of the table is set to YES (that is, the table view is in editing mode).

UISearchDisplayController - how to display search result with only by scope button selected but empty search string

The UISearchDisplayController is very handy and implementing search is pretty straightforward.
However, I bump into problem when, in my app, I want to display search result with empty search string but selected scope button.
It seems like it's a must to enter some search string in order to get the search result table being initialized and displayed.
Is there any ways to display search result immediately after user has picked a scope but not entered search word yet?
Thanks
Bill
when you tap a new scope button the selectedScopeButtonIndex fires:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption;
you could capture the title fire off your search here using:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]
Won't work on the initial scope index, but you could just fire off your search initially based on the last used selectedScopeButtonIndex
I was after the same thing and just found something in the Apple developer forums: The UISearchDisplayController is implemented in a way that the results table won't be shown until some text is entered. There's also a bug report about this: ID# 8839635.
I worked around it by putting a segmented control underneath the search bar, imitating the scope bar.
Here's a workaround that uses the scope buttons. The main thing is to add an extra character for the scope(s) that you want to show search results for automatically, but ensure that you remove it for the scope(s) that you do not want to do this.
You will need to implement searchBar:textDidChange as well as searchBar:selectedScopeButtonIndexDidChange:
// scope All doesn't do a search until you type something in, so don't show the search table view
// scope Faves and Recent will do a search by default
#define kSearchScopeAll 0
#define kSearchScopeFaves 1
#define kSearchScopeRecent 2
// this gets fired both from user interaction and from programmatically changing the text
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
[self initiateSearch];
}
- (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope{
NSString *searchText = self.searchDisplayController.searchBar.text;
// if we got here by selecting scope all after one of the others with no user input, there will be a space in the search text
NSString *strippedText = [searchText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ((selectedScope == kSearchScopeAll) && (strippedText.length == 0) && (searchText.length != 0)){
self.searchDisplayController.searchBar.text = #"";
} else {
[self initiateSearch];
}
}
-(void)initiateSearch{
NSString *searchText = self.searchDisplayController.searchBar.text;
NSInteger scope = self.searchDisplayController.searchBar.selectedScopeButtonIndex;
if ((searchText.length == 0) && (scope != kSearchScopeAll)){
self.searchDisplayController.searchBar.text = #" ";
}
switch (scope) {
case kSearchScopeAll:
[self searchAll:searchText];
break;
case kSearchScopeFaves:
[self searchFavorites:searchText];
break;
case kSearchScopeRecent:
[self searchRecents:searchText];
break;
default:
break;
}
}
// assume these trim whitespace from the search term
-(void)searchAll:(NSString *)searchText{
}
-(void)searchFavorites:(NSString *)searchText{
}
-(void)searchRecents:(NSString *)searchText{
}