代码之家  ›  专栏  ›  技术社区  ›  Mark A. Donohoe

您可以向AXUIElement发送选择器吗?

  •  0
  • Mark A. Donohoe  · 技术社区  · 3 年前

    使现代化

    不确定可访问性框架是如何实现的。它似乎只允许你与其他应用程序交互,而不允许你以编程方式向它们发送选择器。

    有关更多上下文,macOS具有用户定义的密钥绑定( ~/Library/KeyBindings/DefaultKeyBinding.dict )在这里,您可以指定一个键绑定和一个选择器,当您按下它时,它将被发送到任何有焦点的应用程序/控件。这在操作系统级别发生。

    想知道是否有一种方法可以通过编程方式做类似的事情,从我自己的应用程序发送选择器(当然,前提是它被请求了适当的权限)。我认为这就是Privacy->辅助功能设置,但看起来我可能走错了路。

    原问题。

    只是摆弄可访问性框架,尝试将选择器发送到前台应用程序中的第一个响应者(即焦点元素),无论该应用程序是我的还是其他什么。

    为了测试这一点,我正在尝试发送 moveToBeginningOfLine: 到具有焦点的任何文本字段。如果它收到它,光标应该移动到当前行的开头。

    现在,在我自己的应用程序中将它发送给第一响应者很容易。您使用 NSApplication.sendAction 目标为“零”,就像我的 sendToFirstResponder 如下所示。但这不适用于其他应用程序,只能用于您自己的应用程序。

    要与其他应用程序通话,您必须使用 AXUIAutomation (并确保你的应用程序已被授予使用权限,我的应用程序也被授予了使用权限),即使它看起来像是我成功地获得了焦点 AXUIElement 在下面的代码中,它似乎从未像我所期望的那样接收到选择器,并且调用报告为失败。

    这是我正在玩的代码。。。

    选择器扩展:

    extension Selector {
    
        @discardableResult
        func sendToFirstResponder(from sender: Any? = nil) -> Bool {
            return NSApplication.shared.sendAction(self, to: nil, from: sender)
        }
    
        @discardableResult
        func sendToFrontmostApp() -> Bool {
    
            let systemWideElement = AXUIElementCreateSystemWide()
            var untypedFocusedElement : AnyObject?
    
            let getFocusedElementResult = AXUIElementCopyAttributeValue(systemWideElement, kAXFocusedUIElementAttribute as CFString, &untypedFocusedElement)
            guard getFocusedElementResult == .success else {
                print("Couldn't get the focused element. \(getFocusedElementResult)")
                return false
            }
    
            let focusedElement = untypedFocusedElement as! AXUIElement
            let action = description as CFString
    
            let performActionResult = AXUIElementPerformAction(focusedElement, action)
            guard performActionResult == .success else {
                print("Couldn't perform the action '\(action)'. \(performActionResult)")
                return false
            }
    
            return true
        }
    }
    

    测试代码:

    // If you send this to a TextField, the cursor moves to the beginning of the line
    let selectorName = "moveToBeginningOfLine:"
    let selector = Selector(selectorName)
    
    // This works if the current app is focused and a TextField is the first responder
    let result1 = selector.sendToFirstResponder()
    print("Result 1:", result1)
    
    // This always fails on the 'perform action' step, even if there's a focused TextField in the frontmost app
    let result2 = selector.sendToFrontmostApp()
    print("Result 2:", result2)
    

    这可能吗?

    0 回复  |  直到 3 年前
        1
  •  -1
  •   shubh_singh    3 年前

    否,不能将选择器发送到 AXUIElement 直接在Swift 5中。这个 AXUI元素 辅助功能框架中的类提供了一种与应用程序的辅助功能层次结构交互的方式,但它不允许向UI元素发送任意消息或选择器。

    但是,您可以使用 AXUIElementPerformAction 对执行某些操作的函数 AXUI元素 。此函数采用两个参数: AXUI元素 对象,以及指定要执行的操作类型的常量。可用操作在 AXActionConstants 枚举。

    以下是如何使用的示例 AXUI元素性能操作 要对按钮元素执行“按下”操作:

    import Cocoa
    
    let buttonElement: AXUIElement = ... // Obtain the AXUIElement for the button
    
    var actionResult: AnyObject?
    let action = kAXPressAction as CFString
    if AXUIElementPerformAction(buttonElement, action, &actionResult) != .success {
        print("Failed to perform press action on button")
    }
    

    在本例中 AXUI元素性能操作 函数的调用 buttonElement 对象和 kAXPressAction 常量,指定要执行的“按下”操作。如果操作成功执行 actionResult 参数将包含操作的结果(如果有的话)。

    请注意,并非所有 AXUI元素 s支持所有操作,因此您应该查看文档中要与之交互的特定元素,看看有哪些操作可用。