2010-12-03 22 views
36

J'ai implémenté un UISearchBar dans une vue de table et presque tout fonctionne sauf une petite chose: Quand je saisis du texte et que j'appuie sur le bouton de recherche du clavier, le clavier disparaît, la recherche Les résultats sont les seuls éléments affichés dans la table, le texte reste dans l'UISearchBar, mais le bouton Annuler est désactivé.UISearchBar désactiver la désactivation automatique du bouton d'annulation

J'ai essayé d'obtenir ma liste au plus près de la fonctionnalité de l'application de contacts Apple et lorsque vous appuyez sur Rechercher dans cette application, elle ne désactive pas le bouton d'annulation.

Lorsque j'ai regardé dans le fichier d'en-tête UISearchBar, j'ai remarqué un drapeau pour autoDisableCancelButton sous la structure _searchBarFlags mais il est privé.

Y a-t-il quelque chose qui me manque lorsque je configure UISearchBar?

Répondre

51

J'ai trouvé une solution. Vous pouvez utiliser cette boucle pour faire une boucle sur les sous-vues de la barre de recherche et l'activer lorsque vous appuyez sur le bouton de recherche sur le clavier.

for (UIView *possibleButton in searchBar.subviews) 
{ 
    if ([possibleButton isKindOfClass:[UIButton class]]) 
    { 
     UIButton *cancelButton = (UIButton*)possibleButton; 
     cancelButton.enabled = YES; 
     break; 
    } 
} 
+2

Cela ne fonctionne pas pour moi sur iOS 6, car le seul bouton présent est un UINavigationButton qui semble être une classe privée. –

+1

Avez-vous essayé de définir la propriété showsCancelButton sur UISearchBar? – rplankenhorn

+2

UINavigationButton est une sous-classe si UIButton, donc ce code fonctionne, et il n'y a pas d'utilisation de classe privée. https://github.com/kennytm/iphone-private-frameworks/blob/master/UIKit/UINavigationButton.h – Mitya

3

C'est ce qui fait de travailler sur iOS 6 pour moi:

searchBar.showsCancelButton = YES; 
searchBar.showsScopeBar = YES; 
[searchBar sizeToFit]; 
[searchBar setShowsCancelButton:YES animated:YES]; 
+2

cela ne résout pas le problème: l'opérateur souhaite désactiver la fonction de désactivation automatique, c'est-à-dire ne permet pas à UISearchBar de le désactiver. – dwery

15

je devais modifier ce un peu pour le faire fonctionner pour moi dans iOS 7

- (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; 
      } 
     } 
    } 
} 
+2

Vous devriez vraiment juste sortir de l'instruction interne si au lieu de casser. Cette déclaration de rupture va seulement vous sortir de la boucle interne et continuera à parcourir les searchBar.subviews même après avoir trouvé le bouton d'annulation. – rplankenhorn

+0

Bon point J'ai mis à jour ma réponse. –

3

Voici ma solution, qui fonctionne pour toutes les situations dans toutes les versions d'iOS. IE, d'autres solutions ne traitent pas lorsque le clavier est fermé parce que l'utilisateur a fait glisser une vue de défilement.

- (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's 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]; 
+0

Bon code, merci! –

2

Aucune des réponses n'a fonctionné pour moi. Je cible iOS 7. Mais j'ai trouvé une réponse. Ce que j'essaie est quelque chose comme l'application Twitter sur iOS. Si vous cliquez sur la loupe dans l'onglet Chronologie, le UISearchBar apparaît avec le bouton Annuler activé, le clavier affiché et l'écran des recherches récentes. Faites défiler l'écran des recherches récentes et il cache le clavier, mais il maintient le bouton Annuler activé.

Ceci est mon code de travail:

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

Je suis arrivé à cette solution en mettant un point d'arrêt à mon avis de la table de scrollViewWillBeginDragging:. J'ai regardé dans mon UISearchBar et a dévoilé ses sous-vues. Il en a toujours un, qui est de type UIView (ma variable searchBarSubview).

enter image description here

Alors que UIView détient un NSArray appelé subviewCache et je remarquai que le dernier élément, qui est le troisième, est de type UINavigationButton, pas dans l'API publique. J'ai donc décidé d'utiliser plutôt le codage à valeur-clé. J'ai vérifié si le UINavigationButton répond à setEnabled:, et heureusement, il le fait. J'ai donc défini la propriété sur @YES. Il s'avère que ce UINavigationButtonest le bouton Annuler.

Ceci est lié à la rupture si Apple décide de changer la mise en œuvre de l'intérieur d'un UISearchBar, mais que diable. Cela fonctionne pour le moment.

2

Comme par my answer here, placez dans votre délégué SearchBar:

- (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); 
    }); 
} 
1

Pour MonoTouch ou Xamarin iOS J'ai la solution C# suivant travail pour iOS 7 et 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; 
      } 
     } 
    } 
} 

Cette réponse est basé sur la solution David Douglas.

5

Il y a deux façons d'y parvenir facilement

- (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; 
    } 
} 

Vous devriez aller avec le second, il ne plantera pas votre demande si Apple va changer en arrière-plan. BTW je l'ai testé de iOS 4.0 à 8.2 et aucun changement, aussi je l'utilise dans mon application approuvée par le magasin sans aucun problème.

+0

Cela fonctionne toujours sur iOS9. –

+1

Cela fonctionne mais votre application peut être rejetée car elle utilise une méthode privée. Il faut se méfier. –

+0

@ PedroJoséCardoso non, cela ne compte pas vraiment comme une utilisation privée de l'API, et je n'ai jamais vu de rejets à cause de cela. – Laszlo

1

Une réponse plus complète:

  • depuis iOS 7, il y a un niveau supplémentaire de subviews sous la barre de recherche
  • un bon endroit pour activer le bouton d'annulation est 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

est ici une Swift 3 solution qui utilise des extensions pour obtenir le bouton d'annulation facilement:

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

Maintenant, pour l'utilisation:

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

La solution la plus propre que j'ai vue. Merci. – gbhall