swift 是否可以将选择器发送到最前面的应用程序的第一响应者(即#21518;的重点,经许可?

egmofgnx  于 2023-05-16  发布在  Swift
关注(0)|答案(1)|浏览(150)

更新

不确定可访问性框架是否适合这里。似乎它只允许你与其他应用程序交互,但不能做像编程发送选择器给他们这样的事情。
有关更多上下文,macOS具有用户定义的键绑定(~/Library/KeyBindings/DefaultKeyBinding.dict),您可以在其中指定键绑定和选择器,当您按下它时,该选择器将发送到具有焦点的任何应用/控件。这发生在OS级别。
想知道是否有一种方法可以通过编程来做类似的事情,从我自己的应用程序发送选择器(当然,前提是它需要适当的权限来这样做)。

原问题。

只是玩弄可访问性框架,试图向第一响应者发送选择器(即焦点元素),不管该应用程序是我的还是其他东西。
为了测试这一点,我尝试将moveToBeginningOfLine:发送到具有焦点的任何文本字段。如果它接收到它,光标应该移动到当前行的开头。
现在,在我自己的应用程序中将其发送给第一响应者很容易。你使用NSApplication.sendAction,目标为'nil',就像下面我的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)

这可能吗?

5n0oy7gb

5n0oy7gb1#

不可以,在Swift 5中不能直接向AXUIElement发送选择器。Accessibility框架中的AXUIElement类提供了一种与应用程序的可访问性层次结构进行交互的方法,但它不允许您向UI元素发送任意消息或选择器。
但是,您可以使用AXUIElementPerformAction函数在AXUIElement上执行某些操作。此函数采用两个参数:要对其执行操作的AXUIElement对象,以及一个指定要执行的操作类型的常量。可用的操作在AXActionConstants枚举中定义。
下面是一个如何使用AXUIElementPerformAction在button元素上执行“press”操作的示例:

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")
}

在本例中,使用buttonElement对象和kAXPressAction常量调用AXUIElementPerformAction函数,该常量指定要执行的“press”操作。如果操作成功执行,actionResult参数将包含操作的结果(如果有)。
请注意,并非所有AXUIElement都支持所有操作,因此您应该查看文档中您想要与之交互的特定元素,以了解哪些操作可用。

相关问题