web-dev-qa-db-fra.com

UISearchController: afficher les résultats même lorsque la barre de recherche est vide

Si j'ai bien compris, le comportement par défaut de UISearchController est le suivant:

  1. En appuyant sur la barre de recherche, l’arrière-plan est estompé et le bouton «annuler» est affiché. SearchResultsController n'est pas montré jusqu'à ce point.
  2. SearchResultsController est affiché uniquement si la barre de recherche n'est pas vide.

Je souhaite afficher SearchResultsController même lorsque la barre de recherche est vide mais sélectionnée (c'est-à-dire le cas 1 ci-dessus).

En termes simples, au lieu d'atténuer l'arrière-plan, j'aimerais afficher les résultats de la recherche.

Y a-t-il un moyen de faire cela?

Plus de clarification:

Je n'utilise pas UISearchController pour filtrer les résultats affichés dans la vue sur laquelle elle est affichée, mais quelques autres résultats sans rapport. Ce sera comme ce que fait Facebook sur son "Fil d'actualité". En appuyant sur la barre de recherche, les suggestions de recherche s'affichent initialement, puis, lorsque nous commençons l'édition, les résultats de la recherche ne concernant pas nécessairement les flux d'actualités. 

30
optimus

Si votre barre de recherche est active mais ne contient pas de texte, les résultats de la tableView sous-jacente sont affichés. C'est le comportement intégré et la raison pour laquelle searchResultsController est masqué pour cet état.

Pour modifier le comportement lorsque la recherche est active mais ne filtre pas, vous devrez afficher le searchResultsController quand il est normalement toujours masqué.

Il peut y avoir un bon moyen d’y parvenir via <UISearchResultsUpdating> et updateSearchResultsForSearchController:. Si vous pouvez le résoudre via le protocole, c'est la voie à suivre.

Si cela ne vous aide pas, vous vous retrouvez avec le piratage du comportement intégré. Je ne le recommanderais pas et je n’aurais pas confiance en lui, et ça va être fragile, mais voici une réponse si vous choisissez cette option:

  1. Assurez-vous que votre tableViewController est conforme à <UISearchControllerDelegate> et ajoutez

    self.searchController.delegate = self;

  2. Implémenter willPresentSearchController:

    - (void)willPresentSearchController:(UISearchController *)searchController
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            searchController.searchResultsController.view.hidden = NO;
        });
    }
    

    Ceci rend la searchResultsController visible après que sa UISearchController soit définie sur masqué.

  3. Implémenter didPresentSearchController:

    - (void)didPresentSearchController:(UISearchController *)searchController
    {
        searchController.searchResultsController.view.hidden = NO;
    }
    

Pour une meilleure façon de contourner le comportement intégré, voir la réponse de malhal .

30
user4151918

Vous pouvez simplement implémenter le protocole UISearchResultsUpdating et définir la vue du contrôleur de résultats pour qu'elle s'affiche toujours dans updateSearchResultsForSearchController:

 func updateSearchResultsForSearchController(searchController: UISearchController) {

   // Always show the search result controller
   searchController.searchResultsController?.view.hidden = false

   // Update your search results data and reload data
   ..
}

Cela fonctionne car la méthode est appelée même lorsque la barre de recherche est activée, sans aucun texte.

31
Ken Toh

J'ai essayé la solution PetahChristian, le résultat de précharge est apparu lorsque nous avons ciblé la barre de recherche pour la première fois, mais lorsque nous saisissons quelque chose, puis l'effacez, les résultats de précharge ne réapparaissent pas.

Je suis venu avec une autre solution. Nous avons seulement besoin d'ajouter un délégué dans SearchResultsController et de l'appeler lorsque notre searchController.searchBar.text est vide. Quelque chose comme ça:

SearchResultsController:

protocol SearchResultsViewControllerDelegate {
   func reassureShowingList() -> Void
}

