如何获取 UI 个 Window 的元素? Swift

How Get UI Elements of a Window? Swift

如何将此 AppleScript 代码翻译成 Swift?

tell application "System Events" to tell process "Safari" to get UI elements of first window

我已经到达“Safari”的第一个 window,但我不知道如何获得 UI 元素

let pid = NSWorkspace.shared.runningApplications.first(where: {[=14=].localizedName == "Safari"})?.processIdentifier
let appRef = AXUIElementCreateApplication(pid!)
var windows: AnyObject?
_ = AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute as CFString, &windows)
if let firstWindow = (windows as? [AXUIElement])?.first{
    print(firstWindow)
}

您可以使用相同的 AXUIElementCopyAttributeValue() 来查询 window 的 children,以及 children 的 children,等等.

我自己喜欢在可能的情况下在现有类型上添加扩展,以便更清晰:

extension AXUIElement {
    var children: [AXUIElement]? {
        var childrenPtr: CFTypeRef?
        AXUIElementCopyAttributeValue(appRef, kAXChildrenAttribute as CFString, &childrenPtr)
        return childrenPtr as? [AXUIElement]
    }
}

然后您可以在您的代码中使用它:

if let firstWindow = (windows as? [AXUIElement])?.first{
    print(firstWindow, firstWindow.children)
}

您可以更进一步,通过向扩展添加更多功能来简化 AXUIElement 消费者代码:

extension AXUIElement {
    
    static func from(pid: pid_t) -> AXUIElement { AXUIElementCreateApplication(pid) }
    
    var windows: [AXUIElement]? { value(forAttribute: kAXWindowsAttribute) }
    
    var children: [AXUIElement]? { value(forAttribute: kAXChildrenAttribute) }
    
    func value<T>(forAttribute attribute: String) -> T? {
        var attributeValue: CFTypeRef?
        AXUIElementCopyAttributeValue(self, attribute as CFString, &attributeValue)
        return attributeValue as? T
    }
}

let pid = ...
let app = AXUIElement.from(pid: pid!)
if let firstWindow = app.windows?.first{
    print(firstWindow, firstWindow.children)
}