从导航目的地返回时如何重置视图框架原点、宽度和高度

问题描述 投票:0回答:1

我有一个程序,它使用具有 2 个导航目的地的导航堆栈。我不知道如何将每个视图框架在屏幕上居中,并根据显示的特定视图调整宽度和高度(所有 3 个视图框架都有不同的框架宽度和高度,2 个导航目的地视图比主视图大) .

所以我在网上找到了下面的代码。下面列出了链接。我已将代码集成到我的代码中。它允许我将初始主视图框架显示在所需尺寸的中心。

当我转到导航目的地视图时,视图框架大小会正确调整并居中。但是当我返回主视图时,框架大小/位置不会改变。如果我然后转到另一个导航目标视图,则框架大小仅在较大时才会更改。因此,视图框架尺寸/位置最终会达到显示的最大视图尺寸,但始终居中。

在我在网上找到的代码中,它在 NSWindow 的扩展中设置了框架原点。为了让主视图以所需的宽度和高度显示,我必须在 @main 中设置默认值,如下所示:

.defaultSize(width: 450, height: 700)
。由于这是设置默认大小,因此我应该能够返回主视图将帧大小(如果需要,还可以重置原点)重置为默认值。

我创建了一个函数

setHomePosition
,将其添加到 NSWindow 扩展中(请参阅下面的代码),并认为我可以在主视图(ContentView)中运行它
onappear
,但 Xcode 找不到该函数。我认为因为它是 NSWindow 的扩展,而不是 View。

在查看主页视图(内容视图)和 2 个导航目标视图的代码时,我意识到每个目标视图都包含一个 .frame(),用于设置宽度和高度,但主页视图没有。我认为它是用 @main 中的 .default size 代码行设置的。因此,如果我能够确定如何在返回主视图时设置主视图的框架,我可能会有一个解决方案。如果有人能指出我正确的方向,我将不胜感激。

https://gist.github.com/ABridoux/b935c21c7ead92033d39b357fae6366b

@main
struct TableViewTestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .hostingWindowPosition(
                    vertical: .center,
                    horizontal: .center,
                    screen: .main
                )
        }
        .defaultSize(width: 450, height: 700)
    }
}

extension NSWindow {

    struct Position {
        static let defaultPadding: CGFloat = 16
        var vertical: Vertical
        var horizontal: Horizontal
        var padding = Self.defaultPadding
    }
}

extension NSWindow.Position {

    enum Horizontal {
        case left, center, right
    }
    enum Vertical {
        case top, center, bottom
    }
}

extension View {

    func hostingWindowPosition(
        vertical: NSWindow.Position.Vertical,
        horizontal: NSWindow.Position.Horizontal,
        padding: CGFloat = NSWindow.Position.defaultPadding,
        screen: NSScreen? = nil
    ) -> some View {
        modifier(
            WindowPositionModifier(
                position: NSWindow.Position(
                    vertical: vertical,
                    horizontal: horizontal,
                    padding: padding
                ),
                screen: screen
            )
        )
    }
}

private struct WindowPositionModifier: ViewModifier {

    let position: NSWindow.Position
    let screen: NSScreen?
    func body(content: Content) -> some View {
        content.background(
            HostingWindowFinder {
                $0.setPosition(position, in: screen)
            }
        )
    }
}

private struct HostingWindowFinder: NSViewRepresentable {
    
   var callback: (NSWindow) -> ()

   func makeNSView(context: Self.Context) -> NSView {
       let view = BridgingView()
       view.callback = callback
       return view
   }

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

private class BridgingView: NSView {
    
   var callback: ((NSWindow) -> ())?

   override func draw(_ dirtyRect: NSRect) {
       super.draw(dirtyRect)
       if let window = window {
           callback?(window)
       }
   }
}

extension NSWindow {
    
    func setPosition(_ position: Position, in screen: NSScreen?) {
        guard let visibleFrame = (screen ?? self.screen)?.visibleFrame else { return }
        let origin = position.value(forWindow: frame, inScreen: visibleFrame)
        setFrameOrigin(origin)
    }
    
