【问题标题】:UIKeyCommand is not disabled when typing into a text field (Swift)输入文本字段时 UIKeyCommand 未被禁用 (Swift)
【发布时间】:2016-09-18 07:36:04
【问题描述】:

我制作了一个计算器应用程序 (swift),并设置了一些 UIKeyCommands,以便如果用户有蓝牙键盘,他们可以在计算器中输入数字/符号。

它们是这样工作的:

UIKeyCommand(input: "4", modifierFlags: [], action: "TypeFourInCalculator:")
func TypeFourInCalculator(sender: UIKeyCommand) {
    btn4Press(UIButton)
}

这一切都很好,当用户按下四键时将四添加到计算器中(即使计算器本身没有文本字段)。但是:我也有几个标准文本字段,我希望 UIKeyCommands 在用户进入其中一个文本字段时停止(这样他们就可以再次使用 BT 键盘定期键入)。在不禁用 UIKeyCommands 的情况下,键入会导致计算器功能,并且不会在文本字段中输入。

所以我尝试了这个,试图在文本字段成为第一响应者时禁用 UIKeyCommands:

let globalKeyCommands = [UIKeyCommand(input: "4", modifierFlags: [], action: "TypeFourInCalculator:"), UIKeyCommand(input: "5", modifierFlags: [], action: "TypeFiveInCalculator:")]