class FullSearchResultsViewController: UIViewController, UISearchResultsUpdating{
   var delegate: SearchResultsViewControllerDelegate?
   ...
   func updateSearchResultsForSearchController(searchController: UISearchController) {
    let query = searchController.searchBar.text?.trim()
    if query == nil || query!.isEmpty {
        ...
        self.delegate?.reassureShowingList()
        ...
    }
    ...
}

Et dans le contrôleur contient le SearchController, nous ajoutons notre délégué:

self.searchResultsController.delegate = self
func reassureShowingList() {
    searchController.searchResultsController!.view.hidden = false
}
13
mashix

Avec des choses difficiles comme celle-ci, je recommande l'approche du marteau-luge! C’est pour détecter quand quelque chose essaie de le cacher et quand il le fait, le remettre en place. Cela peut être fait via KVO (Key Value Observing). Cela fonctionnera dans tous les cas, sans avoir à gérer toutes les subtilités de la barre de recherche. Désolé, le code est compliqué, mais KVO est une API de style ancien, mais mon code suit les recommandations. Dans votre SearchResultsViewController, mettez ceci:

static int kHidden;

@implementation SearchResultsViewController

-(void)viewDidLoad{
    [super viewDidLoad];
    [self.view addObserver:self
                   forKeyPath:@"hidden"
                      options:(NSKeyValueObservingOptionNew |
                               NSKeyValueObservingOptionOld)
                      context:&kHidden];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    // if it was our observation
    if(context == &kHidden){
        // if the view is hidden then make it visible.
        if([[change objectForKey:NSKeyValueChangeNewKey] boolValue]){
            self.view.hidden = NO;
        }
    }
    else{
        // if necessary, pass the method up the subclass hierarchy.
        if([super respondsToSelector:@selector(observeValueForKeyPath:ofObject:change:context:)]){
            [super observeValueForKeyPath:keyPath
                                 ofObject:object
                                   change:change
                                  context:context];
        }
    }
}

- (void)dealloc
{
    [self.view removeObserver:self forKeyPath:@"hidden"];
}

// Here have the rest of your code for the search results table.

@end

Cela fonctionne dans tous les cas, y compris si le texte est effacé.

Enfin, pour éviter que la table ne se décolore en gris puis en blanc lorsque la recherche est activée, utilisez ceci:

self.searchController.dimsBackgroundDuringPresentation = NO;
11
malhal

Swift 3 Version:

Si votre searchResultController n'est pas nil et que vous utilisez un contrôleur de vue de table séparé pour afficher les résultats de la recherche, vous pouvez le rendre conforme à UISearchResultUpdating et dans la fonction updateSearchResults, vous pouvez simplement masquer la vue.

func updateSearchResults(for searchController: UISearchController) {
    view.hidden = false
}

Swift 4 Version:

func updateSearchResults(for searchController: UISearchController) {
    view.isHidden = false
}
5
Yafi

La version 2.3 de Swift de l'approche de @ malhal:

class SearchResultsViewController : UIViewController {
    var context = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        // Add observer
        view.addObserver(self, forKeyPath: "hidden", options: [ .New, .Old ], context: &context)
    }

    deinit {
        view.removeObserver(self, forKeyPath: "hidden")
    }

    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        if context == &self.context {
            if change?[NSKeyValueChangeNewKey] as? Bool == true {
                view.hidden = false
            }
        } else {
            super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
        }
    }
}
3
Niels

Je travaille récemment sur UISearchController. Je souhaite afficher l'historique de recherche dans searchResultsController lorsque la barre de recherche est vide. Donc, searchResultsController doit apparaître lorsque UISearchController est présenté.

J'ai essayé une autre solution pour rendre la searchResultsController toujours visible en en surchargeant la propriété hidden dans une vue personnalisée .

par exemple, ma searchResultsController est une UITableViewController. Je crée un VisibleTableView en tant que sous-classe de UITableView, puis je change la classe personnalisée UITableView de searchResultsController en VisibleTableView in xib ou en storyboard. De cette façon, ma searchResultsController ne sera jamais cachée par UISearchController.

Les bonnes choses ici:

  1. Plus facile à mettre en œuvre que KVO.

  2. Aucun délai pour afficher searchResultsController. L'inversion de l'indicateur masqué dans la méthode déléguée "updateSearchResults" fonctionne, mais il y a un délai d'affichage de la variable searchResultsController.

  3. Il ne réinitialise pas le drapeau caché, il n'y a donc pas d'écart/saut entre l'interface cachée et visible.

Exemple de code Swift 3 :