    // I added this function
    func setHomePosition() {
        let nsRectangle: NSRect = NSRect(x: 1055.0, y: 370.0, width: 450, height: 700)
        setFrame(nsRectangle, display: true)
    }
}

extension NSWindow.Position {

    func value(forWindow windowRect: CGRect, inScreen screenRect: CGRect) -> CGPoint {
        let xPosition = horizontal.valueFor(
            screenRange: screenRect.minX..<screenRect.maxX,
            width: windowRect.width,
            padding: padding
        )
        let yPosition = vertical.valueFor(
            screenRange: screenRect.minY..<screenRect.maxY,
            height: windowRect.height,
            padding: padding
        )
        return CGPoint(x: xPosition, y: yPosition)
    }
}

extension NSWindow.Position.Horizontal {
    func valueFor(
        screenRange: Range<CGFloat>,
        width: CGFloat,
        padding: CGFloat)
    -> CGFloat {
        switch self {
        case .left: return screenRange.lowerBound + padding
        case .center:
            return (screenRange.upperBound + screenRange.lowerBound - width) / 2
        case .right: return screenRange.upperBound - width - padding
        }
    }
}

extension NSWindow.Position.Vertical {
    func valueFor(
        screenRange: Range<CGFloat>,
        height: CGFloat,
        padding: CGFloat)
    -> CGFloat {
        switch self {
        case .top: return screenRange.upperBound - height - padding
        case .center: return (screenRange.upperBound + screenRange.lowerBound - height) / 2
        case .bottom: return screenRange.lowerBound + padding
        }
    }
}
swift macos swiftui nswindow
1个回答
0
投票

经过一些额外的研究,我确定可以通过在 @main 中为我想要的每个窗口大小/原点创建一个 WindowGroup 来获得所需的结果。所以我最终得到了一个主页、表格和图表窗口组。为了获得我想要的每个窗口组的大小/原点(都不同),我修改了上面的 setPosition NSWindow 扩展,为每个窗口组硬编码了一个 nsRectangle 并用 setFrame 分配它。这意味着为每个窗口创建一个位置逻辑文件。导航堆栈不再使用,取而代之的是一系列按钮,其操作是 openWindow(id:value:),当然使用多个窗口组意味着当选择一个按钮时会显示一个附加窗口。

//updated nswindow extension in position logic for home window group
    func setPositionHome(_ position: Position, in screen: NSScreen?) {
        let nsRectangle: NSRect = NSRect(x: 1055.0, y: 370.0, width: 450, height: 700)
        setFrame(nsRectangle, display: true)
    }

// updated @main
struct WindowGroupsApp: App {
    
    var body: some Scene {
        WindowGroup ("Home") {
            ContentView()
                .hostingWindowPositionHome(
                    vertical: .center,
                    horizontal: .center,
                    screen: .main
                )
        }
        WindowGroup ("Table", id: "table", for: String.self) { $fundName in
            TableView(fundName: fundName!)
                .hostingWindowPositionTable(
                    vertical: .center,
                    horizontal: .center,
                    screen: .main
                )
        }
        WindowGroup ("Chart", id: "chart", for: String.self) { $fundName in
            ChartView(fundName: fundName!)
                .hostingWindowPositionChart(
                    vertical: .center,
                    horizontal: .center,
                    screen: .main
                )
        }
    }
}
// home 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: "Table")
                    } label: {
                        Text("Table")
                            .font(Font.custom("Arial", size: 14.0))
                            .foregroundColor(Color.blue)
                            .background(Color.clear)
                            .padding(.leading, 35)
                    }
                    .focusable(false)
                    .buttonStyle(PlainButtonStyle())
                    Button {
                        openWindow(id: "chart", value: "Chart")
                    } label: {
                        Text("Chart")
                            .font(Font.custom("Arial", size: 14.0))
                            .foregroundColor(Color.blue)
                            .background(Color.clear)
                            .padding(.leading, 35)
                    }
                    .focusable(false)
                    .buttonStyle(PlainButtonStyle())
                }
            } 
        } 
        .frame(width: 450, height: 600, alignment: .topLeading)
    } 
}
© www.soinside.com 2019 - 2024. All rights reserved.