Search code examples
macosswiftuiappkitnspathcontrol

SwiftUI NSPathControl open finder location when clicking icons


I've wrapped NSPathControl for use in my Mac OS SwiftUI app but I can't work out how to make the sizes of the path smaller or how to get the individual paths to open the location in Finder when clicked.

Here is the wrapper:

struct PathView: NSViewRepresentable {
    typealias pathViewNSView = NSPathControl
    var configuration = { (view: pathViewNSView) in }
    
    func makeNSView(context: NSViewRepresentableContext<PathView>) -> NSPathControl {
        pathViewNSView()
    }
    func updateNSView(_ nsView: NSPathControl, context: NSViewRepresentableContext<PathView>) {
        configuration(nsView)
    }
}

I use the view like this:

PathView { view in
    view.url = URL(fileURLWithPath: "/Volumes/Users/myfolder", isDirectory: true)
}

I know to use NSWorkspace.shared.selectFile to open the path in Finder but not sure how to do this with the wrapped NSPathControl.


Solution

  • You can use the Coordinator pattern with NSViewRepresentable:

    struct PathView: NSViewRepresentable {
        var configuration = { (view: NSPathControl) in }
        
        func makeNSView(context: NSViewRepresentableContext<PathView>) -> NSPathControl {
            let pathControl = NSPathControl()
            pathControl.target = context.coordinator
            pathControl.action = #selector(Coordinator.action)
            return pathControl
        }
        func updateNSView(_ nsView: NSPathControl, context: NSViewRepresentableContext<PathView>) {
            configuration(nsView)
        }
        
        func makeCoordinator() -> Coordinator {
            return Coordinator()
        }
        
        class Coordinator : NSObject, NSPathControlDelegate {
            @objc func action(sender: NSPathControl) {
                if let url = sender.clickedPathItem?.url {
                    print(url)
                    NSWorkspace.shared.selectFile(url.path, inFileViewerRootedAtPath: url.path)
                }
            }
        }
    }
    

    Note: I'm not actually using any NSPathControlDelegate methods -- that's just to show that it could be extended to be a delegate as well