1

XCode 14.3.1. Mac running Ventura 13.4.1. I have a clock app where the views are constrained to a 1 to 1 aspect ratio. The containing window however can be resized but has no aspect ratio constraints. The views stay correctly proportioned but it looks bad if say the window is tall and thin with a small view in it. This is a single window App so uses WindowGroup not NSWindow.

In the entry point code I can set the window max and min size like this

import SwiftUI

@main
struct Mac_TestApp: App {
    var body: some Scene {
        WindowGroup {
            Overall_View()
                .frame(minWidth: 300, idealWidth: 500, maxWidth: 600, minHeight: 300, idealHeight: 500, maxHeight: 600)
        }
        .windowResizability(.contentSize)
    }
}

This sets the maximum and minimum size that the window width and height can be resized but does not fix the aspect ratio. The list of parameters I can change for WindowGroup is pretty short and does not have any aspect ratio that I can see (no AppDelegate in this type of App). I have seen examples of document apps where you override the NSWindow class and set your own window controller. This gives much more control but I have not seen anything similar for this type of app.

Archie
  • 15
  • 3

1 Answers1

0

You could use Asperi‘s WindowAccessor for this. The approach is described in this answer. It uses a NSViewRepresentable to access the underlying NSWindow.

import SwiftUI

struct WindowAccessor: NSViewRepresentable {
    @Binding var window: NSWindow?

    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        DispatchQueue.main.async {
            self.window = view.window   // << right after inserted in window
        }
        return view
    }

    func updateNSView(_ nsView: NSView, context: Context) {}
}

So, in your case you could do the following:

@main
struct Mac_TestApp: App {
    @State private var window: NSWindow?
    
    var body: some Scene {
        WindowGroup {
            Overall_View()
                .frame(minWidth: 300, idealWidth: 500, maxWidth: 600, minHeight: 300, idealHeight: 500, maxHeight: 600)
                .background(WindowAccessor(window: $window))
                .onChange(of: window) { newWindow in
                    newWindow?.contentAspectRatio = NSMakeSize(1, 1)
                }
        }
    }
}

On a side note, for a single window app and development target macOS 13+, use Window Scene instead of WindowGroup.

soundflix
  • 928
  • 9
  • 22
  • Thank you. This sort of worked. I used "newWindow?.aspectRatio = CGSize(width: 1, height: 1)". This works when the window is within the size of the views but ".windowResizability(.contentSize)" is no longer recognised so the window can be resized larger than the view. It still maintains the aspect ratio but does not behave how I want. – Archie Aug 13 '23 at 10:19
  • I adapted my code. `.windowResizability(.contentSize)` is not needed. Then it works. Please clarify "can be resized larger than the view". As far as I understood, you want it to be resizable. In that case your `.frame(...) constraints are wrong? – soundflix Aug 13 '23 at 13:27
  • The setting: Overall_View() .frame(minWidth: 300, idealWidth: 500, maxWidth: 600, minHeight: 300, idealHeight: 500, maxHeight: 600) is respected by the views but not the containing window. The window cannot size less than the minimum view size (300) but it can size larger than the maxWidth/maxHeight. So if the view has a different background colour to the window it shows as a square within a square. .windowResizability(.contentSize) constrained the window to the max view size. I am amazed that something like this is so difficult / impossible. – Archie Aug 14 '23 at 15:47
  • My code works. Check if your `Overall_View` is really constrained to a 1 to 1 aspect ratio. – soundflix Aug 14 '23 at 15:47
  • As I said in my first comment I agree your code works and I shall mark it as such. All I'm saying is that the side effect is to stop me using ".windowResizability" so that the window no longer stops resizing past 600 x 600. – Archie Aug 14 '23 at 16:09