首页 > 解决方案 > 等待应用程序窗口打开后再继续

问题描述

我想在 Swift 中打开一个应用程序并等待应用程序窗口打开,然后再继续。

我正在使用以下方法打开应用程序:

func openApp(appPath: String, callback: @escaping (NSRunningApplication) -> Void) {
    let conf = NSWorkspace.OpenConfiguration();
    NSWorkspace.shared.openApplication(
        at: URL.init(fileURLWithPath: appPath),
        configuration: conf
    ) { app, err in
        callback(app!)
    }
}

// relevant code inside another function:
if (app == nil) 
    guard let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleId) else { throw MyError.runtimeError("no app path")}
    do {
        let group = DispatchGroup()
            
            
        try app = NSWorkspace.shared.launchApplication(at: URL.init(fileURLWithPath: appPath), options: [], configuration: [:])
            
        group.enter()

        openApp(appPath: appPath) { (newApp) in
            app = newApp;
            group.leave();
        }
            
        group.wait();
            
    } catch {
        throw MyError.runtimeError("failed to launch");
    }
}

在稍后阶段,我需要使用该应用程序的窗口:

extension AXUIElement {

    func getAttribute<T>(key: String) -> T {
        var ptr: AnyObject?
        AXUIElementCopyAttributeValue(self, "AX\(key)" as CFString, &ptr)
        if key == "Size" ||  key == "Position" {
            let val = ptr as! AXValue
            return val.toValue()
        }
        return ptr as! T
    }

    func setAttribute<T: AnyObject>(key: String, value: T) {
        AXUIElementSetAttributeValue(self, "AX\(key)" as CFString, value)
    }
}

let axApp = AXUIElementCreateApplication(app!.processIdentifier)
let windows: [AXUIElement] = axApp.getAttribute(key: kAXWindowsAttribute) as [AXUIElement];

但是当应用程序启动时,此代码会在实际窗口打开之前执行。所以我收到以下错误:

Could not cast value of type 'Swift.Optional<Swift.AnyObject>' (0x7fff8e6b2cd8) to 'Swift.Array<__C.AXUIElementRef>' (0x7fff8e6b2c78).

如何在执行代码获取窗口实例之前等待窗口打开

标签: swiftmacos

解决方案


我最近遇到了同样的问题。我想在打开应用程序后调整窗口大小并重新定位窗口位置。这是我在 Swift 5 中的解决方案。

NSWorkspace.shared.open(fileURLs,
                            withApplicationAt: appUrl,
                            configuration: config) { runningApp, error in
        
        guard error == nil else { return }
        
        while true {

            //wait for application actual finished launch
            Thread.sleep(forTimeInterval: 0.1)
            
            guard let isFinish = runningApp?.isFinishedLaunching,
                  let isActivate = runningApp?.isActive,
                  isFinish && isActivate else { continue }
            
            //Your code here

            break
        }
        
    }
    
}

推荐阅读