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

Невозможно установить searchBar как firstResponder

У меня есть searchBar, который я устанавливаю в tableviewcontroller. я ссылался на этот аналогичный вопрос UISearchBar не может стать первым ответчиком после повторного появления UITableView, но я все еще не могу установить его как первого ответчика. В файле .h:

@property (strong, nonatomic) UISearchController *searchController;
@property (strong, nonatomic) IBOutlet UISearchBar *searchBar;

В представлении didload:

self.searchController = [[UISearchController alloc]initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.hidesNavigationBarDuringPresentation = NO;

self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.origin.x, self.searchController.searchBar.frame.origin.y, self.searchController.searchBar.frame.size.width, 44.0);
self.tableView.tableHeaderView = self.searchController.searchBar;

И в viewDidAppear:

-(void)viewDidAppear:(BOOL)animated {
  [self.searchController setActive:YES];
  [self.searchController.searchBar becomeFirstResponder];
  [super viewDidAppear:animated];
}

Когда я перехожу к просмотру, анимируется searchBar, но клавиатура не появляется.

4b9b3361

Ответ 1

Я тоже заметил эту проблему. Кажется, что вызов becomeFirstResponder выполняется, когда searchController все еще "загружается". Если вы прокомментируете becomeFirstResponder, вы заметите, что нет никакой разницы. Таким образом, нам нужен способ вызвать startFirstResponder после того, как loadController выполнил загрузку.

Когда я посмотрел на различные методы делегатов, я заметил, что есть метод делегата:

- (void)didPresentSearchController:(UISearchController *)searchController

Этот метод вызывается сразу после представления searchController. Затем я делаю вызов becomeFirstResponder:

- (void)didPresentSearchController:(UISearchController *)searchController
{
    [searchController.searchBar becomeFirstResponder];
}

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

Ответ 2

Решение с - (void)didPresentSearchController:(UISearchController *)searchController не работал, так как этот метод делегата вызывается только тогда, когда пользователь нажимает на строку поиска...

Однако это решение действительно работало:

- (void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self performSelector:@selector(showKeyboard) withObject:nil afterDelay:0.1];
}

- (void) showKeyboard
{
    [self.searchController.searchBar becomeFirstResponder];
}

Swift 3

delay(0.1) { self.searchController.searchBar.becomeFirstResponder() }

func delay(_ delay: Double, closure: @escaping ()->()) {
    let when = DispatchTime.now() + delay
    DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}

Ответ 3

Хорошо, я нашел решение, которое действительно отлично работает для меня.

Не вызывайте [self.searchController setActive:YES]; перед вызовом [self.searchController.searchBar becomeFirstResponder];

Что лучше, не называйте [self.searchController setActive:YES]; вообще.

Вызовите только [self.searchController.searchBar becomeFirstResponder];, и клавиатура просто выскочит, как следует, без каких-либо задержек.

Кажется, это похоже на ошибку, и многие люди ее подтверждают. Например, здесь: При назначении фокуса через startFirstResponder для UISearchBar UISearchController, клавиатура не отображается

Ответ 4

Функция searchController.searchBar.becomeFirstResponder() должна быть вызвана в основном потоке и после searchController.active = true в методе viewDidLoad. Вот полное решение. Он работает на iOS 9.3

override func viewDidAppear(animated: Bool) {
  super.viewDidAppear(animated)
  searchController.active = true
  Async.main {
    self.searchController.searchBar.becomeFirstResponder()
  }
}

Ответ 5

Очень похоже на другие ответы, но мне пришлось получить доступ к основной очереди в ViewDidAppear. SearchBarController не может действовать, пока не появится View, и тогда это может быть сделано только в основной очереди для UI:

searchController.active = true  // doubtful whether this is needed

        dispatch_async(dispatch_get_main_queue(), {
            self.searchController.searchBar.becomeFirstResponder()
        });

Ответ 6

Swift 4, iOS 11

Меня устраивает

// 1.
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    resultSearchController.isActive = true
}

// 2. ->> UISearchControllerDelegate
func didPresentSearchController(_ searchController: UISearchController) {

    DispatchQueue.main.async {
        searchController.searchBar.becomeFirstResponder()
    }
}

Ответ 7

Простой вариант Swift3:

override func viewDidLoad() {
    super.viewDidLoad()
    navigationItem.titleView = mySearchController.searchBar
    mySearchController.searchResultsUpdater = self
    mySearchController.delegate = self

}

override func viewDidAppear(_ animated: Bool) {
    DispatchQueue.main.async {
        self.mySearchController.isActive = true
    }
}

func presentSearchController(_ searchController: UISearchController) {
    mySearchController.searchBar.becomeFirstResponder()
}

Это работает; -)

Ответ 8

Альтернативный подход

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


TL;DR; Посмотрите на self.definesPresentationContext = true, isBeingDismissed, isBeingPresented, canBecomeFirstResponder, и назначение делегата SearchController, Searchbar, SearchResultsUpdater и их методы делегирования.

(Источник решения self.definesPresentationContext - См. ответ SO)

