Может ли интерфейс UIWebView взаимодействовать с пользователем, не становясь первым ответчиком?

Мое приложение имеет вид, похожий на приложение "Сообщения iOS", с представлением в стиле панели инструментов, содержащим текстовое поле, прикрепленное к нижней части экрана:

aZb16.png

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

SEbI7.png

Я реализовал это, создав панель инструментов inputAccessoryView на UITableViewController и сделав ее основным view первым ответчиком. Текст основного текста "Вот ссылка..." реализуется как UIWebView, поскольку он заполняется HTML из веб-службы и нуждается в поддержке форматирования и ссылок.

Проблема, с которой я сталкиваюсь, заключается в том, что когда пользователь нажимает на UIWebView, он становится первым ответчиком, а inputAccessoryView скрывается сам:

vFFKn.png

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

Должен ли UIWebView стать первым ответчиком для обработки кранов? Если это так, можете ли веб-просмотр обращаться с краном по ссылке, а затем передать событие касания до основного view и снова сделать этого первого ответчика? Любые указатели на то, как исправить это, будут с благодарностью приняты.

UPDATE:

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

extension UIView {

    public override func becomeFirstResponder() -> Bool {
        // Actual view is instance of private class UIWebBrowserView, its parent parent view is UIWebView
        if self.superview?.superview is UIWebView {
            return false
        } else {
            return super.becomeFirstResponder()
        }
    }

}

Может ли кто-нибудь сказать мне, как достичь вышеуказанного в не-хакерской манере?

Ответ 1

Здесь я могу предложить две вещи. Быстрое и грязное решение (я надеюсь) и совет (или вы можете назвать это меня удивленным).

Грязное решение: выполните что-то вроде этого

func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
    self.shouldCloseKeyboard = false
    return true
}

func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
    return self.shouldCloseKeyboard
}

@IBAction func inputDone(_ sender: AnyObject) {
    if self.inputField.isFirstResponder {
        self.shouldCloseKeyboard = true
        self.inputField.resignFirstResponder()
    }
}

inputDone: будет подключен к вашей кнопке "Опубликовать" и, очевидно, также будет содержать все, что вам нужно сделать для публикации (или вы помещаете это в textFieldShouldEndEditing, если и перед тем, как закрыть его, т.е. выполняются с помощью ввода), shouldCloseKeyboard - это Bool var, который инициализируется до false, но затем используется, как вы можете видеть здесь. Очевидно, что inputField - это ваше текстовое поле (не весь вид аксессуаров!), А весь класс соответствует UITextFieldDelegate, а текстовое поле имеет свой экземпляр, заданный как delegate через Interface Builder (или как-то еще).

Это должно вести себя так, как вы планируете. Я попробовал это в примерном проекте, хотя я не использовал панель инструментов, подобную вам (см. Ниже), или помещала все в таблицу. Я просто сделал простой веб-просмотр с введенным ниже и кнопкой, чтобы закрыть материал. Когда текстовое поле было первым ответчиком, то есть клавиатурой, я мог щелкнуть по веб-просмотру, следовать ссылкам и т.д., И клавиатура не исчезла. Когда вы нажимаете кнопку, она правильно закрывается (я не беспокоился о том, что было первым ответчиком тогда).

Теперь, что меня удивило: сначала я подумал, что это хороший трюк, чтобы просто использовать панель инструментов как inputAccessoryView, чтобы она двигалась по клавиатуре. Однако, подумав, я пришел к выводу, что не сделал бы этого (и вместо этого вручную переведю представление, когда клавиатура будет показана, вместе с чем-нибудь еще. Вам, вероятно, также придется сделать это, чтобы не было ничего важного скрытый клавиатурой или аксессуаром). Причина в том, что аксессуарные представления не должны находиться в иерархии представлений в другом месте. Фактически, когда я быстро попробовал это в своем демонстрационном проекте по прямому вопросу (я вложил кнопку и текстовое поле в UIView, определил выход для этого, а затем установил его как inputAccessoryView в коде, попробовал в разных местах) приложение разбилось. что разумно, потому что текстовое поле является подвидным контейнером, который будет аксессуаром, но в то же время дочерним элементом главного представления диспетчера представлений... Я удивлен, что вам удалось преодолеть все это.

Короче говоря, этот подход казался хлопот. То, как я понимаю входные виды аксессуаров, больше предназначено для использования в дополнительных представлениях, а не для элементов управления, которые уже представлены на экране (и, таким образом, уже являются частью иерархии представлений). В другом проекте я специально избегал возиться с этим, просто притворяя его: у меня было отдельное представление, загруженное как аксессуар, который просто выглядел как элемент управления, с которым он был связан. Как только это поддельное представление было отображено, я переключил на него первый ответчик (ну, в одно из его подзонов, текстовое поле, поэтому клавиатура осталась), и мой пользователь мог бы отредактировать. После нажатия кнопки "Готово" я скопировал вход в текстовое поле "оригинал", и все было neato.

Итак, в конечном счете, я бы рекомендовал вам (или кому-либо) пересмотреть возиться с существующими представлениями в качестве аксессуаров и вместо этого выбрать "регулярное" смещение вида там, где это необходимо, и остаться с аксессуарами как "дополнительные помощники".

UPDATE:

Хорошо, то, что я описал выше (в "быстром и грязном" решении) предполагал, что вам нужен только один выход из редактирования, т.е. предотвратить что-либо еще, чтобы стать первым ответчиком и только позволить ему потерять первого ответчика после "Готово",.

В отношении webView он выполняет одно и то же. Проблема при попытке только предотвратить появление WebView от первого ответчика заключается в том, как определить, что заставляет текстовое поле терять статус первого ответчика. Я думаю, что ваш "хакерский" подход указывает на правильное направление, но поскольку вы не должны переопределять методы в расширении (iirc), я бы предложил в этом случае подклассифицировать UIWebView. Затем вы можете также дать ему свойство включать и выключать это поведение (т.е. Если текстовое поле становится первым ответчиком/начинает редактирование, вы включаете его, как только оно теряет его, вы его отключите). Этого должно быть достаточно для подкласса:

class MyWebView: UIWebView {

    public var shouldNotBecomeFirstResponder = false;

    override public var canBecomeFirstResponder: Bool {
        if self.shouldNotBecomeFirstResponder {
            return false
        } else {
            return super.canBecomeFirstResponder
        }
    }
}

(Я понял, что исходное поведение передразнивается, если новый Bool не задан, это должно привести к тому, что класс ведет себя точно так же, как webView во всех остальных случаях. Однако я не тестировал это в вашей настройке. ) Установка shouldNotBecomeFirstResponder должна выполняться во всех соответствующих местах. Я думаю, вы могли бы это сделать, например, прослушивать уведомления о клавиатуре (dis).

Как правило, могут быть и другие способы сделать это, не требуя подклассов. Вы могли бы использовать распознаватели жестов в дополнение к моему первому предложению выше, чтобы выяснить, нужно ли устанавливать shouldCloseKeyboard или нет, но это, вероятно, больше работает и приводит к трудному чтению/сохранению кода. Может быть, трудно понять, что сначала вызвано, логика, определяющая, был ли нажат webView (и, следовательно, shouldCloseKeyboard должен быть установлен до того, как он станет первым ответчиком!) Или что-то еще (в этом случае он должен быть отменен как-то).

Итак, я # d иду с подклассом, хотя подкласс почти ничего не добавляет к webView.:)