I'm needing to do something full-screen app, which would usually not be the problem. The problem now is that I need to have the user's desktop, but without icons, as the background of my full screen window, much like Launchpad in 10.7. I've gotten a reference to the desktop background in AppleScript:
tell application "Finder"
set a to desktop picture
end tell
This gives me something like this: document file "100930-F-7910D-001.jpg" of folder "Pictures" of folder "Fighter Jet Stuff" of folder "Desktop" of folder "tristan" of folder "Users" of startup disk of application "Finder" which I just could not figure out to get into a regular path.
I tried doing set a to desktop picture as POSIX path but that throws up on me. Any idea of how I could do this in Cocoa, using the above Applescript to get the path, or even better, without an Applescript? I'd like to not rely on the specific format of any plist that might store this info, as it has the potential to break later on. I'm thinking there might be a framework that I just don't know about...
The methods you are looking for are available in NSWorkspace.
– desktopImageURLForScreen:
– setDesktopImageURL:forScreen:options:error:
– desktopImageOptionsForScreen:
Please take a look at the documentation here: NSWorkspace Class Reference
If you needs just the current wallpaper, you can take a screenshot of it:
extension NSImage {
static func desktopPicture() -> NSImage {
let windows = CGWindowListCopyWindowInfo(
CGWindowListOption.OptionOnScreenOnly,
CGWindowID(0))! as NSArray
var index = 0
for var i = 0; i < windows.count; i++ {
let window = windows[i]
// we need windows owned by Dock
let owner = window["kCGWindowOwnerName"] as! String
if owner != "Dock" {
continue
}
// we need windows named like "Desktop Picture %"
let name = window["kCGWindowName"] as! String
if !name.hasPrefix("Desktop Picture") {
continue
}
// wee need the one which belongs to the current screen
let bounds = window["kCGWindowBounds"] as! NSDictionary
let x = bounds["X"] as! CGFloat
if x == NSScreen.mainScreen()!.frame.origin.x {
index = window["kCGWindowNumber"] as! Int
break
}
}
let cgImage = CGWindowListCreateImage(
CGRectZero,
CGWindowListOption(arrayLiteral: CGWindowListOption.OptionIncludingWindow),
CGWindowID(index),
CGWindowImageOption.Default)!
let image = NSImage(CGImage: cgImage, size: NSScreen.mainScreen()!.frame.size)
return image
}
}
Related
I have an app for ipad/iphone, now adding also mac support by Mac Catalyst. On Mac, I want to control window resizing, in order to allow only some sizes and aspects. It goes beyond simple minimal height and weight, or aspect. I want to allow user to resize window freely, but when app gets too high and narrow, I want to also seemlessly increase width, to keep some minimal aspect.
I believe that in AppKit it can be done through NSWindowDelegate.windowWillResize() (get user defined size, count required size and return it). However I am getting error "NSWindowDelegate is unavailable in Mac Catalyst" . Is it possible to achieve the result I want by Catalyst means?
Answering my own question. It is NOT possible to create own NSWindowDelegate with windowWillResize() implemented in Catalyst. However, it IS possible to create a new target only for mac, and use it as a plugin from catalyst target.
First I load mac-only plugin (using Bundle.load() ), and instantiate its principalClass. Then I get NSWindow from UIWindow, which is easy through Dynamic library. Then I pass NSWindow to method of a plugin, which then can set own NSWindowDelegate, because it does not run in catalyst.
Sample code:
guard let bundle = Bundle(url: bundleURL) else { return }
let succ = bundle.load()
if (succ) {
let macUtilsClass = bundle.principalClass! as! MacUtilsProtocol.Type
self.macUtils = macUtilsClass.init()
var dnsw: NSObject? = nil
if (ProcessInfo.processInfo.isOperatingSystemAtLeast(
OperatingSystemVersion(majorVersion: 11, minorVersion: 0, patchVersion: 0))) {
dnsw = Dynamic.NSApplication.sharedApplication.delegate.hostWindowForUIWindow(AppDelegate.ref!.window).attachedWindow
}
else {
dnsw = Dynamic.NSApplication.sharedApplication.delegate.hostWindowForUIWindow(AppDelegate.ref!.window)
}
self.macUtils.SetupMainWindow(win: dnsw!)
}
I'm trying to build a menubar application in swift. I want to set an image and text for the status item. Evidently NSStatusItem has deprecated setting a custom view since 10.10, which is fine, since I am able to set an image and text on the status item's button. However, I'm unable to set the imagePosition property for some reason and so the text and the image overlap.
This is my code:
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-1)
func applicationDidFinishLaunching(aNotification: NSNotification) {
let icon = NSImage(named: "statusIcon")
icon!.setTemplate(true) // best for dark mode
statusItem.button!.image = icon
statusItem.button!.imagePosition = ImageLeft
statusItem.button!.title = "Hello, world"
statusItem.menu = menu;
}
The problem is that Xcode gives me an error on this line:
statusItem.button!.imagePosition = ImageLeft
It says "Use of unresolved identifier 'ImageLeft'", but from what I can tell from the documentation (https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSCell_Class/index.html#//apple_ref/c/tdef/NSCellImagePosition) that is the identifier I would want to use.
Can anyone help?
I figured it out. Evidently I have much to learn about Swift syntax.
This line allows it to work:
statusItem.button!.imagePosition = NSCellImagePosition.ImageLeft
I'm completely new to coding and I'm trying to learn Swift. I'm trying to cycle through background images for an app. The images I have are named 1.jpg, 2.jpg, 3.jpg, 4.jpg. When I try run the simulator, the "super.viewDidLoad()" line is highlighted green with the comment, "Thread 1, Breakpoint 3.1." Here's my code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var imageList = [UIImage]()
for i in 1...4 {
let imageName = "\(i).jpg"
var image = UIImage(named:imageName)
imageList.append(image)
}
self.myImageView.animationImages = imageList
self.myImageView.animationDuration = 4.0
self.myImageView.startAnimating()
It sounds like you've set a breakpoint there. If there's a blue pointer to the left of the line, click it to disable it. Or, use the keyboard shortcut command-Y to disable all breakpoints.
I am working on OS X application. In the code, I would like to output a path of the current Mac OS App to a variable for future use. So later I could read the files in the same folder. Could anyone tell me the command/method in the Xcode ?
Thanks very much.
UPDATE
To be more clear, I am using xcode and create a cocoa-application.
My application is connected with applescript to control Mac software read files on the Mac. So I have to return the files' directory and name.
Actually I have no idea about what to do. So got stuck here.
To get the URL of your application, you can use:
[[NSBundle mainBundle] bundleURL]
See the NSBundle Class Reference for further information.
my 2 cents for swift 5,x:
let path = Bundle.main.bundlePath
print(path)
If You need a path to REAL exacutable: (i.e. you mast pass to shell or similar..)
let appName = AppName()
let exePath = path + "/Contents/MacOS/" + appName
print(exePath)
where AppName() is:
func AppName()->String{
let bund = Bundle.main
//print(bund)
if let displayName = bund.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String {
if displayName.count>0{
return displayName
}
}
if let name = bund.object(forInfoDictionaryKey: "CFBundleName") as? String {
return name
}
return "no AppName"
}
Is is possible to capture the Mac OS X desktop without desktop items and any windows that may be open (i.e. just the wallpaper)?
I've experimented with CGWindowListCreateImage, CGWindowListCreateImageFromArray, and CGDisplayCreateImage, but no luck.
Essentially I'm trying to capture the desktop wallpaper without using [NSWorkspace desktopImageURLForScreen:] (it's a sandboxed app without access to the file system).
You'll need to be careful to test that this is still correct, but the desktop window sits below the Finder (it's drawn by the Dock). Passing the kCGWindowListOptionOnScreenBelowWindow CGWindowListOption to CGWindowListCreateImage should get you what you want (unless something else is drawing below that level).
Otherwise, you'll need to use CGWindowListCreate and iterate through the response excluding anything that isn't drawn by the dock at the window level kCGMinimumWindowLevel + 19.
It gets kind of tricky when there are multiple screens, but hopefully this information is enough for you to do what you need.
I know this is a super old question, and Tony Arnold's question is right, and what I used to build my own "grab the desktop" code.
I have some example code that shows how to do all these things (it's a wonderful thing walking in parts of Cocoa that are barely documented... )
I've put that sample code up in a bitbucket repository. Specifically the code sample to take a picture. (There's more interesting Cocoa code in my learning Cocoa repository, where that sample code is from )
Swift version:
extension NSImage {
static func desktopPicture() -> NSImage {
let windows = CGWindowListCopyWindowInfo(
CGWindowListOption.OptionOnScreenOnly,
CGWindowID(0))! as NSArray
var index = 0
for var i = 0; i < windows.count; i++ {
let window = windows[i]
// we need windows owned by Dock
let owner = window["kCGWindowOwnerName"] as! String
if owner != "Dock" {
continue
}
// we need windows named like "Desktop Picture %"
let name = window["kCGWindowName"] as! String
if !name.hasPrefix("Desktop Picture") {
continue
}
// wee need the one which belongs to the current screen
let bounds = window["kCGWindowBounds"] as! NSDictionary
let x = bounds["X"] as! CGFloat
if x == NSScreen.mainScreen()!.frame.origin.x {
index = window["kCGWindowNumber"] as! Int
break
}
}
let cgImage = CGWindowListCreateImage(
CGRectZero,
CGWindowListOption(arrayLiteral: CGWindowListOption.OptionIncludingWindow),
CGWindowID(index),
CGWindowImageOption.Default)!
let image = NSImage(CGImage: cgImage, size: NSScreen.mainScreen()!.frame.size)
return image
}
}