2017-12-30 10 views
1

참고 : 가능한 한 접근하기 쉬운 macOS에서 창 스크린 샷을 캡처하는 방법을 문서화하기 위해이 질문은 의도적으로 매우 일반적입니다 (예 : Objective-C 및 Swift 코드 예가 ​​필요합니다).macOS 창의 캡쳐 화면

Objective-C/Swift 코드로 macOS 창의 스크린 샷을 캡처하고 싶습니다. MacOS (⇧⌘4, 그래버 유틸리티, 명령 행의 screencapture ...)에서 스크린 샷을 찍는 방법이 다양하기 때문에 이것이 가능하다는 것을 알고 있지만, 내 자신의 코드에서 어떻게해야하는지 모르겠습니다. 이상적으로는 특정 응용 프로그램의 창을 지정한 다음 NSImage 또는 CGImage으로 캡처하여 처리하고 사용자에게 표시하거나 파일로 저장할 수 있습니다.

+1

당신 때문에 실제로 당신이 직접 대답 해줄 때 질문을하는 척하십시오. –

+0

@ElTomato https://stackoverflow.com/help/self-answer – ThatsJustCheesy

+0

내 사과 ... –

답변

1

코어 그래픽 프레임 워크의 기능인 Quartz Window Services을 통해 macOS에서 화면 캡처가 가능합니다. 우리의 주요 기능은 CGWindowListCreateImage입니다. "동적으로 생성 된 창 목록을 기반으로 합성 이미지를 반환합니다."즉, 지정된 기준에 따라 창을 찾고 각각의 내용으로 이미지를 만듭니다. 완전한!

CGImageRef CGWindowListCreateImage(CGRect screenBounds, 
            CGWindowListOption listOption, 
            CGWindowID windowID, 
            CGWindowImageOption imageOption); 

그래서, 화면에 하나 개의 특정 창을 캡처하기 위해, 우리는 윈도우 ID (CGWindowID)를해야합니다 다음과 같이 선언이다. 검색을 위해 먼저 시스템에서 사용할 수있는 모든 창 목록이 필요합니다. CGWindowListCopyWindowInfo을 통해이를 얻으므로 CGWindowListOption 초이고 해당하는 CGWindowID이 함께 결과 목록에 포함 할 창을 선택합니다. 모든 창을 얻으려면 kCGWindowListOptionAllkCGNullWindowID을 각각 지정하십시오. 또한 이미 알아 내지 못했다면 C API이므로 브리징 캐스트를 사용하여 Core Foundation보다 더 친숙한 Objective-C 컨테이너로 작업 할 것입니다.

목표 - C :

NSArray<NSDictionary*> *windowInfoList = (__bridge_transfer id) 
    CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID); 

스위프트 : 여기에서

let windowInfoList = CGWindowListCopyWindowInfo(.optionAll, kCGNullWindowID)! 
    as NSArray 

, 우리는 필터링해야 우리 windowInfoList 우리가 원하는 특정 창 아래로. 먼저 응용 프로그램별로 필터링 할 가능성이 있습니다. 그렇게하기 위해서는 우리가 선택한 응용 프로그램의 프로세스 ID가 필요합니다.

목표 - 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; 

스위프트 : 우리는이 달성하기 위해 NSRunningApplication을 사용할 수 있습니다 손에 appPID으로

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 

을, 우리는 지금 가서 아래로 필터링 할 수 있습니다 우리의 일치하는 소유자가있는 창에만 창 정보 목록 표시 :

목표 - C :

NSMutableArray<NSDictionary*> *appWindowsInfoList = [NSMutableArray new]; 
for (NSDictionary *info in windowInfoList) { 
    if ([info[(__bridge NSString *)kCGWindowOwnerPID] integerValue] == appPID) { 
     [appWindowsInfoList addObject:info]; 
    } 
} 

스위프트 : 우리는 정보의 다른 키를 테스트하여 위의 추가 필터링을 할 수 있었다

var appWindowsInfoList = [NSDictionary]() 
for info_ in windowInfoList { 
    let info = info_ as! NSDictionary 
    if (info[kCGWindowOwnerPID as NSString] as! NSNumber).intValue == appPID { 
     appWindowsInfoList.append(info) 
    } 
} 

사전 - 예를 들어, 이름 (kCGWindowName)에 의해, 또는 창이 화면에 표시되는지 (kCGWindowIsOnscreen) - 현재로서는 목록의 첫 번째 창을 가져옵니다.

목표 - C :

NSDictionary *appWindowInfo = appWindowsInfoList[0]; 
CGWindowID windowID = [appWindowInfo[(__bridge NSString *)kCGWindowNumber] unsignedIntValue]; 

스위프트 :

let appWindowInfo: NSDictionary = appWindowsInfoList[0]; 
let windowID: CGWindowID = (appWindowInfo[kCGWindowNumber as NSString] as! NSNumber).uint32Value 

그리고 우리는 우리의 창 ID를 가지고! 이제 그 전화에 또 뭐가 필요 했나요?

CGImageRef CGWindowListCreateImage(CGRect screenBounds, 
            CGWindowListOption listOption, 
            CGWindowID windowID, 
            CGWindowImageOption imageOption); 

먼저

, 우리는 캡처하는 screenBounds이 필요합니다. the documentation에 따르면이 매개 변수에 대해 CGRectNull을 지정하여 지정된 모든 창을 최대한 단 ~ 으로 지정할 수 있습니다. 나를 위해 일합니다.

두 번째로, 우리는 listOption으로 창을 선택하는 방법을 지정해야합니다. 실제로 우리는 CGWindowListCopyWindowInfo을 호출 할 때 이전에이 중 하나를 사용했지만 시스템의 모든 창을 원했습니다. 여기, 우리는 하나를 원하는, 그래서 우리는, 은 우리가 전달하는 윈도우를 지정한다는 점에서 CGWindowListCreateImage에 대한 자체에 의미입니다 its documentation page는 달리, kCGWindowListOptionIncludingWindow, 및 우리가 통과 창을 지정할 수 있습니다.

세 번째로 캡처 할 창으로 windowID을 전달합니다.

마지막으로 imageOption 매개 변수를 사용하여 CGWindowImageOption을 지정할 수 있습니다. 이는 결과 이미지의 모양에 영향을줍니다. 비트 OR로 결합 할 수 있습니다.전체 목록은 here이지만 일반적인 내용은 프레임과 그림자와 함께 창 내용을 캡처하는 kCGWindowImageDefault 또는 내용 만 캡처하는 kCGWindowImageBoundsIgnoreFraming과 가능한 한 최상의 해상도로 창 콘텐츠를 캡처하는 kCGWindowImageBestResolution 중 하나를 포함합니다. 실제 크기 (및 창에 따라 상당히 클 수 있음) 또는 kCGWindowImageNominalResolution. 화면의 현재 크기로 창을 캡처합니다. 여기서는 kCGWindowImageBoundsIgnoreFramingkCGWindowImageNominalResolution을 사용하여 화면과 동일한 크기로만 콘텐츠를 캡처합니다.

Aaand, 제발 드럼 롤 :

목표 - C :

CGImageRef windowImage = 
    CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, 
          windowID, kCGWindowImageBoundsIgnoreFraming| 
          kCGWindowImageNominalResolution); 
// NOTE: windowImage may be NULL if the capture failed 

스위프트 : I 오프 주제로이 질문을 닫으 투표 해요

let windowImage: CGImage? = 
    CGWindowListCreateImage(.null, .optionIncludingWindow, windowID, 
          [.boundsIgnoreFraming, .nominalResolution])