Одна вещь, о которой следует помнить, - это контекст того, как будет отображаться SearchBar. Встраивается в панель инструментов, панель навигации, другой UIView, как вид входа или входа. Все, что я обнаружил, повлияло на время и внутреннюю анимацию панели поиска, поскольку она представлена ​​или увольняется.

Я попытался представить все представленные решения, и ни одна из них не работала, пока я не передумал, как я пытался использовать searchBar. В моем случае я нажал контроллер (B) с помощью контроллера поиска с контроллера (A), на котором уже был начальный контрольный контролер. Я программно встраивал каждый из контроллеров поиска в titleView моего элемента навигации, когда делал обновление при загрузке.

Ответы, предлагающие добавить searchbar.becomeFirstResponder() в жизненный цикл, не имели смысла для моего случая использования, так как представление было полностью загружено к моменту, когда я хотел вставить и отобразить мою панель поиска в navigationItem. Логика также казалась запутанной, так как методы жизненного цикла контроллера представления должны уже работать в основном потоке. Добавление задержки к презентации также представляло собой вмешательство во внутренние операции отображения и анимации, используемые системой.

Я обнаружил, что вызов моей функции для переключения вставки работал, когда я выталкивал контроллер вида из контроллера A, но клавиатура не отображалась должным образом при нажатии с контроллера B. Разница между этими двумя ситуациями заключалась в том, что controllerA был статическим контроллером таблицы, а контроллер B имел контроллер таблицы, который я сделал для поиска, добавив свой собственный контроллер поиска.

например. controllerA с поисковым сегментом до контроллераB с поисковой панелью

Используя несколько точек останова и исследуя статус searchController и поисковой панели в разных точках, я смог определить, что searchController.canBecomeFirstResponder возвращал false. Я также обнаружил, что мне нужно установить SearchResultsUpdater в self и делегаты как на searchController, так и на searchBar.

Наконец, я заметил, что установка self.definesPresentationContext = true на контроллере A не позволяла отображать клавиатуру, когда я вставлял контроллер B в стек навигации. Мое решение состояло в том, чтобы переместить self.definesPresentationContext = true в viewDidAppear на контроллер A и в метод prepare(for:sender:) контроллера A, я изменил его на self.definesPresentationContext = false, когда пунктом назначения является контроллер B. Это разрешило проблему с отображением клавиатуры в моем случае.

Слово в анимации ~ Я обнаружил, что при назначении вещей в navigationItem или navigationBar система имеет встроенные настройки времени и анимацию по умолчанию. Я избегаю добавления пользовательской анимации, кода в методах moveToParent или отложенных презентаций, потому что во многих случаях происходит непредвиденное поведение.

Почему это решение работает

Apple documentation на definesPresentationContext указывает поведение по умолчанию и отмечает некоторые ситуации, когда этот контекст регулирует поведение контроллера, назначенного для управления клавиатурой появление. В моем случае controllerA был настроен управлять презентацией, а не контроллером B, поэтому я просто изменил это поведение, настроив это значение:

При использовании стиля currentContext или overCurrentContext для представления view controller, это свойство контролирует, какой существующий контроллер представления на ваш взгляд, иерархия контроллера фактически покрывается новой содержание. Когда возникает контекстная презентация, UIKit начинается с представляя контроллер представления и просматривая иерархию контроллера представления. Если он находит контроллер представления, значение которого для этого свойства истинно, он запрашивает у контроллера вида представление нового контроллера вида. Если нет контроллер просмотра определяет контекст представления, UIKit запрашивает Windows root view для обработки презентации. По умолчанию Значение для этого свойства ложно. Некоторые системные представления контроллеры, такие как UINavigationController, изменить значение по умолчанию к true.

Ответ 9

Я думаю, что может быть более чистое решение. Я обнаружил, что клавиатура была как бы "сбивающейся", а затем назад, когда она была представлена, и нажимал becomeFirstResponder в didPresentSearchController:, но клавиатура приходила в конце, а анимация была немного причудливой.

Обертка моего метода данных перезагрузки с проверкой на презентацию сделала всех счастливыми:

- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
    if (!searchController.isBeingPresented && !searchController.isBeingDismissed) {
        [self.collectionView reloadData];
    }
}

Я нашел это, установив точку останова в resignFirstResponder. resignFirstResponder вызывался из reloadData, который, в свою очередь, вызывался updateSearchResultsForSearchController:. Оказывается, что updateSearchResultsForSearchController: вызывается во время представления контроллера поиска. Если вы заблуждаетесь с иерархией представлений, в которой находится UISearchBar в течение этого времени, контроллер поиска получает borked. Я предполагаю, что вызов reloadData вызывал представление заголовка UICollectionReusableView и возвращался в иерархию представлений и заставлял подзаголовок UISearchBar сбрасывать первого ответчика.

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

Ответ 10

Решение микширования @edwardmp и @mislovr отвечает на то, что сработало для меня (клавиатура выскакивает с небольшой задержкой):

- (void)didPresentSearchController:(UISearchController *)searchController {
    [self performSelector:@selector(showKeyboard) withObject:nil afterDelay:0.001];
}

- (void) showKeyboard {
    [self.searchController.searchBar becomeFirstResponder];
}

