Reputation: 109
I have an iOS app that I have enabled Catalyst for. One function in the app opens a new window. By default, this window opens very large but I need a way to make it smaller by default. I know you can set windowScene.sizeRestrictions?.minimumSize
and .maximumSize
, but that then limits the window to my preferred size. I'd like to make it so the window opens a certain size, say 500x800
by default, but can be expanded by the user to whatever they want.
I have tried window?.frame = CGRect(origin: .zero, size: CGSize(width: 500, height: 800))
in the SceneDelegate
, but it has no effect.
Visual example:
Upvotes: 3
Views: 2432
Reputation: 348
If you want to restore the window size and position from a previous run this might be the right solution for you:
First of all you have to implement a SceneDelegate
for your app. That’s straightforward for UIKit but a little more complicated for SwiftUI. Take a look at this post for that:
https://www.fivestars.blog/articles/app-delegate-scene-delegate-swiftui/
It’s a good idea to remember the corresponding window scene in the delegate:
class MySceneDelegate: NSObject, UIWindowSceneDelegate, ObservableObject {
var scene: UIWindowScene? // remember our window scene
…
And then you implement this delegate function:
…
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
#if targetEnvironment(macCatalyst) // only for running on a Mac
guard let windowScene = scene as? UIWindowScene else { return }
self.scene = windowScene // remember our window scene
var preferredRect: CGRect? { // is a size preference present?
var rect: CGRect? // by default its not
let width = UserDefaults.standard.double(forKey: MyMessages.windowSizeWidth)
let height = UserDefaults.standard.double(forKey: MyMessages.windowSizeHeight)
let x = UserDefaults.standard.double(forKey: MyMessages.windowOriginX)
let y = UserDefaults.standard.double(forKey: MyMessages.windowOriginY)
if width > 0 && height > 0 { // we do have a valid size
rect = CGRect(x: x, y: y, width: width, height: height)
}
return rect // return the rect (or nothing)
}
if let rect = preferredRect { // we do have a rect
let geoPrefs = UIWindowScene.GeometryPreferences.Mac(systemFrame: rect)
windowScene.requestGeometryUpdate(geoPrefs) // apply it to the current scene
}
#endif
}
}
To store the current geometry of your window you can use any View
in your app with a GeometryReader
:
struct MyView: View {
@EnvironmentObject var sceneDelegate: MySceneDelegate
…
var body: some View {
GeometryReader { geo in
…
.onChange(of: geo.size) { // size changed
#if targetEnvironment(macCatalyst) // are we running on a Mac?
if let rect = sceneDelegate.scene?.effectiveGeometry.systemFrame { // get the system frame from the scene delegate
UserDefaults.standard.set(rect.width, forKey: MyMessages.windowSizeWidth) // and set the user defaults
UserDefaults.standard.set(rect.height, forKey: MyMessages.windowSizeHeight)
UserDefaults.standard.set(rect.origin.x, forKey: MyMessages.windowOriginX)
UserDefaults.standard.set(rect.origin.y, forKey: MyMessages.windowOriginY)
}
#endif
}
}
}
}
Upvotes: 0
Reputation: 12582
Following Dylan's great tip,
func tidyCatalystWindow() {
#if targetEnvironment(macCatalyst)
UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
.forEach { ws in
ws.sizeRestrictions?.minimumSize = CGSize(width: 500, height:800)
ws.sizeRestrictions?.maximumSize = CGSize(width: 500, height: 800)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
ws.sizeRestrictions?.maximumSize = CGSize(width: 9000, height: 9000)
}
}
#endif
}
It's that easy.
Upvotes: 3
Reputation: 109
Figured it out, so I'll help anyone with this issue in the future. Set your .maximumSize
as your preferred size. Then after setting the window, use DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
to set the .maximumSize
again, but this time what you want to be the actual maximum window size.
My full code:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
#if targetEnvironment(macCatalyst)
let toolbarDelegate = NewSchoolworkToolbar()
let toolbar = NSToolbar(identifier: "main")
windowScene.title = "New Schoolwork"
if let titlebar = windowScene.titlebar {
titlebar.toolbar = toolbar
titlebar.toolbarStyle = .unified
titlebar.separatorStyle = .shadow
}
#endif
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
let newClassworkView = NewClassworkTableViewController()
windowScene.sizeRestrictions?.minimumSize = CGSize(width: 400, height: 500)
// This will be your "preferred size"
windowScene.sizeRestrictions?.maximumSize = CGSize(width: 500, height: 800)
window.rootViewController = newClassworkView
self.window = window
window.makeKeyAndVisible()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
// This will be your actual size.
windowScene.sizeRestrictions?.maximumSize = CGSize(width: 9000, height: 9000)
}
}
Upvotes: 6