Mac Catalyst - control window resize - macos

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!)
}

Related

Can't get CoreSpotlight to work properly on the Mac... sample code anyone?

Can someone point me to some sample code for using CoreSpotlight on the Mac? I can’t find a single sample code example (for mac, plenty of iOS ones out there). I have already successfully implemented CS on our iOS app.
Using code similar to what I used for iOS, I’m getting it to index the content, but it’s very minimal: it gives me the generic app icon for the content (rather than the custom thumbnail image I specify) and has no description, etc (on the Spotlight pane, the entire right side is blank).
Also, it's unclear to me what code I need to add to AppDelegate to handle the click on the spotlight data. I used `func application(_ application: NSApplication, continue userActivity: NSUserActivity, restorationHandler:) on iOS but this doesn't seem to get called on the Mac.
Here's the code I'm using to index the content:
attributeSet.title = content.name
attributeSet.contentDescription = content.description
if let image = image {
let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil)!
let bitmapRep = NSBitmapImageRep(cgImage: cgImage)
attributeSet.thumbnailData = bitmapRep.representation(using: NSBitmapImageRep.FileType.jpeg, properties: [:])!
}
let item = CSSearchableItem(uniqueIdentifier:content.uuid, domainIdentifier: "com.my.app.bundle.id", attributeSet: attributeSet)
searchableIndex?.indexSearchableItems([item]) { error in
if let error = error {
NSLog("CoreSpotlight Indexing error: \(error.localizedDescription)")
}
}
And this is an example of what I get in Spotlight:
A bit of sample code would clear this right up, Methinks.
Thanks in advance.

OS X App view managment in Swift

I am trying to write my first own app for OS X with Swift (without any tutorial or anything) and this one will contain two different "pages". So I created another XIB file containing the first view and put it as Main interface. I created a little "Entry.swift" file containing the IBOutlets and variables of the first view, in which I wrote this:
#IBAction func validation(sender: AnyObject) //Right username and password needed to unlock the second view
{
if (usernameField.stringValue == "Stan" && passwordField.stringValue == "Test") || (usernameField.stringValue == "Nico" && passwordField.stringValue == "Test2")
{
accesLabel.textColor = NSColor.greenColor()
accesLabel.stringValue = "Acces Granted"
//GO TO THE OTHER XIB FILE (1)
}
else
{
accesLabel.textColor = NSColor.redColor()
accesLabel.stringValue = "Acces Denied"
}
}
But my questions are:
Is this the best way to go from one window to another one ?
and
If yes, how can I code it ? (1)
If no, what is it ?
I am new to iOS and OSX programming, since I learned in C# first... This was my first own programm in C# so I am trying to convert it.
Have you tried using only one storyboard but hidding/showing buttons regarding where you are ? That might be a way for a light program...
With Xcode6 you can now have Storyboards for OS X projects. In this case you can just create a segue from one window to another and trigger this segue programmatically whenever you want.

Cocoa: take screenshot of desktop wallpaper (without icons and windows)

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
}
}

Testing to see if a window is maximized

