Home > Blockchain >  How to keep subview's position relative to its parent view in native macOS SwiftUI?
How to keep subview's position relative to its parent view in native macOS SwiftUI?

Time:08-05

There is nice api from apple to showing a view as sheet in macOS and sheet respect the size of sheetView even the size could be bigger than parent and it shows the sheetView nicely under the titlebar of parent view, and if we move the parent title, the child or the sheetView moves and follows the parent, so far so good. I am very interstate to build a diy codes which it's results would be same as apple sheet view in native macOS SwiftUI, so I could successfully show the childView but the childView or my custom sheetView position is completely independent from the parent view, looking to solve the position of childView that stand under parent menu title.

ps: I noticed if i remove the unneeded parameter from my function called sender: Any? the window is not going show, how ever if I keep the unneeded parameter of sender: Any? in function and also feeding it with nil, it makes the child view show and work, not sure why that makes so big deal, but I wanted point it for you. May my codes need refactor and remove extra or unneeded part as well.

So my goal is to replicate apple sheet view and trying to solve the position issue of my childView to lock to parent view.

struct ContentView: View {
    @State private var show: Bool = false
    var body: some View {
            Color.gray
                .overlay(VStack {
                    Button("show sheet") { show.toggle() }
                    Button("show window") { sheetView().openInWindow(sender: nil) }
                })
                .frame(width: 200, height: 200)
                .sheet(isPresented: $show, content: { sheetView() })
                
    }
    func sheetView() -> some View {
        return Color.yellow
            .frame(width: 400, height: 400)
    }
}

extension View {
    @discardableResult
    func openInWindow(sender: Any?) -> NSWindow {
        let controller = NSHostingController(rootView: self)
        let window = NSWindow(contentViewController: controller)
        window.contentViewController = controller
        window.makeKeyAndOrderFront(sender)
        window.titleVisibility = .hidden
        window.toolbar = nil
        window.styleMask = .fullSizeContentView
        return window
    }
}

CodePudding user response:

You just need to add it as a child to parent window, like

Button("show window") {
    if let parent = NSApp.mainWindow {
        let window = sheetView().openInWindow(sender: nil)
        parent.addChildWindow(window, ordered: .above)      // << here !!
    }
}

and some animation (Tested with Xcode 13.4 / macOS 12.5)

demo

Main part (macOS Y zero is at right-bottom):

var frame = parent.frame
let (midX, maxY) = (frame.midX, frame.maxY)
frame.size = window.frame.size
frame.origin = NSPoint(x: midX - frame.size.width/2, y: maxY - frame.size.height / 2.0)
window.setFrame(frame, display: true, animate: false)

window.makeKeyAndOrderFront(nil)
frame.size = window.frame.size
frame.origin = NSPoint(x: midX - frame.size.width/2, y: maxY - frame.size.height - 28)
window.setFrame(frame, display: true, animate: true)

Test module is here

  • Related