By default, in MacOS, you can double click the titlebar of a window to zoom it (resize to fit the screen - different from maximize). Double clicking it again brings it back to the previous size.
This works fine on my Catalyst app too. However I need to hide the titlebar and need to give my own custom UIView in that titlebar area the double click behavior. I am able to hide it using this:
Is there a method which lets me toggle the window zoom?
This works fine on my Catalyst app too. However I need to hide the titlebar and need to give my own custom UIView in that titlebar area the double click behavior. I am able to hide it using this:
Code Block #if os(OSX) || os(macOS) || targetEnvironment(macCatalyst) UIApplication.shared.connectedScenes.forEach({ if let titlebar = ($0 as? UIWindowScene)?.titlebar { titlebar.titleVisibility = .hidden titlebar.toolbar = nil } }) #endif
Is there a method which lets me toggle the window zoom?
I was able to figure this out using a workaround. UIKit/Catalyst itself doesn't provide any way to do this. But I was able to use the second method outline in this post on the link in my answer on Stackoverflow:
I used the second method and not the first one as the first one seems to be private API (I could be wrong) and will get rejected in App Store. The second method of using a plugin bundle and calling methods on that works well for me. This way I was able to not just perform the zoom, I was also able to perform other MacOS Appkit functionality like listening for keyboard, mouse scroll, hover detection etc.
After creating the plugin bundle, here's my code inside the plugin:
Plugin.swift:
MacPlugin.swift:
Then I call this from my iOS app code. This adds a transparent view at the top where double clicking calls the plugin code for toggling zoom.
NOTE that you must call this from viewDidAppear or somewhere when the windows have been initialized and presented. Otherwise it won't work.
Calling it from viewDidAppear:
https://stackoverflow.com/questions/67067248/macos-catalyst-how-to-allow-double-click-on-uiview-to-zoom-window/67084666#67084666How to Access the AppKit API from Mac Catalyst Apps
I used the second method and not the first one as the first one seems to be private API (I could be wrong) and will get rejected in App Store. The second method of using a plugin bundle and calling methods on that works well for me. This way I was able to not just perform the zoom, I was also able to perform other MacOS Appkit functionality like listening for keyboard, mouse scroll, hover detection etc.
After creating the plugin bundle, here's my code inside the plugin:
Plugin.swift:
Code Block import Foundation @objc(Plugin) protocol Plugin: NSObjectProtocol { init() func toggleZoom() func macOSStartupStuff() }
MacPlugin.swift:
Code Block import AppKit class MacPlugin: NSObject, Plugin { required override init() {} func macOSStartupStuff() { NSApplication.shared.windows.forEach({ $0.titlebarAppearsTransparent = true $0.titleVisibility = .hidden $0.backgroundColor = .clear ($0.contentView?.superview?.allSubviews.first(where: { String(describing: type(of: $0)).hasSuffix("TitlebarDecorationView") }))?.alphaValue = 0 }) } func toggleZoom(){ NSApplication.shared.windows.forEach({ $0.performZoom(nil) }) } } extension NSView { var allSubviews: [NSView] { return subviews.flatMap { [$0] + $0.allSubviews } } }
Then I call this from my iOS app code. This adds a transparent view at the top where double clicking calls the plugin code for toggling zoom.
NOTE that you must call this from viewDidAppear or somewhere when the windows have been initialized and presented. Otherwise it won't work.
Code Block #if os(OSX) || os(macOS) || targetEnvironment(macCatalyst) @objc func zoomTapped(){ plugin?.toggleZoom() } var pluginWasLoaded = false lazy var plugin : Plugin? = { pluginWasLoaded = true if let window = (UIApplication.shared.delegate as? AppDelegate)?.window { let transparentTitleBarForDoubleClick = UIView(frame: .zero) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(zoomTapped)) tapGesture.numberOfTapsRequired = 2 transparentTitleBarForDoubleClick.addGestureRecognizer(tapGesture) transparentTitleBarForDoubleClick.isUserInteractionEnabled = true transparentTitleBarForDoubleClick.backgroundColor = .clear transparentTitleBarForDoubleClick.translatesAutoresizingMaskIntoConstraints = false window.addSubview(transparentTitleBarForDoubleClick) window.bringSubviewToFront(transparentTitleBarForDoubleClick) window.addConstraints([ NSLayoutConstraint(item: transparentTitleBarForDoubleClick, attribute: .leading, relatedBy: .equal, toItem: window, attribute: .leading, multiplier: 1, constant: 0), NSLayoutConstraint(item: transparentTitleBarForDoubleClick, attribute: .top, relatedBy: .equal, toItem: window, attribute: .top, multiplier: 1, constant: 0), NSLayoutConstraint(item: transparentTitleBarForDoubleClick, attribute: .trailing, relatedBy: .equal, toItem: window, attribute: .trailing, multiplier: 1, constant: 0), transparentTitleBarForDoubleClick.bottomAnchor.constraint(equalTo: window.safeTopAnchor) ]) window.layoutIfNeeded() } guard let bundleURL = Bundle.main.builtInPlugInsURL?.appendingPathComponent("MacPlugin.bundle") else { return nil } guard let bundle = Bundle(url: bundleURL) else { return nil } guard let pluginClass = bundle.classNamed("MacPlugin.MacPlugin") as? Plugin.Type else { return nil } return pluginClass.init() }() #endif
Calling it from viewDidAppear:
Code Block override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) #if os(OSX) || os(macOS) || targetEnvironment(macCatalyst) if !Singleton.shared.pluginWasLoaded { Singleton.shared.plugin?.macOSStartupStuff() } #endif }