I noticed that in Windows, if you maximize a window you can not resize it until you un-maximized it again. This appears to be a normal behaviour, so I would like to remove my resize gripper when the window is maximised.
At the moment I can't find a property to detect if a window is maximized, and although I could add a boolean in my controller, it wouldn't necessarily catch requests to maximize from the OS.
So if you know of a reliable way to test if a window is maximized please let me know.
On a related note, I am using custom chrome, and when I maximize a window it overlaps the windows task bar. I can think of hacks to detect available screen size (using a transparent system chrome window), but it would be good to know of a better method.
Thanks
Rob
In your application (MXML) on the in the init method you ussually call on creationComplete:
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
creationComplete="init()" >
Add the following code:
this.addEventListener(NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGE, trackState);
the method looks like this:
public function trackState(event:NativeWindowDisplayStateEvent):void
{
if (event.afterDisplayState == NativeWindowDisplayState.MAXIMIZED)
{
isMaximised = true;
} else {
isMaximised = false;
}
}
I have figured out how this can best be done thanks to some pointers from TheBrain.
Firstly you need to watch for resize events to the window your want to control:
NativeApplication.nativeApplication.activeWindow.addEventListener(NativeWindowBoundsEvent.RESIZE, onWindowResize);
Then handle that event to decide if the window is maximised or not:
public function onWindowResize(event:NativeWindowBoundsEvent):void
{
if (event.afterBounds.height >= Screen.mainScreen.visibleBounds.height && event.afterBounds.width >= Screen.mainScreen.visibleBounds.width)
isMaximised = true;
else
isMaximised = false;
}
You then need to catch or create your own maximize button, and when clicked perform the following code:
if (isMaximised)
{
var bounds:Rectangle = Screen.mainScreen.visibleBounds;
NativeApplication.nativeApplication.activeWindow.bounds = bounds;
}
else
{
NativeApplication.nativeApplication.activeWindow.bounds = new Rectangle(100, 100, 500, 600);
}
You can modify the bounds to over maximize (which is handy for custom chrome windows with shadows), and you can also set the application to reset to a default size if the maximize button is clicked when it's already maximized (or do nothing).
I had issues about when to assign the window resize listner, and ended up removing and adding it every time the maximize button was clicked. It's a bit of overkill, but not too bad.
There is Win32 API Call that will do this for you:
BOOL IsZoomed( HWND hWnd );
to get the actual usable space from the screen, use the flash.display.Screen class, or you can use the systemMaxSize() which returns the largest window size allowed by the OS. For maximization you have some events that the window is dispaching when maximized/minimized/restored. You can find more info on the adobe pages (the link under systemMaxSize).
To detect if window is maximized...I don't think there is such a function (I might be wrong) but you can test if the app size is equal with the available screen size which means it's maximized. Or hook on the resize event which is triggered when the app is maximized/minimized/resized
Here is an easier way of checking if a window is maximized:
if(stage.nativeWindow.displayState == NativeWindowDisplayState.MAXIMIZED)
{
//do something
}
The following worked for me. No need to set event listeners, this code can be used to check the real-time state of the native window:
if (nativeWindow.displayState == 'maximized')
{
trace('Maximized');
}
else
{
trace('Minimized');
}
Can you use something like this to hook the maximize() event?

How can I tell if Voice Over is turned on in System Preferences?

Is there an way, ideally backwards compatible to Mac OS X 10.3, to tell if "Voice Over" is activated in System Preferences?
This appears to be stored in a preferences file for Universal Access. The app identifier is "com.apple.universalaccess" and the key containing the flag for whether VoiceOver is on or off is "voiceOverOnOffKey". You should be able to retrieve this using the CFPreferences API, something looking like:
CFBooleanRef flag = CFPreferencesCopyAppValue(CFSTR("voiceOverOnOffKey"), CFSTR("com.apple.universalaccess"));
If anyone has the same question, it could be good to know, that Voice Over status is accessible via convenient interface now:
NSWorkspace.shared.isVoiceOverEnabled
Based on Petes excellent answer I’ve created this Swift 4.2 solution, which I find much easier to read. I also think it’s more handy to use a computed property in this case instead of a function.
var hasVoiceOverActivated: Bool {
let key = "voiceOverOnOffKey" as CFString
let id = "com.apple.universalaccess" as CFString
if let voiceOverActivated = CFPreferencesCopyAppValue(key, id) as? Bool {
return voiceOverActivated
}
return false
}
VoiceOver and Accessibility in general are very important topics and it is sad that the lack of Apples documentation especially for macOS makes it so hard for developers to implement it properly.
Solution in Swift 4 is as follows:
func NSIsVoiceOverRunning() -> Bool {
if let flag = CFPreferencesCopyAppValue("voiceOverOnOffKey" as CFString, "com.apple.universalaccess" as CFString) {
if let voiceOverOn = flag as? Bool {
return voiceOverOn
}
}
return false
}
Furthermore, to make a text announcement with VoiceOver on macOS, do the following:
let message = "Hello, World!"
NSAccessibilityPostNotificationWithUserInfo(NSApp.mainWindow!,
NSAccessibilityNotificationName.announcementRequested,
[NSAccessibilityNotificationUserInfoKey.announcement: message,
NSAccessibilityNotificationUserInfoKey.priority:
NSAccessibilityPriorityLevel.high.rawValue])

Resources