Ответ 11

Я боролся с этим какое-то время, но получил его работу:

  • Инициализация searchController в viewDidLoad()
  • Настройка active = true в viewDidAppear()
    • Это вызывает didPresentSearchController() в расширении UISearchControllerDelegate.
  • Настройка searchBar.becomeFirstResponder() в didPresentSearchController()

Вот полный пример: он использует Автозаполнение Карт Google.

class myViewController: UIViewController {

    // MARK: Variables

    var resultsViewController: GMSAutocompleteResultsViewController?
    var searchController: UISearchController?
    var resultView: UITextView?

    // MARK: Outlets

    @IBOutlet var myView: UIView!

    // MARK: View Methods

    override func viewDidLoad() {
        super.viewDidLoad()

        resultsViewController = GMSAutocompleteResultsViewController()
        resultsViewController?.delegate = self

        searchController = UISearchController(searchResultsController: resultsViewController)
        searchController?.delegate = self
        searchController?.searchResultsUpdater = resultsViewController

        searchController?.searchBar.prompt = "Search for a Place"
        searchController?.searchBar.placeholder = "place name"
        searchController?.searchBar.text = ""
        searchController?.searchBar.sizeToFit()

        searchController?.searchBar.returnKeyType = .Next
        searchController?.searchBar.setShowsCancelButton(true, animated: false)

        myView.addSubview((searchController?.searchBar)!)
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(true)
        searchController?.active = true
    }

    // MARK: GMSAutocompleteResultsViewControllerDelegate Extension

    extension myViewController: GMSAutocompleteResultsViewControllerDelegate {

        func resultsController(resultsController: GMSAutocompleteResultsViewController,
                       didAutocompleteWithPlace place: GMSPlace) {
            searchController?.active = false
            // Do something with the selected place.
            print("Place name: ", place.name)
            print("Place address: ", place.formattedAddress)
            print("Place attributions: ", place.attributions)
        }

        func resultsController(resultsController: GMSAutocompleteResultsViewController,
                       didFailAutocompleteWithError error: NSError){
            // TODO: handle the error.
            print("Error: ", error.description)
        }

        // Turn the network activity indicator on and off again.
        func didRequestAutocompletePredictionsForResultsController(resultsController: GMSAutocompleteResultsViewController) {
            UIApplication.sharedApplication().networkActivityIndicatorVisible = true
        }

        func didUpdateAutocompletePredictionsForResultsController(resultsController: GMSAutocompleteResultsViewController) {
            UIApplication.sharedApplication().networkActivityIndicatorVisible = false
        }
    }

    extension myViewController: UISearchControllerDelegate {

        func didPresentSearchController(searchController: UISearchController) {
            self.searchController?.searchBar.becomeFirstResponder()
        }
    }
}

Ответ 12

Ответ заключается в том, чтобы вызвать сталFirstResponder в viewDidAppear.

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    searchBar?.becomeFirstResponder()
}

Ответ 13

Вот что у меня получилось в Swift 4

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    searchController.isActive = true
    DispatchQueue.main.async{
        self.searchController.searchBar.becomeFirstResponder()
    }
}

Ответ 14

Исходя из решения @mislovr, задержка 0.1 была недостаточно продолжительной. Вот мой обновленный код к этому ответу.

func presentSearchController() {
    searchController.isActive = true

    Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak searchController] timer in
        guard let searchController = searchController else {
            timer.invalidate()
            return
        }

        if searchController.searchBar.canBecomeFirstResponder {
            searchController.searchBar.becomeFirstResponder()
            timer.invalidate()
        }
    }
}

Ответ 15

Для меня это довольно большое отставание при использовании viewDidAppear. Лучше использовать becomeFirstResponder асинхронно в viewDidLoad (тестируется с iOS 10, Swift 3):

override func viewDidLoad() {
    super.viewDidLoad()

    DispatchQueue.main.async {
        searchController.searchBar.becomeFirstResponder()
    }
}

Ответ 16

Это то, что сработало для меня в Swift 3.

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    self.perform(#selector(self.showKeyboard), with: nil, afterDelay: 0.1)
}

func showKeyboard() {
    self.searchController.searchBar.becomeFirstResponder()
}

Ответ 17

В Задаче C:

- (void)viewDidLoad {
    [super viewDidLoad];

    // Setup your SearchViewController here...
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    // Will show Cancel button in White colour 
    [[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTintColor:[UIColor whiteColor]];

    // searchController.active = YES;   // This is not necessary

    // set SearchBar first responder inside Main Queue block
    dispatch_async(dispatch_get_main_queue(), ^{
        [self->searchController.searchBar becomeFirstResponder];
    });
}

Ответ 18

Решение Swift 5:

override func viewDidLoad() {
        super.viewDidLoad()
        definesPresentationContext = true
        searchController.delegate = self
    }

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    searchController.isActive = true
}

extension ViewController: UISearchControllerDelegate {
 func didPresentSearchController(_ searchController: UISearchController) {
      DispatchQueue.main.async {[weak self] in
          self?.searchController.searchBar.becomeFirstResponder()
       }
  }
}