I have a osx (MacOS) application with a mainwindowController and the user can maximise the view. The app also has a preferences window that opens as a childwindowcontroller.
However if the main window is maximised the child window also tries to be maximised which ends up with a black screen with the child window display in the middle.
Is there anyway to make the child window not be displayed based on the main window maximised state and the child window always not be maximised.
Here is some code of how the preferences window is displayed.
let currentWindowController = self.view.window?.windowController as! MainWindowViewController
let childWindowControllers = windowControllers[currentWindowController]
if let subPreferencesWindowController: SubPreferenceWindowViewController? = {
for windowController in childWindowControllers! {
if windowController is SubPreferenceWindowViewController {
return windowController as? SubPreferenceWindowViewController
}
}
return nil
}(){
let subPreferencesController = subPreferencesWindowController?.contentViewController as! SubPreferenceViewController
subPreferencesController.appStateJSON = self.appStateJSON!
subPreferencesController.preferences = self.preferencesJSON!
subPreferencesController.setUpSubPreferences()
}
SOLVED IT - When I create the window I add a styling mask to set the new window to not be resizable. (Last line of code)
if let subPreferenceViewController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "sub-preferences")) as? SubPreferenceViewController {
let subPreferenceWindow = NSWindow(contentViewController: subPreferenceViewController)
let subPreferenceWindowController = SubPreferenceWindowViewController(window: subPreferenceWindow)
var windowControllersForCurrent = windowControllers[currentWindowController]
windowControllersForCurrent?.append(subPreferenceWindowController)
windowControllers[currentWindowController] = windowControllersForCurrent
newWindowController = subPreferenceWindowController
newWindow = subPreferenceWindow
newWindow.standardWindowButton(.zoomButton)!.isHidden = true
width = 400
height = 607
newWindow.maxSize = NSSize(width: width, height: height)
newWindow.styleMask.remove(.resizable)
}
Related
Problem
Hi All,
I need help to make my window NSToolbar transparent, however, I still need to be able to use a toolbar within the window scene titlebar. Currently, you can set a titlebar to be transparent by either hiding your toolbar or not having one at all, but even though I will not be adding any actual tools within the toolbar, there needs to be one so that I can reposition the window traffic light buttons using the UITitlebarToolbarStyle property - I know that from Electron 8, developers are able to reposition the traffic light buttons (is this also possible in Swift?)
Any help is much appreciated ๐
Current Code
- this is situated within the AppDelegate
guard let windowScene = (scene as? UIWindowScene) else { return }
#if targetEnvironment(macCatalyst)
windowScene.sizeRestrictions?.minimumSize = CGSize(width: 1366 * 0.9, height: 1024 * 0.9)
if let titlebar = windowScene.titlebar {
let customToolbar = NSToolbar()
titlebar.titleVisibility = .hidden
titlebar.toolbar = customToolbar
titlebar.toolbarStyle = .unified
titlebar.autoHidesToolbarInFullScreen = true
}
#endif
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
window?.makeKeyAndVisible()
I am thinking about porting my macOS app to Catalyst.
My app shows a transparent window (no title bar, clear background) on top of all other apps windows (dock included).
To do that, in the non-catalyst code I use:
window.isOpaque = false
window.hasShadow = false
window.backgroundColor = .clear
window.styleMask = .borderless
window.isMovableByWindowBackground = true
window.level = .statusBar
Using UIKit, I was only able to remove the toolbar so far:
window.titleBar.titleVisibility
...But no clue about the other settings.
I plan to make the app available on the App Store in the future, but if the only way to do so is and hack with a private API, that's fine.
Any ideas?
Thanks in advance
There is no official API for doing that, but you can easily access the NSWindow instance and modify it directly. You can do that manually or using some library like Dynamic (Full disclosure: I'm the author):
let window = Dynamic.NSApplication.sharedApplication.delegate.hostWindowForUIWindow(uiWindow)
window.isOpaque = false
window.hasShadow = false
window.backgroundColor = Dynamic.NSColor.clearColor
window.styleMask = 0 /*borderless*/
window.isMovableByWindowBackground = true
window.level = 25 /*statusBar*/
I have some success removing the close button on Catalyst by calling a function from the viewDidAppear(). I called it AppDelegate().disableTitleBarButtons(). Has to be from view did appear.
AppDelegate().disableTitleBarButtons() is as follows
func disableTitleBarButtons() {
func bitSet(_ bits: [Int]) -> UInt {
return bits.reduce(0) { $0 | (1 << $1) }
}
func property(_ property: String, object: NSObject, set: [Int], clear: [Int]) {
if let value = object.value(forKey: property) as? UInt {
object.setValue((value & ~bitSet(clear)) | bitSet(set), forKey: property)
}
}
// disable full-screen button
if let NSApplication = NSClassFromString("NSApplication") as? NSObject.Type,
let sharedApplication = NSApplication.value(forKeyPath: "sharedApplication") as? NSObject,
let windows = sharedApplication.value(forKeyPath: "windows") as? [NSObject]
{
for window in windows {
let resizable = 4
property("styleMask", object: window, set: [], clear: [resizable])
let fullScreenPrimary = 7
let fullScreenAuxiliary = 8
let fullScreenNone = 9
property("collectionBehavior", object: window, set: [fullScreenNone], clear: [fullScreenPrimary, fullScreenAuxiliary])
}
}
}
Where is says let resizable = 4,
Change to 3 for no Maximise,
Change to 2 for No minimise,
Change to 1 of No Close button.
Play with the other numbers or stylemask settings also. Good luck
I am trying to get the application to be the size of the full screen using Catalyst supported APIs. I have tried using the UIScreen proeprty as well as the window property(below), but both have the problem where the window does not fill the full screen.
I have attached my code, but the window does not occupy the full screen size.
let windowRect = self.window?.frame
let windowWidth = windowRect?.size.width
let windowHeight = windowRect?.size.height
UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.forEach { windowScene in
windowScene.sizeRestrictions?.minimumSize = CGSize(width: windowWidth! , height:windowHeight! )
windowScene.sizeRestrictions?.maximumSize = CGSize(width: windowWidth!, height: windowHeight!)
}
I have a preferences window with a NSTabViewController hooked up to the toolbar for selecting tabs. I want the window to be resizable, and to resize if necessary when switching tabs to fit the new tab's size.
I'm subclassing NSTabViewController with the following overload:
override var selectedTabViewItemIndex: Int
{
didSet
{
guard let view = tabViewItems[selectedTabViewItemIndex].view,
let window = view.window
else { return }
let minSize = view.fittingSize
let contentRect = NSWindow.contentRect(forFrameRect: window.frame,
styleMask: window.styleMask)
let minRect = NSRect(origin: contentRect.origin, size: minSize)
let newRect = minRect.union(contentRect)
let newFrame = NSWindow.frameRect(forContentRect: newRect,
styleMask: window.styleMask)
window.animator().setFrame(newFrame, display: true, animate: true)
}
}
The result is that it animates resizing horizontally, and at the end it suddenly resizes vertically as well. How do I get it to just animate both directions at once?
Do you have height constraints on any of the tabs? Those might prevent the window from getting larger (even while offscreen).
I'm trying to detect if NSWindow is open or closed using the isVisible property of NSWindow, but it is not working as I expected. For example I overrode the loadWindow method of my NSWindowController (I need to show a fullscreen web on the extended screen):
override func loadWindow() {
self.contentController = WKUserContentController();
guard let contentController = self.contentController else {
return
}
let config = WKWebViewConfiguration()
config.userContentController = contentController
let externalScreens = NSScreen.externalScreens()
let screen = externalScreens.count == 0 ? NSScreen.main()! : externalScreens[0]
window = KeyWindow(
contentRect: NSRect(x: 0, y: 0, width: screen.frame.width, height: screen.frame.height),
styleMask: NSBorderlessWindowMask,
backing: NSBackingStoreType.buffered,
defer: false,
screen: screen
)
if let w = window {
w.level = Int(CGShieldingWindowLevel())
w.backgroundColor = NSColor.black
w.makeKeyAndOrderFront(self)
w.makeFirstResponder(self)
webView = WKWebView(frame: w.frame, configuration: config)
w.contentView = webView!
debugPrint("Window is visible = \(w.isVisible)")
}
}
KeyWindow:
import Foundation
import AppKit
class KeyWindow : NSWindow {
override var canBecomeKey: Bool {
return true
}
}
but debugPrint shows that isVisible property is set to true, although the window was not opened yet (self.showWindow(self) method of controller was not called yet).
How can I reliably find out if window is open (displayed on screen) or not?
in your code
if let w = window {
w.level = Int(CGShieldingWindowLevel())
w.backgroundColor = NSColor.black
w.makeKeyAndOrderFront(self)
w.makeFirstResponder(self)
webView = WKWebView(frame: w.frame, configuration: config)
w.contentView = webView!
debugPrint("Window is visible = \(w.isVisible)")
}
you are calling w.makeKeyAndOrderFront(self) before the check
according to the Apple documentation:
func makeKeyAndOrderFront(Any?) Moves the window to the front of the
screen list, within its level, and makes it the key window; that is,
it shows the window.
So technically the isVisible works as advertised :)
isVisible
The value of this property is true when the window is onscreen (even
if itโs obscured by other windows); otherwise, false.
You window should be onscreen - despite the fact that you have set it's level as CGShieldingWindowLevel it maybe just invisible due to call of
w.makeKeyAndOrderFront(self) you can try to call func orderFrontRegardless()
Apple Doc
In this case it shall show the window immediately - but I think it is another SO Question