В iOS 14 как программно нажать кнопку?
В iOS 14 я настроил кнопку для отображения меню вопросов. Сама по себе кнопка работает отлично. Вот как это настроено:
let button = UIButton()
button.setImage(UIImage(systemName: "chevron.down"), for: .normal)
button.showsMenuAsPrimaryAction = true
button.menu = self.menu
Я установил его в текстовом поле rightView. Вот как это выглядит при отображении меню.
При нажатии на элемент меню этот вопрос появляется в метке прямо над текстовым полем. Это прекрасно работает. Кроме….
Я также хочу, чтобы меню появлялось, когда пользователь нажимает в текстовом поле. Я не хочу, чтобы им приходилось специально нажимать на кнопку. В старые времена целевого действия это было легко:
button.sendActions(for: .touchUpInside)
. Я попробовал это и
.menuActionTriggered
и
.primaryActionTriggered
но ни один из них не работает. Я думаю, что sendActions() ищет старые действия на основе селектора.
Но есть новый метод sendActions(for: UIControl.Event). Вот что я пытаюсь
textFieldShouldBeginEditing()
:
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
let tempAction = UIAction { action in
print("sent action")
}
menuButton.sendAction(tempAction)
print("textField tapped")
return false
}
Разумеется, при касании текстового поля в консоли появляются сообщения «отправлено действие» и «текстовое поле нажато». Но я понятия не имею, какой UIAction я мог бы отправить, что означает «отобразить ваше меню». Я бы хотел, чтобы был этот метод:
send(_: UIControl.Event)
.
Любые идеи о том, как заставить кнопку отображать свое меню при нажатии на текстовое поле?
пс. да,
textFieldShouldBeginEditing
нужно будет знать, был ли выбран вопрос, и в этом случае нужно будет разрешить редактирование. Эта часть проста.
1 ответ
Из того, что я читал, вы не можете программно запускать
UIContextMenuInteraction
поскольку взаимодействия, кажется, обрабатываются внутри себя, в отличие от
send(_: UIControl.Event)
Я вижу это упомянутым в этом сообщении SO здесь
Также в документах кажется, что у нас нет доступа к управлению взаимодействием. Apple должна решить, доступно ли 3D-касание или по умолчанию вернуться к долгому касанию.
Из документов
Объект взаимодействия с контекстным меню отслеживает жесты Force Touch на устройствах, поддерживающих 3D Touch, и жесты длительного нажатия на устройствах, которые его не поддерживают.
Обходной путь
Я могу предложить следующий обходной путь для вашего варианта использования. Мой пример был создан с использованием фрейма вместо автоматического макета, так как быстрее и проще продемонстрировать концепцию таким образом, однако вам нужно будет внести коррективы с помощью автомакета.
1. Создайте элементы пользовательского интерфейса
// I assume you have a question label, answer text field and drop down button
// Set up should be adjusted in case of autolayout
let questionLabel = UILabel(frame: CGRect(x: 10, y: 100, width: 250, height: 20))
let answerTextField = UITextField(frame: CGRect(x: 10, y: 130, width: 250, height: 50))
let dropDownButton = UIButton()
2. Обычная настройка этикетки и текстового вида в первую очередь
// Just regular set up
private func configureQuestionLabel()
{
questionLabel.textColor = .white
view.addSubview(questionLabel)
}
// Just regular set up
private func configureAnswerTextField()
{
let placeholderAttributes = [NSAttributedString.Key.foregroundColor :
UIColor.lightGray]
answerTextField.backgroundColor = .white
answerTextField.attributedPlaceholder = NSAttributedString(string: "Tap to select a question",
attributes: placeholderAttributes)
answerTextField.textColor = .black
view.addSubview(answerTextField)
}
3. Добавьте кнопку в текстовое представление
// Here we have something interesting
private func configureDropDownButton()
{
// Regular set up
dropDownButton.setImage(UIImage(systemName: "chevron.down"), for: .normal)
dropDownButton.showsMenuAsPrimaryAction = true
// 1. Create the menu options
// 2. When an option is selected update the question label
// 3. When an option is selected, update the button frame
// Update button width function explained later
dropDownButton.menu = UIMenu(title: "Select a question", children: [
UIAction(title: "Favorite movie") { [weak self] action in
self?.questionLabel.text = action.title
self?.answerTextField.placeholder = "Add your answer"
self?.answerTextField.text = ""
self?.updateButtonWidthIfRequired()
},
UIAction(title: "Car make") { [weak self] action in
self?.questionLabel.text = action.title
self?.answerTextField.placeholder = "Add your answer"
self?.answerTextField.text = ""
self?.updateButtonWidthIfRequired()
},
])
// I right align the content and set some insets to get some padding from
// the right of the text field
dropDownButton.contentHorizontalAlignment = .right
dropDownButton.contentEdgeInsets = UIEdgeInsets(top: 0.0,
left: 0.0,
bottom: 0.0,
right: 20.0)
// The button initially will stretch across the whole text field hence we
// right aligned the content above
dropDownButton.frame = answerTextField.bounds
answerTextField.addSubview(dropDownButton)
}
// Update the button width if a menu option was selected or not
func updateButtonWidthIfRequired()
{
// Button takes the text field's width if the question isn't selected
if let questionText = questionLabel.text, questionText.isEmpty
{
dropDownButton.frame = answerTextField.bounds
return
}
// Reduce button width to right edge as a question was selected
dropDownButton.frame = CGRect(x: answerTextField.frame.width - 50.0,
y: answerTextField.bounds.origin.y,
width: 50,
height: answerTextField.frame.height)
}
4. Конечный результат
Начните с представления, похожего на ваше
Затем я нажимаю в середине текстового поля
Он отображает меню, как задумано
После выбора параметра вопрос отображается в метке, а заполнитель обновляется.
Теперь я могу начать вводить свой ответ, используя текстовое поле, и кнопка активна только с правой стороны, так как ее размер был изменен.
И кнопка все еще активна
Последние мысли
- Может быть лучше поместить это в подкласс UIView/UITextField
- Это был пример использования кадров со случайными значениями, необходимо внести коррективы для автомакета.
Правки из ОП (слишком длинные для комментария):
- Я пытался установить
contentEdgeInsets
так что кнопка была далеко слева, но закрывала текст-заполнитель. - Ваша идея просто добавить кнопку в качестве подвида текстового поля была ключевой, но...
- После выбора вопроса и изменения размера кнопки, если текстовое поле было первым ответившим, нажатие кнопки не имело никакого эффекта. Кнопка была в иерархии представлений, но текстовое поле получило нажатие.
- Итак, когда вопрос выбран, я удаляю кнопку из ее супервизора (текстового поля) и добавляю ее в текстовое поле.
rightView
. Затем он примет касание, даже если текстовое поле будет первым ответчиком. - И, как вы подозревали, мне пришлось прикрепить кнопку к textField с ограничениями.