Подтвердить что ты не робот

UISearchController: показывать результаты, даже если панель поиска пуста

Как я понимаю, поведение UISearchController по умолчанию:

  • При нажатии на панель поиска фон становится тусклым и отображается кнопка "Отменить". SearchResultsController пока не отображается.
  • SearchResultsController отображается только в том случае, если строка поиска не пуста.

Я хочу отображать SearchResultsController, даже если панель поиска пуста, но выбрана (т.е. это пример 1 выше).

Проще говоря, вместо фонового затемнения я хотел бы показать результаты поиска.

Есть ли способ сделать это?

Дополнительная информация:

Я не использую UISearchController для фильтрации результатов, отображаемых в представлении, на котором он показан, но некоторых других несвязанных результатов. Это будет похоже на то, что делает facebook на своем "Лента новостей". Нажатие на панель поиска сначала показывает поисковые предложения, а затем, когда мы начинаем редактирование, оно показывает результаты поиска, которые могут быть не связаны с лентой новостей.

4b9b3361

Ответ 1

Если ваш searchBar активен, но не имеет текста, отображаются результаты таблицы tableView. Это встроенное поведение и причина, по которой searchResultsController скрыт для этого состояния.

Чтобы изменить поведение, когда поиск активен, но не фильтрует, вам нужно будет показать searchResultsController, когда он обычно скрыт.

Может быть хороший способ выполнить это через <UISearchResultsUpdating> и updateSearchResultsForSearchController:. Если вы можете решить это по протоколу, это предпочтительный путь.

Если это не помогает, вы можете взломать встроенное поведение. Я бы не рекомендовал и не полагался на него, и он будет хрупким, но вот ответ, если вы выберете этот вариант:

  • Убедитесь, что ваш tableViewController соответствует <UISearchControllerDelegate>, и добавьте

    self.searchController.delegate = self;

  • Внедрить willPresentSearchController:

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

    Это делает searchResultsController видимым после того, как его UISearchController установил его в скрытое.

  • Внедрить didPresentSearchController:

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

Для лучшего способа работы со встроенным поведением см. malhal answer.

Ответ 2

Вы можете просто реализовать протокол UISearchResultsUpdating и установить отображение контроллера результатов всегда в updateSearchResultsForSearchController:

 func updateSearchResultsForSearchController(searchController: UISearchController) {

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

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

Это работает, потому что метод вызывается даже тогда, когда панель поиска активирована без какого-либо текста.

Ответ 3

Я испробовал решение PetahChristian, результат предварительной загрузки появился, когда мы впервые сфокусировали поисковую панель, но когда мы вводим что-то, то очищаем его, результаты предварительной загрузки снова не появятся.

Я придумал другое решение. Нам нужно добавить делегата в SearchResultsController и вызвать его, когда наш searchController.searchBar.text пуст. Что-то вроде этого:

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()
        ...
    }
    ...
}

И в контроллере содержится SearchController, мы добавляем наш делегат:

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

Ответ 4

С такими сложными вещами я рекомендую использовать кувалду! То есть, чтобы обнаружить, когда что-то пытается скрыть его, и когда это произойдет, измените его. Это можно сделать с помощью KVO (Key Value Observing). Это будет работать независимо от того, что, без необходимости обрабатывать все тонкости панели поиска. Извините, код сложный, но KVO - это старый API, но мой код следует рекомендациям. В вашем SearchResultsViewController поставьте это:

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

Это работает во всех случаях, в том числе, если текст очищается.

Наконец, чтобы таблица не делала уродливое затухание до серого, а затем до белого, когда поиск активируется, используйте это:

self.searchController.dimsBackgroundDuringPresentation = NO;

Ответ 5

Версия Swift 3:

Если ваш searchResultController не равен nil и вы используете отдельный контроллер табличного представления для отображения результатов поиска, то вы можете настроить этот контроллер табличного представления на UISearchResultUpdating а в функции updateSearchResults вы можете просто отобразить представление.

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

Версия Swift 4:

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

Ответ 6

Версия Swift 2.3 для подхода @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)
        }
    }
}

Ответ 7

Обновлено для iOS 13

От iOS13 мы получили поддержку системного API для этого поведения. Вы можете установить свойство showsSearchResultsController = true

WWDC screenshot

Для iOS 12 и ниже

Я недавно работаю над UISearchController. Я хочу показать историю поиска в searchResultsController, когда строка поиска пуста. Поэтому searchResultsController должен появляться всякий раз, когда UISearchController будет представлен.

Здесь я использую другое решение, чтобы сделать searchResultsController всегда видимым, переопределяя свойство hidden в пользовательском представлении.

например, мой searchResultsController - это UITableViewController. Я создаю VisibleTableView как подкласс UITableView, а затем изменяю пользовательский класс UITableView searchResultsController на VisibleTableView в XIB или раскадровке. Таким образом, мой searchResultsController никогда не будет скрыт UISearchController.

Хорошие вещи здесь:

  1. Проще реализовать, чем КВО.

  2. Нет задержки, чтобы показать searchResultsController. Переключение скрытого флага в методе делегата "updateSearchResults" работает, но есть задержка для отображения searchResultsController.

  3. Он не сбрасывает скрытый флаг, поэтому между скрытым и видимым отсутствует разрыв/переход пользовательского интерфейса.

Пример кода Swift 3:

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

Ответ 8

Что скрывается, так это представление контроллера результатов поиска. Поэтому достаточно отобразить его в любое время, когда оно может быть скрыто. Просто выполните в контроллере результатов поиска следующее:

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 ...
}

Теперь представление результатов (т.е. представление таблицы) всегда отображается, даже если текст строки поиска пуст.

Кстати, приложение iOS Mail ведет себя так, и я предполагаю, что он реализован (если у Apple нет доступа к секретной частной настройке UISearchController).

[Протестировано в iOS 10 и iOS 11; Я не тестировал ни одну более раннюю систему.]

Ответ 9

Если вы не хотите уменьшать результаты, установите для свойства dimsBackgroundDuringPresentation значение false.

Это гарантирует, что основной контент не будет помечен во время поиска.

Вам также нужно будет убедиться, что вы возвращаете результаты, даже если searchText пуст, иначе будет отображаться пустой столбец.

Ответ 10

Я потратил много времени на это, и в конечном итоге решение, с которым я пошел, похоже на @malhals, но количество кода значительно сокращено с помощью facebook KVOController: https://github.com/facebook/KVOController. Еще одно преимущество заключается в том, что если ваш searchResultsController является UINavigationController, тогда вам не нужно подклассифицировать его только для добавления кода @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;

Ответ 12

Мне очень понравился Симон Ван ответ и работал с ним, и это то, что я сделал, и он отлично работает:

Я подклассифицирую UISearchController в своем пользовательском классе:

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

Также убедитесь, что у вас нет этого в вашем коде:

self.resultsSearchController.isActive = true

resultsSearchController - мой UISearchController

Ответ 13

Swift 4 версия 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
            }
        })

    }

}

Обратите внимание на [weak self]. В противном случае вы должны ввести цикл сохранения.

Ответ 14

Я думаю, вы ошибаетесь.

SearchResultsController появляется только при наличии результатов. Это немного отличается от вашей интерпретации.

Результаты загружаются вручную на основе текста в строке поиска. Таким образом, вы можете перехватить его, если панель поиска пуста и вернуть свой собственный набор результатов.