MacOS - 按先前最常用的方式订购正在运行的应用程序

时间:2017-10-26 06:01:17

标签: objective-c swift macos

使用Swift(或Objective-C)我想获取当前在macOS上运行的应用列表,按照最近的使用顺序。这将是cmd-tab的顺序在Mac上显示应用程序图标。

以下内容为我提供了应用程序,但不是按照我想要的顺序。

let apps = NSWorkspace.shared.runningApplications

documentation for the .runningApps property中,它说:

  

数组的顺序未指定,但它是稳定的,这意味着   特定应用程序的相对顺序不会在多个方面发生变化   调用runningApplications。

有没有办法按所需顺序对应用进行排序/获取?

编辑:

'brimstone'在答案中建议的方法似乎很有希望,但CGWindowListCopyWindowInfo只有在指定CGWindowListOption.optionOnScreenOnly时才会以前后顺序返回窗口。但是,在这种情况下,只返回当前空格中的窗口。

cmd-tab但是能够列出所有空间的应用。

有没有人知道其他任何方式?难道不应该有更直接/更简单的方法吗?

2 个答案:

答案 0 :(得分:1)

所以我看着cmd-tab,我想到了一种模仿行为的方法是通过窗口层次结构。 CGWindowListOption将按层次结构的顺序返回屏幕上的窗口列表 - 因此最新的应用程序将是第一个。这可以解决您订购runningApps财产的问题。

let options = CGWindowListOption(arrayLiteral: CGWindowListOption.excludeDesktopElements, CGWindowListOption.optionOnScreenOnly)
let windowList = CGWindowListCopyWindowInfo(options, CGWindowID(0))
let windows = windowList as NSArray? as! [[String: AnyObject]]

然后,您可以遍历infoList并检索所需的任何数据,例如每个应用程序的名称。

for window in windows {
    let name = window[kCGWindowOwnerName as String]!
    print(name)
}

如果您仍然需要NSRunningApplication变量,则可以将窗口的所有者PID与应用程序的PID匹配。

let id = pid_t(window[kCGWindowOwnerPID as String]! as! Int)
let app = apps.filter { $0.processIdentifier == id } .first
app.hide() //Or whatever your desired action is

对我来说,这返回了我运行的所有应用程序,其顺序与CMD-Tab显示的顺序相同。但是,此方法还返回了一些进程,这些进程是菜单栏或后台进程中的项目,例如SystemUIServerSpotlight

答案 1 :(得分:1)

您可以从NSWorkspaceDidDeactivateApplicationNotification订阅通知[NSWorkspace sharedWorkspace].notificationCenter,并将其放入字典bundleId和时间戳,然后使用它对数组进行排序。缺点是应用程序在开始监听通知之前对使用的应用程序一无所知。

@property (strong) NSMutableDictionary *appActivity;

...

- (instancetype)init {
    if (self = [super init]) {
        self.appActivity = [NSMutableDictionary dictionary];
        [self setupNotifications];
    }
    return self;
}

- (void)setupNotifications {
    NSNotificationCenter *workspaceNotificationCenter = [NSWorkspace sharedWorkspace].notificationCenter;
    [workspaceNotificationCenter addObserver:self selector:@selector(deactivateApp:) name:NSWorkspaceDidDeactivateApplicationNotification object:nil];
}

- (void)deactivateApp:(NSNotification *)notification {
    NSRunningApplication *app = notification.userInfo[NSWorkspaceApplicationKey];
    if (app.bundleIdentifier) {
        self.appActivity[app.bundleIdentifier] = @([[NSDate date] timeIntervalSince1970]);
    }
}

...

    NSArray<NSRunningApplication *> *apps = [[NSWorkspace sharedWorkspace] runningApplications];

    NSComparator sortByLastAccess = ^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
        NSRunningApplication *item1 = obj1;
        NSRunningApplication *item2 = obj2;
        NSComparisonResult result = NSOrderedSame;
        if (item1.bundleIdentifier && item2.bundleIdentifier) {
            NSNumber *ts1 = self.appActivity[item1.bundleIdentifier];
            NSNumber *ts2 = self.appActivity[item2.bundleIdentifier];
            if (ts1 && ts2) {
                result = [ts2 compare:ts1];
            } else if (ts1) {
                result = NSOrderedAscending;
            } else if (ts2) {
                result = NSOrderedDescending;
            }
        }
        return result;
    };

    apps = [apps sortedArrayUsingComparator:sortByLastAccess];