UISearchBar disable auto disable button cancel

I implemented UISearchBar as a table, and almost everything works, except for one small thing: when I enter the text and then press the search button on the keyboard, the keyboard leaves, the search results are the only elements shown in the table, the text remains in the UISearchBar, but the cancel button is disabled.

I try to keep my list close to the functionality of the Apple Contacts application, and when you click search in this application, it does not disable the cancel button.

When I looked in the UISearchBar header file, I noticed a flag for autoDisableCancelButton in the _searchBarFlags structure, but it is closed.

Is there something I am missing when setting up a UISearchBar?

+36
ios4 uisearchbar cancel-button
Dec 03 '10 at 17:51
source share
10 answers

I have found a solution. You can use this for loop to loop over the subzones of the search bar and enable it when the search button is pressed on the keyboard.

for (UIView *possibleButton in searchBar.subviews) { if ([possibleButton isKindOfClass:[UIButton class]]) { UIButton *cancelButton = (UIButton*)possibleButton; cancelButton.enabled = YES; break; } } 
+51
Dec 03 '10 at 19:10
source share

I had to adjust this a bit to make it work for me in iOS7

 - (void)enableCancelButton:(UISearchBar *)searchBar { for (UIView *view in searchBar.subviews) { for (id subview in view.subviews) { if ( [subview isKindOfClass:[UIButton class]] ) { [subview setEnabled:YES]; NSLog(@"enableCancelButton"); return; } } } } 
+15
Aug 09 '13 at 16:03
source share

There are two ways to achieve this easily.

 - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{ // The small and dirty [(UIButton*)[searchBar valueForKey:@"_cancelButton"] setEnabled:YES]; // The long and safe UIButton *cancelButton = [searchBar valueForKey:@"_cancelButton"]; if ([cancelButton respondsToSelector:@selector(setEnabled:)]) { cancelButton.enabled = YES; } } 

You have to go with the second one, this will not crash your application if Apple changes it in the background.

BTW I tested it from iOS 4.0 to 8.2 and no changes, also I use it in an app approved in the Store, without any problems.

+4
Jan 12 '15 at 22:25
source share

That is what made me work on iOS 6 for me:

 searchBar.showsCancelButton = YES; searchBar.showsScopeBar = YES; [searchBar sizeToFit]; [searchBar setShowsCancelButton:YES animated:YES]; 
+3
Nov 01 '12 at 2:11
source share

Here is my solution that works for all situations in all versions of iOS.

IE, other solutions are not processed when the keyboard is rejected because the user has drag and drop the scroll.

 - (void)enableCancelButton:(UIView *)view { if ([view isKindOfClass:[UIButton class]]) { [(UIButton *)view setEnabled:YES]; } else { for (UIView *subview in view.subviews) { [self enableCancelButton:subview]; } } } // This will handle whenever the text field is resigned non-programatically // (IE, because it set to resign when the scroll view is dragged in your storyboard.) - (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar { [self performSelector:@selector(enableCancelButton:) withObject:searchBar afterDelay:0.001]; } // Also follow up every [searchBar resignFirstResponder]; // with [self enableCancelButton:searchBar]; 
+3
Jan 13 '14 at 5:39
source share

None of the answers worked for me at all. I am targeting iOS 7. But I found the answer.

What I'm trying is similar to the Twitter iOS app. If you click on the magnifying glass on the Timelines tab, a UISearchBar appears with the Cancel button activated, a keyboard and the last search screen. Scroll through the last search screen and it hides the keyboard, but it holds the Cancel button.

This is my working code:

 UIView *searchBarSubview = self.searchBar.subviews[0]; NSArray *subviewCache = [searchBarSubview valueForKeyPath:@"subviewCache"]; if ([subviewCache[2] respondsToSelector:@selector(setEnabled:)]) { [subviewCache[2] setValue:@YES forKeyPath:@"enabled"]; } 

I came to this solution by setting a breakpoint in my scrollViewWillBeginDragging: table scrollViewWillBeginDragging: . I looked into my UISearchBar and exposed its subtitles. It always has only one that is of type UIView (my searchBarSubview variable).

enter image description here

Then, that UIView contains an NSArray called subviewCache , and I noticed that the last third element is of type UINavigationButton , and not a public API. Therefore, I decided to use the encoding with the key. I checked if the UINavigationButton responds to setEnabled: and, fortunately, it does. So I set the @YES property. It turns out that UINavigationButton is the Cancel button.

It will definitely break if Apple decides to change the implementation of the UISearchBar internally, but what the hell. He is working now.

+2
Feb 25 '14 at 17:20
source share

According to my answer here , put this in your searchBar delegate:

 - (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar { dispatch_async(dispatch_get_main_queue(), ^{ __block __weak void (^weakEnsureCancelButtonRemainsEnabled)(UIView *); void (^ensureCancelButtonRemainsEnabled)(UIView *); weakEnsureCancelButtonRemainsEnabled = ensureCancelButtonRemainsEnabled = ^(UIView *view) { for (UIView *subview in view.subviews) { if ([subview isKindOfClass:[UIControl class]]) { [(UIControl *)subview setEnabled:YES]; } weakEnsureCancelButtonRemainsEnabled(subview); } }; ensureCancelButtonRemainsEnabled(searchBar); }); } 
+2
Sep 05 '14 at 12:53 on
source share

For Monotouch or Xamarin iOS, I have the following C # solution working for iOS 7 and iOS 8:

 foreach(UIView view in searchBar.Subviews) { foreach(var subview in view.Subviews) { //Console.WriteLine(subview.GetType()); if(subview.GetType() == typeof(UIButton)) { if(subview.RespondsToSelector(new Selector("setEnabled:"))) { UIButton cancelButton = (UIButton)subview; cancelButton.Enabled = true; Console.WriteLine("enabledCancelButton"); return; } } } } 

This answer is based on a decision by David Douglas .

+1
Jan 9 '15 at 2:31 on
source share

More complete answer:

  • since iOS 7, there is an additional level of subtasks in the search bar
  • A good place to enable the cancel button is in searchBarTextDidEndEditing

.

 extension MyController: UISearchBarDelegate { public func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { DispatchQueue.main.async { // you need that since the disabling will // happen after searchBarTextDidEndEditing is called searchBar.subviews.forEach({ view in view.subviews.forEach({ subview in // ios 7+ if let cancelButton = subview as? UIButton { cancelButton.isEnabled = true cancelButton.isUserInteractionEnabled = true return } }) // ios 7- if let cancelButton = subview as? UIButton { cancelButton.isEnabled = true cancelButton.isUserInteractionEnabled = true return } }) } } } 
+1
Dec 04 '16 at 4:05
source share

Here's a quick fix that uses extensions to easily get the cancel button:

 extension UISearchBar { var cancelButton: UIButton? { for subView1 in subviews { for subView2 in subView1.subviews { if let cancelButton = subView2 as? UIButton { return cancelButton } } } return nil } } 

Now for use:

 class MyTableViewController : UITableViewController, UISearchBarDelegate { var searchBar = UISearchBar() func viewDidLoad() { super.viewDidLoad() searchBar.delegate = self tableView.tableHeaderView = searchBar } func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { DispatchQueue.main.async { if let cancelButton = searchBar.cancelButton { cancelButton.isEnabled = true cancelButton.isUserInteractionEnabled = true } } } } 
+1
Apr 7 '17 at 23:20
source share



All Articles