I have a program that utilizes a Navigation Stack. I want each view to be centered on the screen and be a specific size. I accomplished this with some position logic which sets the views frame size and origin. There is unique position logic for each of the 3 view sizes, the Content View and 2 navigation destination views. When the Content View is first displayed the associated position logic code runs and the view is the correct size and centered. The same is true every time one of the 2 navigation destination views is displayed. Unfortunately, when I return to the Content View from a navigation destination view the position logic does not run again and the Content View is now the same size and position as the previous navigation destination view. How do I resolve this problem? Below is the position logic code associated with the Content View and how it is called.
@main
struct TableViewTestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.hostingWindowPositionHome(
screen: .main
)
}
}
}
extension View {
func hostingWindowPositionHome(
screen: NSScreen? = nil
) -> some View {
modifier(
WindowPositionModifierHome(
screen: screen
)
)
}
}
private struct WindowPositionModifierHome: ViewModifier {
let screen: NSScreen?
func body(content: Content) -> some View {
content.background(
HostingWindowFinderHome {
$0?.setPositionHome(in: screen)
}
)
}
}
private struct HostingWindowFinderHome: NSViewRepresentable {
var callback: (NSWindow?) -> ()
func makeNSView(context: Self.Context) -> NSView {
let view = NSView()
DispatchQueue.main.async { self.callback(view.window) }
return view
}
func updateNSView(_ nsView: NSView, context: Context) {
DispatchQueue.main.async { self.callback(nsView.window) }
}
}
extension NSWindow {
func setPositionHome(in screen: NSScreen?) {
let nsRectangle: NSRect = NSRect(x: 1055.0, y: 370.0, width: 450, height: 700)
setFrame(nsRectangle, display: true)
}
}
I finally figured out what to do. I created 3 WindowGroups in @main. One for the home view, one for the table view and one for the chart view. I then simplified the position logic as well as added logic to account for each view (they are all of the different size). I manually determined the origin and size of all three windows and assign the appropriate values in an NSRect which is used in the setFrame function of NSWindow. The position logic is assigned to each WindowGroup in @main. All works. Below is the updated code.
// @main code
struct WindowGroupsApp: App {
var body: some Scene {
WindowGroup ("Home") {
ContentView()
.hostingWindowPosition(window: "Home")
}
WindowGroup ("Table", id: "table", for: String.self) { $fundName in
TableView(fundName: fundName!)
.hostingWindowPosition(window: "Table")
}
WindowGroup ("Chart", id: "chart", for: String.self) { $fundName in
ChartView(fundName: fundName!)
.hostingWindowPosition(window: "Chart")
}
}
}
// position logic
extension View {
func hostingWindowPosition(window: String) -> some View {
modifier(
WindowPositionModifier(window: window)
)
}
}
private struct WindowPositionModifier: ViewModifier {
let window: String
func body(content: Content) -> some View {
content.background(
HostingWindowFinder {
$0?.setPosition(window: window)
}
)
}
}
struct HostingWindowFinder: NSViewRepresentable { // the system calls the methods at appropriate times
var callback: (NSWindow?) -> ()
func makeNSView(context: Self.Context) -> NSView {
let view = NSView()
DispatchQueue.main.async { self.callback(view.window) }
return view
}
func updateNSView(_ nsView: NSView, context: Context) {
DispatchQueue.main.async { self.callback(nsView.window) }
}
}
extension NSWindow {
func setPosition(window: String) {
var nsRectangle: NSRect = NSRect(x: 100, y: 100, width: 100, height: 100)
switch window {
case "Home":
nsRectangle = NSRect(x: 1055.0, y: 370.0, width: 450, height: 700)
case "Table":
nsRectangle = NSRect(x: 1055.0, y: 320.0, width: 450, height: 800) // 390
case "Chart":
nsRectangle = NSRect(x: 680.0, y: 245.0, width: 1200, height: 950)
default: print("problem in window logic set position")
}
setFrame(nsRectangle, display: true)
}
}
// content view
struct ContentView: View {
@Environment (\.openWindow) private var openWindow
var body: some View {
VStack(alignment: .leading) {
ZStack (alignment: .leading) {
RoundedRectangle(cornerRadius: 10.0)
.fill(Color.white)
.frame(width: 200, height: 80)
.padding(.leading, 20)
VStack(alignment: .leading, spacing: 10) {
Button {
openWindow(id: "table", value: "Table1")
} label: {
Text("Table")
.font(Font.custom("Arial", size: 14.0))
.foregroundColor(Color.blue)
.background(Color.clear)
.padding(.leading, 35)
}
.focusable(false)
.buttonStyle(.link)
Button {
openWindow(id: "chart", value: "Chart1")
} label: {
Text("Chart - 1 Year")
.font(Font.custom("Arial", size: 14.0))
.foregroundColor(Color.blue)
.background(Color.clear)
.padding(.leading, 35)
}
.focusable(false)
.buttonStyle(.link)
Button {
openWindow(id: "chart", value: "Chart5")
} label: {
Text("Chart - 5 Years")
.font(Font.custom("Arial", size: 14.0))
.foregroundColor(Color.blue)
.background(Color.clear)
.padding(.leading, 35)
}
.focusable(false)
.buttonStyle(.link)
} // end v stack
} // end zstack
} // end v stack
.frame(width: 450, height: 600, alignment: .topLeading)
} // end some view
}