override var keyCommands: [UIKeyCommand]? {
    if powertextfield.isFirstResponder() == false { // if the user isn't typing into that text field
        return globalKeyCommands // use key commands
    } else { // if the user is typing
        return nil // disable those UIKeyCommands
    }

这偶尔会起作用,但通常不起作用。如果用户还没有使用 BT 键盘输入任何内容(即我猜没有激活键盘命令),那么他们可以像往常一样使用 BT 键盘在文本字段中输入。但是,如果他们已经通过 UIKeyCommand 在计算器中输入了数字,他们就无法在文本字段中输入(嗯,有时它可以正常工作,有时它会像我添加该预防性代码之前那样失败)。键入的文本不会出现在该文本字段中,而是调用计算器命令。

那么当用户开始输入时,我该怎么做才能禁用这些 UIKeyCommands 在普通文本字段中?

【问题讨论】:

  • 我会使用一些日志记录来查看何时查询keyCommands,以确保当您认为它正在更新时它实际上正在更新。
  • 下面的答案真的很有帮助,所以感谢克里斯。如果有人在这里停留需要任何​​帮助,请在下方发表评论,我可以为您提供帮助。

标签: ios swift bluetooth bluetooth-keyboard


【解决方案1】:

您可以将addKeyCommand(_:)removeKeyCommand(_:) 方法用于UIViewControllers,而不是使keyCommands 成为计算属性。像这样子类化您的文本字段:

class PowerTextField: UITextField {
    var enableKeyCommands: (Bool->())?

    override func becomeFirstResponder() -> Bool {
        super.becomeFirstResponder()
        enableKeyCommands?(false)
        return true
    }

    override func resignFirstResponder() -> Bool {
        super.resignFirstResponder()
        enableKeyCommands?(true)
        return true
    }
}

然后在你的UIViewController:

override func viewDidLoad() {
    super.viewDidLoad()

    // globalKeyCommands is a view controller property

    // Add key commands to be on by default
    for command in globalKeyCommands {
        self.addKeyCommand(command)
    }

    // Configure text field to callback when 
    // it should enable key commands
    powerTextField.enableKeyCommands = { [weak self] enabled in
        guard let commands = self?.globalKeyCommands else {
            return
        }
        if enabled {
            for command in globalKeyCommands {
                self?.addKeyCommand(command)
            }
        } else {
            for command in globalKeyCommands {
                self?.removeKeyCommand(command)
            }
        }
    }
}

您可以为UITextField 设置UIViewController 将采用的委托协议,而不是由UIViewController 配置的可选存储过程。

另外,如果您需要在弹出UIAlertController 时停止这些键命令,您可以创建一个UIAlertController 的子类,它实现viewWillAppear(_:)viewWillDisappear(_:) 事件方法,类似于您实现becomeFirstResponder() 的方式和resignFirstResponder(),通过调用由主视图控制器在其viewDidLoad() 期间配置的enableKeyCommands(_:) 可选存储过程。

至于为什么会发生这种情况的解释,也许最可能的解释是它是一个错误。我不确定为什么这在您的测试中是不规则的。我认为您可以尝试隔离它在什么条件下有效或无效。不清楚为什么这只会发生在蓝牙键盘上,但是当您开始引入无线技术时,可能会出现很多边缘情况。

【讨论】:

  • 我应该把第二个代码块放在哪里?如果我只是将它单独放在 UIViewController 中的任何位置,它会显示“错误:预期声明”。
  • @JohnRamos 您可以在多个位置放置它,这取决于您的应用程序,但我猜您可能应该将它放在 viewDidLoad() 中
  • 好的,所以我将第一块代码放在计算器视图控制器文件的底部,在我的 UIViewController 类中,但不在其他任何东西中,第二块代码在 viewdidload 中,以及所有我在 viewdidload 中的其他 UIKeyCommand 代码,我的项目构建失败并出现错误“Segmentation Fault 11”。
  • @JohnRamos 第一个代码块是文本字段的类声明的一部分。如果您已经有它的子类,请在此处添加代码。如果没有,请创建一个单独的文件来声明这个子类。确保在代码中或在 Interface Builder 中实例化文本字段时使用此子类。第二个代码块放在 viewDidLoad() 中。将此视为使用回调配置自定义文本字段。在 viewDidLoad() 中,您还需要将所有键命令添加到视图控制器,以便它们默认处于打开状态。
  • 当我将第一个代码块放入一个新文件时,第二个代码块无法读取它。第二块第一行有错误:Value of type 'UITextView' has no member 'enableKeyCommands'
【解决方案2】:

所以我遇到了同样的问题,并找到了一个非常好的和简单的解决方案。

我想办法弄清楚当前的 firstResponder 是否是文本字段或类似字段。

extension UIResponder {
    private weak static var _currentFirstResponder: UIResponder? = nil

    public static var isFirstResponderTextField: Bool {
        var isTextField = false
        if let firstResponder = UIResponder.currentFirstResponder {
            isTextField = firstResponder.isKind(of: UITextField.self) || firstResponder.isKind(of: UITextView.self) || firstResponder.isKind(of: UISearchBar.self)
        }

        return isTextField
    }

    public static var currentFirstResponder: UIResponder? {
        UIResponder._currentFirstResponder = nil
        UIApplication.shared.sendAction(#selector(findFirstResponder(sender:)), to: nil, from: nil, for: nil)
        return UIResponder._currentFirstResponder
    }

    @objc internal func findFirstResponder(sender: AnyObject) {
        UIResponder._currentFirstResponder = self
    }
}

然后简单地使用这个布尔值来确定在覆盖 keyCommands var 时要返回哪些 UIKeyCommands:

    override var keyCommands: [UIKeyCommand]? {
        var commands = [UIKeyCommand]()

        if !UIResponder.isFirstResponderTextField {
            commands.append(UIKeyCommand(title: "Some title", image: nil, action: #selector(someAction), input: "1", modifierFlags: [], propertyList: nil, alternates: [], discoverabilityTitle: "Some title", attributes: [], state: .on))

        }

        commands.append(UIKeyCommand(title: "Some other title", image: nil, action: #selector(someOtherAction), input: "2", modifierFlags: [.command], propertyList: nil, alternates: [], discoverabilityTitle: "Some title", attributes: [], state: .on))

        return commands
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-06
    • 2016-06-10
    • 2015-05-14
    • 2014-05-11
    • 1970-01-01
    • 2017-04-17
    • 2013-09-07
    • 1970-01-01
    相关资源
    最近更新 更多