Tha*_*esy 4 macos screenshot objective-c swift
注意:这个问题是故意很一般(比如他们的Objective-C和SWIFT代码示例要求),因为它是用来记录如何捕获窗口的截图在MacOS尽量通俗易懂越好.
我想在Objective-C/Swift代码中捕获macOS窗口的屏幕截图.我知道这是可能的,因为有多种方法可以在macOS上截取屏幕截图(⇧⌘4,Grab实用程序,screencapture在命令行中,...),但我不知道如何在我自己的代码中执行此操作.理想情况下,我可以指定一个特定应用程序的窗口,然后将其捕获到一个NSImage或者CGImage我可以处理并显示给用户或存储在文件中.
Tha*_*esy 16
通过Quartz Window Services(Core Graphics框架的一个工具)可以在macOS上进行屏幕捕获.我们的主要功能就是在这里CGWindowListCreateImage,该"返回一个基于窗口的动态生成的列表上的合成图像",或者,换句话说,发现根据指定的标准窗口和创建具有每个内容的图像.完善!其声明如下:
CGImageRef CGWindowListCreateImage(CGRect screenBounds,
CGWindowListOption listOption,
CGWindowID windowID,
CGWindowImageOption imageOption);
Run Code Online (Sandbox Code Playgroud)
因此,为了捕获屏幕上的一个特定窗口,我们需要它的窗口ID(CGWindowID).为了检索它,我们首先需要一个系统上所有可用窗口的列表.我们得到了这个CGWindowListCopyWindowInfo,它采用CGWindowListOptions和相应的CGWindowID一起,选择要包含在结果列表中的窗口.为了获得所有窗口,我们分别指定kCGWindowListOptionAll和kCGNullWindowID.此外,如果您还没有弄明白,这是一个C API,所以我们将使用桥接转换来处理更友好的Objective-C容器而不是Core Foundation容器.
Objective-C的:
NSArray<NSDictionary*> *windowInfoList = (__bridge_transfer id)
CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID);
Run Code Online (Sandbox Code Playgroud)
迅速:
let windowInfoList = CGWindowListCopyWindowInfo(.optionAll, kCGNullWindowID)!
as NSArray
Run Code Online (Sandbox Code Playgroud)
从这里开始,我们需要将我们windowInfoList的窗口过滤到我们想要的特定窗口.我们希望首先按应用程序过滤.为此,我们需要选择应用程序的进程ID.我们可以NSRunningApplication用来完成这个:
Objective-C的:
NSArray<NSRunningApplication*> *apps =
[NSRunningApplication runningApplicationsWithBundleIdentifier:
/* Bundle ID of the application, e.g.: */ @"com.apple.Safari"];
if (apps.count == 0) {
// Application is not currently running
puts("The application is not running");
return; // Or whatever
}
pid_t appPID = apps[0].processIdentifier;
Run Code Online (Sandbox Code Playgroud)
迅速:
let apps = NSRunningApplication.runningApplications(withBundleIdentifier:
/* Bundle ID of the application, e.g.: */ "com.apple.Safari")
if apps.isEmpty {
// Application is not currently running
print("The application is not running")
return // Or whatever
}
let appPID = apps[0].processIdentifier
Run Code Online (Sandbox Code Playgroud)
随着appPID在手,我们现在可以继续前进,我们的窗口信息列表进行过滤,下降到仅具有匹配所有者PID窗口:
Objective-C的:
NSMutableArray<NSDictionary*> *appWindowsInfoList = [NSMutableArray new];
for (NSDictionary *info in windowInfoList) {
if ([info[(__bridge NSString *)kCGWindowOwnerPID] integerValue] == appPID) {
[appWindowsInfoList addObject:info];
}
}
Run Code Online (Sandbox Code Playgroud)
迅速:
var appWindowsInfoList = [NSDictionary]()
for info_ in windowInfoList {
let info = info_ as! NSDictionary
if (info[kCGWindowOwnerPID as NSString] as! NSNumber).intValue == appPID {
appWindowsInfoList.append(info)
}
}
Run Code Online (Sandbox Code Playgroud)
我们可以通过测试信息字典的其他键来完成上面的额外过滤 - 例如,通过名称(kCGWindowName),或者窗口是否在屏幕上(kCGWindowIsOnscreen) - 但是现在,我们只需要在第一个窗口中列表:
Objective-C的:
NSDictionary *appWindowInfo = appWindowsInfoList[0];
CGWindowID windowID = [appWindowInfo[(__bridge NSString *)kCGWindowNumber] unsignedIntValue];
Run Code Online (Sandbox Code Playgroud)
斯威夫特:
let appWindowInfo: NSDictionary = appWindowsInfoList[0];
let windowID: CGWindowID = (appWindowInfo[kCGWindowNumber as NSString] as! NSNumber).uint32Value
Run Code Online (Sandbox Code Playgroud)
我们有窗口ID!现在,我们还有什么需要再次接听电话?
Run Code Online (Sandbox Code Playgroud)CGImageRef CGWindowListCreateImage(CGRect screenBounds, CGWindowListOption listOption, CGWindowID windowID, CGWindowImageOption imageOption);
首先,我们需要一个screenBounds捕获.根据文档,我们可以指定CGRectNull此参数尽可能紧密地包含所有指定的窗口.适合我.
其次,我们必须指定我们如何选择我们的窗口listOption.在我们的调用中CGWindowListCopyWindowInfo,我们实际上使用过其中一个,但是我们想要系统上的所有窗口; 在这里,我们只需要一个,所以我们将指定kCGWindowListOptionIncludingWindow,与其文档页面相反,它本身是有意义的CGWindowListCreateImage,因为它指定了我们传递的窗口,并且只指定了我们传递的窗口.
第三,我们将我们windowID作为我们想要捕获的窗口传递.
第四,也是最后,我们可以CGWindowImageOption用imageOption参数指定s .这些会影响所得图像的外观; 你可以通过按位OR组合它们.完整列表在这里,但常见的包括kCGWindowImageDefault,它捕获窗口的内容及其框架和阴影,或者kCGWindowImageBoundsIgnoreFraming仅捕获内容,并且kCGWindowImageBestResolution以可用的最佳分辨率捕获窗口的内容,而不管实际大小(并且,取决于窗口,可能相当大),或者kCGWindowImageNominalResolution,它以屏幕上的当前大小捕获窗口.在这里,我只使用kCGWindowImageBoundsIgnoreFraming和kCGWindowImageNominalResolution捕获与屏幕上相同大小的内容.
Aaand,鼓声请:
Objective-C的:
CGImageRef windowImage =
CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow,
windowID, kCGWindowImageBoundsIgnoreFraming|
kCGWindowImageNominalResolution);
// NOTE: windowImage may be NULL if the capture failed
Run Code Online (Sandbox Code Playgroud)
迅速:
let windowImage: CGImage? =
CGWindowListCreateImage(.null, .optionIncludingWindow, windowID,
[.boundsIgnoreFraming, .nominalResolution])
Run Code Online (Sandbox Code Playgroud)