class VisibleTableView: UITableView {
override var isHidden: Bool {
    get {
        return false
    }
    set {
        // ignoring any settings
    }
}
}
2
simonwjw

Ce qui est caché est la vue du contrôleur de résultats de recherche. Par conséquent, il est suffisant de le révéler à tout moment. Procédez simplement comme suit dans le contrôleur de résultats de recherche:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.view.isHidden = false
}

func updateSearchResults(for searchController: UISearchController) {
    self.view.isHidden = false
    // ... your other code goes here ...
}

Maintenant, la vue des résultats (c'est-à-dire la vue tableau) est toujours visible, même lorsque le texte de la barre de recherche est vide.

En passant, l'application iOS Mail se comporte comme ceci, et je suppose que c'est ainsi qu'elle est mise en œuvre (sauf si Apple a accès à un paramètre de secret privé UISearchController).

[Testé sous iOS 10 et iOS 11; Je n'ai testé sur aucun système antérieur.]

2
matt

Je pense que tu te trompes.

SearchResultsController apparaît uniquement lorsqu'il y a des résultats. Ceci est légèrement différent de votre interprétation.

Les résultats sont chargés manuellement en fonction du texte figurant dans la barre de recherche. Vous pouvez donc l'intercepter si la barre de recherche est vide et renvoyer votre propre ensemble de résultats.

0
Fogmeister

Swift 4 version de malhals answer :

class SearchController: UISearchController {

    private var viewIsHiddenObserver: NSKeyValueObservation?

    override func viewDidLoad() {
        super.viewDidLoad()

        viewIsHiddenObserver = self.searchResultsController?.view.observe(\.hidden, changeHandler: { [weak self] (view, _) in
            guard let searchController = self else {return}
            if view.isHidden && searchController.searchBar.isFirstResponder {
            view.isHidden = false
            }
        })

    }

}

Veuillez noter le [weak self]. Sinon, vous introduiriez un cycle de conservation.

0
Lausbert

J'ai vraiment aimé la réponse de Simon Wang et j'ai travaillé dessus. C'est ce que j'ai fait et cela fonctionne parfaitement:

Je sous-classe UISearchController dans ma classe personnalisée:

class CustomClass: UISearchController {
  override var searchResultsController: UIViewController? {
    get {
      let viewController = super.searchResultsController
      viewController?.view.isHidden = false
      return viewController
    }
    set {
      // nothing
    }
  }
}

Assurez-vous également que cela ne figure nulle part dans votre code:

self.resultsSearchController.isActive = true

resultsSearchController est mon contrôleur UISearchController

0
Volhan Salai

Si vous ne souhaitez pas assombrir les résultats, définissez la propriété dimsBackgroundDuringPresentation sur false.

Cela garantira que le contenu sous-jacent n'est pas estompé lors d'une recherche.

Vous devrez également vous assurer de renvoyer les résultats même lorsque le searchText est vide, sinon une tableview vide sera affichée.

0
Praveen Gowda I V

J'ai passé beaucoup de temps avec cela, et finalement la solution que j'ai choisie ressemble à celle de @ malhals, mais la quantité de code est considérablement réduite en utilisant le KVOController de Facebook: https://github.com/facebook/KVOController . Un autre avantage ici est que si votre searchResultsController est un UINavigationController, vous n'avez pas besoin de le sous-classer pour ajouter du code @ malhal.

// always show searchResultsController, even if text is empty
[self.KVOController observe:self.searchController.searchResultsController.view keyPath:@"hidden" options:NSKeyValueObservingOptionNew block:^(id observer, UIView* view, NSDictionary *change) {
    if ([change[NSKeyValueChangeNewKey] boolValue] == YES) {
        view.hidden = NO;
    }
}];
self.searchController.dimsBackgroundDuringPresentation = NO;
0
skensell

Le plus simple est d’utiliser ReactiveCocoa avec cette extension https://github.com/ColinEberhardt/ReactiveTwitterSearch/blob/master/ReactiveTwitterSearch/Util/UIKitExtensions.Swift

        presentViewController(sc, animated: true, completion: {
            sc.searchResultsController?.view.rac_hidden.modify({ value -> Bool in
                return false
            })
        } )

où sc est votre UISearchController

0
Roman Barzyczak