1

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

果然,当文本字段被点击时,“sent action”和“textField tapped”出现在控制台中。但我不知道我可以发送什么 UIAction 来表示“显示你的菜单”。我希望有这种方法:send(_: UIControl.Event)

关于如何在点击文本字段时让按钮显示其菜单的任何想法?

附言。是的,textFieldShouldBeginEditing需要知道是否选择了一个问题,在这种情况下需要允许编辑。那部分很容易。

4

1 回答 1

0

根据我的阅读,您无法以编程方式触发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. 最终结果

从与您相似的观点开始

带有下拉菜单的自定义 UITextField

然后我点击文本字段的中间

带有下拉菜单的 UITextField

它按预期显示菜单

从 UITextField UIButton 显示 UIMenu UIContextMenuInteraction

选择一个选项后,问题会显示在标签中,并且占位符会更新

来自 UIButton 的 UIMenu UIContextMenuInteraction

现在我可以开始使用文本字段输入我的答案,并且该按钮仅在右侧处于活动状态,因为它已调整大小

UITextField 与 UIContextMenuInteraction 上的 UIButton 中的 UIMenu

并且按钮仍然处于活动状态

来自 UIButton 的 UIMenu

最后的想法

  1. 将其放入 UIView / UITextField 子类可能会更好
  2. 这是一个使用具有随机值的帧的示例,需要对自动布局进行调整

来自 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 上。
于 2022-01-23T05:19:36.153 回答