根据我的阅读,您无法以编程方式触发UIContextMenuInteraction
,因为交互似乎是由内部自行处理的,不像send(_: UIControl.Event)
我在这里看到这个 SO 帖子中提到的
同样在文档中,我们似乎无法访问交互管理Apple 需要决定 3D 触摸是否可用或默认返回长按
从文档
上下文菜单交互对象跟踪支持 3D Touch 的设备上的 Force Touch 手势,以及不支持它的设备上的长按手势。
解决方法
我可以为您的用例提出以下解决方法。我的示例是使用框架而不是自动布局创建的,这样可以更快、更轻松地演示这个概念,但是您需要使用自动布局进行调整
1. 创建 UI 元素
// 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 子类可能会更好
- 这是一个使用具有随机值的帧的示例,需要对自动布局进行调整
来自 OP 的编辑(评论太长):
- 我试过设置
contentEdgeInsets
,所以按钮在左边,但它覆盖了占位符文本。
- 您将按钮简单地添加为文本字段的子视图的想法是关键,但是......
- 选择问题并调整按钮大小后,如果文本字段是第一响应者,则点击按钮无效。该按钮位于视图层次结构中,但文本字段得到了点击。
- So, when a question is selected, I remove the button from its superview (the textfield) and add it to the textField's
rightView
. 然后即使 textField 是第一响应者,它也会接受点击。
- 而且,正如您所怀疑的,我必须将按钮固定到带有约束的 textField 上。