はじめに
私はmacOSのネイティブアプリケーションをよく作るのですが、アプリの審査申請用にスクリーンショットを撮る時に、毎回デスクトップを綺麗にするのが面倒でした。そこで、デスクトップを一時的に綺麗に見せかけるアプリをささっと作ってみました。
完成して「やったー」と思ったら、全く同じ機能を持ったアプリがAppStoreにて配信されていましたorz... HiddenMeというアプリです。マルチディスプレイ対応にするには課金が必要っぽいですね。
成果物
GitHubにソースと.dmg
のダウンロードリンクをあげてあります。
https://github.com/Kyome22/SecretDesktop
メニューバーに常駐しているアイコンからHide Desktop
を押すことでトグルできます。デフォルトでマルチディスプレイに対応しています。
動作確認するのが面倒だったので.dmg
での配布版はmacOS Mojave 10.14
以上でしか動きません。Mojave以下で動かしたい人はGitHubからソースダウンロードしてターゲットのバージョンを下げてみてください。
ポイント
要はアイコンの一枚上のレイヤーにNSImageView
で背景の画像を貼って隠しているだけなのですが、背景画像をとってくるところのTipsが少なくて実装厄介でした。
extension NSImage {
static func desktopPicture(targetPoint: CGPoint) -> NSImage? {
guard let rawList: NSArray = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) else {
return nil
}
var windowList = rawList as! [NSDictionary]
windowList = windowList.filter { (data) -> Bool in
if let owner = data[kCGWindowOwnerName] as? String, owner == "Dock" {
if let name = data[kCGWindowName] as? String, name.contains("Desktop Picture") {
return true
}
}
return false
}
for window in windowList {
let bounds = window[kCGWindowBounds] as! NSDictionary
let X = bounds["X"] as! CGFloat
let Y = bounds["Y"] as! CGFloat
let W = bounds["Width"] as! CGFloat
let H = bounds["Height"] as! CGFloat
let rect = CGRect(x: X, y: Y, width: W, height: H)
if rect.contains(targetPoint) {
let id = window[kCGWindowNumber] as! UInt32
guard let cgImage = CGWindowListCreateImage(rect, .optionIncludingWindow, id, .boundsIgnoreFraming) else {
break
}
let nsImage = NSImage(cgImage: cgImage, size: rect.size)
nsImage.resizingMode = NSImage.ResizingMode.stretch
return nsImage
}
}
return nil
}
}
CGWindowListCopyWindowInfo
というのから芋づる式にデスクトップのWindow
の情報を抜いてきています。
あと、アイコンの一枚上のレイヤーにNSWindow
を配置するというのはこんな感じでやりました。
self.window!.level = NSWindow.Level(Int(CGWindowLevelForKey(CGWindowLevelKey.normalWindow)) - 1)
通常のアプリケーションのWindowを配置すべきレイヤーの一階層下に配置しています。