I want to show the searchResultsTableView when my searchBar is selected. I have tried this:
- (void) searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
[self.searchDisplayController.searchResultsTableView reloadData];
[self.searchDisplayController setActive:YES animated:YES];
}
and this:
- (void) searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
[self.searchDisplayController setActive:YES animated:YES];
}
Neither works. In my numberOfRowsInSection method, I do have rows in the array, but the searchResultsTableView does not show until I start typing. Any thoughts? Thanks!
Try to set your searchdisplaycontroller to active in the following UISearchBarDelegate method
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar;
Related
I have a tableview for showing a list of devices in my application. When viewWillAppear is called, I add the self.searchDisplayController.searchBar as a subview to a headerView. I then assign self.tableView.tableHeaderView = headerView. It looks like this:
I scroll the tableview down so that headerview goes out of view and then go to some other view controller by tapping on a cell. When I come back to this tableView, scroll up to the headerView, the searchBar becomes invisible, however on tapping the invisible area the searchDisplayController gets activated and the cancel button doesn't work. This happens for iOS 7 only. Why is this happening?
Note: It happens only if the headerView is out of the view when I come back to the tableViewController.
I've just had the same issue. When I go to debug into the delegate method of UISearchDisplayController at the end search state, the searchBar becomes a subview of an UIView, not the UITableView. Please see below code:
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
//My Solution: remove the searchBar away from current super view,
//then add it as subview again to the tableView
UISearchBar *searchBar = controller.searchBar;
UIView *superView = searchBar.superview;
if (![superView isKindOfClass:[UITableView class]]) {
NSLog(#"Error here");
[searchBar removeFromSuperview];
[self.tableView addSubview:searchBar];
}
NSLog(#"%#", NSStringFromClass([superView class]));
}
My solution is remove the searchBar away from current super view, then add it as subview again to the tableView. I've already tested successfully.
Hope that help!
Regards
I have the exact same problem. the search bar is still there and can receive touch events. it is however not rendered. I believe the problem is in UISearchDisplaycontroller because it renders fine if I don't use UISearchDisplayController. I ended up writing a custom SearchDisplaycontroller to replace it. it is very basic and only does what I need.
use it is the same way as you would the normal UISearchDisplayController but self.searchDisplayController will not return anything. you will have to use another pointer to refer to the custom search display controller.
looks like a big ugly work around, but the only one that worked for me. keen to hear of alternatives.
#protocol SearchDisplayDelegate;
#interface SearchDisplayController : NSObject<UISearchBarDelegate>
- (id)initWithSearchBar:(UISearchBar *)searchBar contentsController:(UIViewController *)viewController;
#property(nonatomic,assign) id<SearchDisplayDelegate> delegate;
#property(nonatomic,getter=isActive) BOOL active; // configure the view controller for searching. default is NO. animated is NO
- (void)setActive:(BOOL)visible animated:(BOOL)animated; // animate the view controller for searching
#property(nonatomic,readonly) UISearchBar *searchBar;
#property(nonatomic,readonly) UIViewController *searchContentsController; // the view we are searching (often a UITableViewController)
#property(nonatomic,readonly) UITableView *searchResultsTableView; // will return non-nil. create if requested
#property(nonatomic,assign) id<UITableViewDataSource> searchResultsDataSource; // default is nil. delegate can provide
#property(nonatomic,assign) id<UITableViewDelegate> searchResultsDelegate;
#end
#protocol SearchDisplayDelegate <NSObject>
// implement the protocols you need
#optional
#end
the implementation
#implementation SearchDisplayController {
UISearchBar *_searchBar;
UIViewController *_viewController;
UITableView *_searchResultsTableView;
UIView *_overLay;
}
- (void)dealloc {
[_searchBar release];
[_searchResultsTableView release];
[_overLay release];
[super dealloc];
}
- (UIViewController *)searchContentsController {
return _viewController;
}
- (UITableView *)searchResultsTableView {
return _searchResultsTableView;
}
- (id)initWithSearchBar:(UISearchBar *)searchBar contentsController:(UIViewController *)viewController {
self = [super init];
if (self) {
_searchBar = [searchBar retain];
_searchBar.delegate = self;
_viewController = viewController;
_searchResultsTableView = [[UITableView alloc]initWithFrame:CGRectMake(0, CGRectGetMaxY(_searchBar.frame), _viewController.view.frame.size.width, _viewController.view.frame.size.height - CGRectGetMaxY(_searchBar.frame))];
_overLay = [[UIView alloc]initWithFrame:_searchResultsTableView.frame];
_overLay.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(overLayTapped)];
[_overLay addGestureRecognizer:tap];
[tap release];
}
return self;
}
- (void)setSearchResultsDataSource:(id<UITableViewDataSource>)searchResultsDataSource {
_searchResultsTableView.dataSource = searchResultsDataSource;
}
- (void)setSearchResultsDelegate:(id<UITableViewDelegate>)searchResultsDelegate {
_searchResultsTableView.delegate = searchResultsDelegate;
}
- (void)overLayTapped {
[self setActive:NO animated:YES];
[_searchBar resignFirstResponder];
_searchBar.text = nil;
_searchBar.showsCancelButton = NO;
}
- (void)setActive:(BOOL)visible animated:(BOOL)animated {
UIView *viewToAdd = nil;
if (!_searchBar.text.length) {
viewToAdd = _overLay;
} else {
viewToAdd = _searchResultsTableView;
}
float a = 0;
if (visible) {
[_viewController.view addSubview:viewToAdd];
a = 1.0;
}
if ([_viewController.view respondsToSelector:#selectore(scrollEnabled)]) {
((UIScrollView *)_viewController.view).scrollEnabled = !visible;
}
if (animated) {
[UIView animateWithDuration:0.2 animations:^{
_overLay.alpha = a;
_searchResultsTableView.alpha = a;
}];
} else {
_overLay.alpha = a;
_searchResultsTableView.alpha = a;
}
}
- (void)setActive:(BOOL)active {
[self setActive:active animated:YES];
}
#pragma mark - UISearchBar delegate protocols
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
[self setActive:YES animated:YES];
searchBar.showsCancelButton = YES;
[_searchResultsTableView reloadData];
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[_searchResultsTableView reloadData];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[self overLayTapped];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (searchText.length) {
[_overLay removeFromSuperview];
[_viewController.view addSubview:_searchResultsTableView];
} else {
[_searchResultsTableView removeFromSuperview];
[_viewController.view addSubview:_overLay];
}
[_searchResultsTableView reloadData];
}
#end
Update: on how to use this progammatically
declare an ivar
SearchDisplayController *mySearchDisplayController;
initialize it programmatically
mySearchDisplayController = [[SearchDisplayController alloc]initWithSearchBar:mySearchBar contentsController:self];
adding the searchbar to your tableview
self.tableView.headerView = mySearchBar;
use mySearchDisplayController as reference to the custon class instead on self.searchDisplayController.
In my case, the table view that held the search display controller's search bar in its header view was being reloaded almost as soon as the view appeared. It was at this point that the search bar would cease to render. When I scrolled the table, it would reappear. It's also worth mentioning that my table contained a UIRefreshControl and was not a UITableViewController subclass.
My fix involved setting the search display controller active and then inactive very quickly just before after loading the table (and ending the refresh control refreshing):
[self.tableView reloadData];
[self.refreshControl endRefreshing];
[self.searchDisplayController setActive:YES animated:NO];
[self.searchDisplayController setActive:NO];
A bit of a hack but it works for me.
Using the debugger, I've found that the UISearchBar is initially a child view of the tableHeaderView - but when it disappears, it has become a child of the tableView itself. This has probably been done by UISearchDisplayController somehow... So I did the following hack to simply return the UISearchBar to the header view:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(!self.searchDisplayController.isActive && self.searchBar.superview != self.tableView.tableHeaderView) {
[self.tableView.tableHeaderView addSubview:self.searchBar];
}
}
Seems to work fine on iOS 7 as well as 6 :)
(checking that the searchDisplayController isn't active is necessary, otherwise the sarch bar disappears during search)
I endorse Phien Tram's answer. Please upvote it. I don't have enough points myself.
I had a similar problem where a search bar loaded from storyboard would disappear when I repeatedly tapped it, invoking and dismissing search. His solution repairs the problem.
There seems to be a bug where repeated invocation and dismissal of the search display controller doesn't always give the search bar back to the table view.
I will say I'm uncomfortable with the solution's dependence on the existing view hierarchy. Apple seems to reshuffle it with every major release. This code may break with iOS 8.
I think a permanent solution will require a fix by Apple.
I had the same issue and I could fix it calling next line after creating the UISearchDisplayController
[self performSelector:#selector(setSearchDisplayController:) withObject:displayController];
My viewDidLoad function look like this:
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 44)];
searchBar.placeholder = NSLocalizedString(#"Search", #"Search");
UISearchDisplayController *displayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
displayController.delegate = self;
displayController.searchResultsDataSource = self;
displayController.searchResultsDelegate = self;
[self performSelector:#selector(setSearchDisplayController:) withObject:displayController];
self.tableView.contentOffset = CGPointMake(0, 44);
self.tableView.tableHeaderView = searchBar;
Thanks to this answer: https://stackoverflow.com/a/17324921/1070393
Use UISearchBar above UITableView,Then make IBOutlet for and connect them with file's owner to UISearchbar
Example- .h file
#import <UIKit/UIKit.h>
#interface LocationViewController : UIViewController<UISearchBarDelegate>
{
BOOL IsSearchOn;
}
#property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
#property (strong, nonatomic) IBOutlet UITableView *TBVLocation;
.m file
#pragma mark -
#pragma mark UISearchBar Delegate Methods
-(void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[self.searchResult removeAllObjects];
if(searchText.length == 0)
{
IsSearchOn=NO;
// [filteredTableData removeAllObjects];
[self.searchBar resignFirstResponder];
// [self .tblView reloadData];
}
else
{
IsSearchOn=YES;
if(searchText != nil && ![searchText isEqualToString:#""])
{
/* NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#", searchText];
self.searchResult = [NSMutableArray arrayWithArray: [searchArray filteredArrayUsingPredicate:resultPredicate]];*/
for(int i=0;i<[[arrCountryList valueForKey:#"country_name"] count];i++)
{
NSRange titleRange = [[[[arrCountryList valueForKey:#"country_name"] objectAtIndex:i] lowercaseString] rangeOfString:[searchText lowercaseString]];
if(titleRange.location != NSNotFound)
[self.searchResult addObject:[arrCountryList objectAtIndex:i]];
}
[TBVLocation reloadData];
}
}
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[searchBar resignFirstResponder];
}
-(void)searchBarCancelButtonClicked:(UISearchBar *) searchBar
{
[searchBar resignFirstResponder];
IsSearchOn=NO;
searchBar.text = nil;
[TBVLocation reloadData];
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
{
[searchBar resignFirstResponder];
return YES;
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
searchBar.showsCancelButton = YES;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
// IsSearchOn=YES;
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
IsSearchOn=NO;
searchBar.showsCancelButton = NO;
[TBVLocation reloadData];
[searchBar resignFirstResponder];
}
It will work like charm.
I've faced similar problem and after some digging, I've found that this is a bug in UISearchBar hierarchy. This hacky solution worked for me in iOS 7, but be aware that this may break in future iOS versions:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIView *buggyView = [self.searchDisplayController.searchBar.subviews firstObject];
// buggyView bounds and center are incorrect after returning from controller, so adjust them.
buggyView.bounds = self.searchDisplayController.searchBar.bounds;
buggyView.center = CGPointMake(CGRectGetWidth(buggyView.bounds)/2, CGRectGetHeight(buggyView.bounds)/2);
}
I had the same problem and tested some of the solutions proposed here in this thread, but they didn't solve the problem for me.
Previously, I added and configured the UISearchBar in the
- (void)viewDidLoad
method of my ViewController in code.
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:searchBarView.frame];
searchBar.delegate = self;
searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
ect...
What solved this issue for me was that I added a UISearchbar in the InterfaceBuilder, created an outlet in my ViewController and added this UISearchBar to my UISearchDisplayController.
self.searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar(<--outlet) contentsController:self];
hope this might also help some people
This is really simple, though still driving my nuts. I have a uitableview where I am trying to animate transition in and out of editing mode. This is what I took from an example that I have seen. It does do the job, but without the animation.
Any thoughts?
- (IBAction) EditTable:(id)sender
{
if(self.editing)
{
[super setEditing:NO animated:YES];
[tblSimpleTable setEditing:NO animated:YES];
[tblSimpleTable reloadData];
[self.navigationItem.leftBarButtonItem setTitle:#"Edit"];
[self.navigationItem.leftBarButtonItem setStyle:UIBarButtonItemStylePlain];
}
else
{
[super setEditing:YES animated:YES];
[tblSimpleTable setEditing:YES animated:YES];
[tblSimpleTable reloadData];
[self.navigationItem.leftBarButtonItem setTitle:#"Done"];
[self.navigationItem.leftBarButtonItem setStyle:UIBarButtonItemStyleDone];
}
}
PS: I am also not sure why I need this line: [super setEditing:NO animated:YES]; but things just dont seem to work at all without it. I just saw a few examples online that dont do that.
Thanks!
Maybe you should not reloadData when set editing property.
BTW, What's your "super" class? Normally you don't have to invoke [super setEditing:YES animated:YES];
Is it only the button that isn't animating properly? Either way you should probably be using super.editButtonItem instead of your own; it's animated and just setting the text and style like that (I believe) isn't. As far as calling the super, are you overriding one of the editing methods and not calling the super method from within there? And xuzhes's answer about the reloadData is, I believe, correct as well.
Try this:
#Implementation YourViewController // This can (should) be a subclass of UITableViewController to make your life easier
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem; // Automatically calls setEditing:animated: and changes itself to "Edit"/"Done" between presses
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
if (editing == YES) {
// Do stuff here
} else {
// Do stuff here
}
// Reload all sections of the table view
// THIS IS THE PART YOU'RE INTERESTED IN
NSRange range = NSMakeRange(0,[self.tableView numberOfSections]);
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
[self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];
}
Check out the documentation for comments on the methods from Apple :)
My App is having a search bar for searching records from the table view,which is populated by sqlite DB.
My problem is that when the view opens the "cancel" button is not enabled and also I cant touch on that, just like a image only.It is there but no action is with that.
when we click on that search bar text the cancel button will be changed to "done" it is enabled one.
so here is my code
this is my search bar view,see that cancel button.It is not enabled
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
//[newSearchBar setShowsCancelButton:YES animated:YES];
newSearchBar.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters;
NSLog(#"search begin edit") ;
//searchString = searchBar.text;
//NSLog(#"print did edit searchstring : %#", searchString) ;
for(UIView *view in [searchBar subviews])
{
//shareItemId =newSearchBar.text;
if([view isKindOfClass:[NSClassFromString(#"UINavigationButton") class]]) {
[(UIBarItem *)view setTitle:#"Done"];
}
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
NSLog(#"searchBarTextDidEndEditing:");
[searchBar resignFirstResponder];
//[self dismissModalViewControllerAnimated:YES];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
NSLog(#"searchBarSearchButtonClicked");
searchString = searchBar.text;
NSLog(#"search %#", searchBar.text);
[newSearchBar setShowsCancelButton:NO animated:YES];
[searchBar resignFirstResponder];
//[self dismissModalViewControllerAnimated:YES];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
NSLog(#" searchBarCancelButtonClicked");
[searchBar resignFirstResponder];
shareItemName =newSearchBar.text;
[self dismissModalViewControllerAnimated:YES];
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
NSLog(#"searchBarShouldBeginEditing");
[newSearchBar setShowsCancelButton:YES animated:YES];
return YES;
}
These are my delegates for that
Please check my code and give me the answer. I need to enable the "Cancel" button when the view is loaded and it action will be go back to previous view
I need like this
Or else how can I add a another cancel button on exciting cancel button.so that I can enable that.please give me all the details
You need to set the UISearchDisplayController to be ACTIVE, like this:
[mySearchDisplayController setActive:YES animated:YES];
or more simply:
mySearchDisplayController.active = YES;
My guess is that Apple made the UISearchBar in a way that the cancel button is disabled if the search text field is empty or not first responder.
This is make sense because you should not use the "Cancel" button to other purpose than actually canceling the search. and since there is no search to cancel - the button is disabled.
If you still want that the button will be active immediately when the view is presented, you can call at viewWillAppear: to [mySearchBar becomeFirstResponder];
This will cause to the keyboard to appear and the button will be enabled.
And then if the user hit cancel you can intercept it to go back to the previous view. (I'm not sure if apple will like this behavior).
Sample code:
-(void) viewWillAppear : (BOOL) animated
{
[super viewWillAppear:animated];
// Make keyboard pop and enable the "Cancel" button.
[self.mySearchBar becomeFirstResponder];
}
Here's what I did to always enable the cancel button, even when the search field is not first responder.
I'm calling this method whenever I call resignFirstResponder on the search field
- (void)enableCancelButton {
for (UIView *view in self.searchBar.subviews) {
if ([view isKindOfClass:[UIButton class]]) {
[(UIButton *)view setEnabled:YES];
}
}
}
This works, but I'm not sure whether it will pass App Store verification yet, so use it at your own risk. Also, this probably only works if the cancel button is the only button you are using with the search field.
This works to reenable the cancel button as of iOS 8:
private func enableButtonsInSubviews(view: UIView) {
if let view = view as? UIButton {
view.enabled = true
}
for subview in view.subviews {
enableButtonsInSubviews(subview)
}
}
I have implemented a UITableView with search bar (and search display) - all works fine, but the table results do not get updated until the search bar cancel button is tapped.
Delegate methods:
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// asynchronous request with [self.tableView reloadData] in the connectionDidFinishLoading
[self getProductData:searchBar.text];
[searchBar resignFirstResponder];
[self.tableView reloadData];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar {
return YES;
}
Do I need to call a delegate method after receiving the data from the server? Or should I make the request synchronous?
Thanks
edit: I tried with a synchronous request and it still does not work!
Resolved this issue by adding this code to the end of my getProductData method:
[[[self searchDisplayController] searchResultsTableView] performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
I think you have to implement:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
and return YES
See the TableSearch sample code
Please try something like this:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
[self getProductData:searchText];
[self.tableView reloadData];
}
I'm implementing a search bar on my table, which should be pretty straight forward. I've got these:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar {
NSLog(#"searchBarTextDidBeginEditing");
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
NSLog(#"The search text is: %#", searchText);
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)theSearchBar {
NSLog(#"searchBarTextDidEndEditing");
[theSearchBar resignFirstResponder];
}
And searchBarTextDidBeginEditing fires, and I get that message in my log, but when I tap outside the search bar, above the keyboard, I don't get the searchBarTextDidEndEditing event so I can't make the keyboard disappear – the message doesn't even appear in the log.
The textDidChange is working, so it's just searchBarTextDidBeginEditing that isn't.
Any ideas? Thanks!!
Even i faced the same problem.
Please find with the solution below
Implement Below methods
1.searchBarTextDidEndEditing
2.searchBarSearchButtonClicked
and make sure you [UISearchchbar resignfirstresponder] in the second method mentioned above
Once I implemented searchBarSearchButtonClicked that solved it for me.
The method
(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
is only called when [searchBar resignFirstResponder] is called.
The best place to call [searchBar resignFirstResponder] is in the method
(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
In swift:
(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar;
(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar;