Mouse cursor dragging bookmark file to a Safari window tab

在Safari中,我可以将一个项目(比如URL,甚至是查找器中的.webloc书签)拖到标签页上打开.

How do I make the window tab bar item view a drop target in my own AppKit app?

我希望能够接受放弃NSPasteboard分,就像NSView个实例可以:

override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
    // Handle drop
}

但选项卡栏和包含的NSTabButton个实例是由系统提供的. 子类化或扩展NSTabButton似乎行不通,因为它是私有的.

推荐答案

布莱恩·韦伯斯特的精彩回答(谢谢!)启发了这个解决方案.

Window with tabs that can accept a drop

我加了demo code to a fully working project on GitHub.

首先,我们在创建窗口时向windowtab添加一个自定义附件视图.我们传递一个对NSWindowController的引用,以便无论何时在选项卡项上有东西落下时都可以很容易地通知它.

window.tab.accessoryView = TabAccessoryView(windowController: windowController)

该定制accessoryView(TabAccessoryView)是将接受拖放的视图not,因为附件视图与关闭按钮和标题标签一起被限制在NSStackView中,仅覆盖标题标签旁边的标签的一部分.

因此,取而代之的是,我们利用accessoryViewNSTabButton的视图层次 struct 的一部分这一事实来注入另一个定制视图(TabDropTargetView)behindNSStackView

class TabAccessoryView: NSView {

    weak private(set) var windowController: NSWindowController?

    private let tabDropTargetView: TabDropTargetView

    init(windowController: NSWindowController? = nil) {
        self.windowController = windowController
        self.tabDropTargetView = TabDropTargetView(windowController: windowController)
        super.init(frame: .zero)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidMoveToWindow() {
        guard tabDropTargetView.superview == nil else { return }

        // DEBUG: Highlight accessory view
        wantsLayer = true
        layer?.backgroundColor = NSColor.red.withAlphaComponent(0.1).cgColor

        // The NSTabButton close button, title, and accessory view are contained in a stack view:
        guard let stackView = superview as? NSStackView,
              let backgroundView = stackView.superview else { return }

        // Add the drop target view behind the NSTabButton’s NSStackView and pin it to the edges
        backgroundView.addSubview(tabDropTargetView, positioned: .below, relativeTo: stackView)
        tabDropTargetView.translatesAutoresizingMaskIntoConstraints = false
        tabDropTargetView.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor).isActive = true
        tabDropTargetView.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor).isActive = true
        tabDropTargetView.topAnchor.constraint(equalTo: backgroundView.topAnchor).isActive = true
        tabDropTargetView.bottomAnchor.constraint(equalTo: backgroundView.bottomAnchor).isActive = true
    }

}

…它将处理丢弃的项目:

class TabDropTargetView: NSView {
    private(set) weak var windowController: NSWindowController?

    let allowedDropTypes: Array<NSPasteboard.PasteboardType> = [.URL, .fileContents, .string, .html, .rtf]

    init(windowController: NSWindowController? = nil) {
        self.windowController = windowController
        super.init(frame: .zero)

        // Tell the system that we accept drops on this view
        registerForDraggedTypes(allowedDropTypes)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidMoveToWindow() {
        // DEBUG: Highlight drop target view
        wantsLayer = true
        layer?.backgroundColor = NSColor.green.withAlphaComponent(0.05).cgColor
    }

    override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
        return .copy
    }

    override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation {
        return .copy
    }

    override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
        // Optional: Ignore drags from the same window
        guard (sender.draggingSource as? NSView)?.window != window else { return false }

        // Check if the dropped item contains text:
        let pasteboard = sender.draggingPasteboard
        guard let availableType = pasteboard.availableType(from: allowedDropTypes),
              let text = pasteboard.string(forType: availableType) else {
            return false
        }

        if let windowController = windowController as? WindowController {
            // Use the reference to the tab’s NSWindowController to pass the dropped item
            windowController.handleDroppedText(text)
        }

        return true
    }
}

Swift相关问答推荐

如何使用RxSwift根据数据数绘制UICollectionView单元格

UITableView未显示类数组数据

如何将EnvironmentObject从UIKit视图控制器传递到SwiftUI视图

如何在AudioKit中实现自定义音效 node ?

UICollectionViewCompostionalLayout Collectionview单元格宽度在重新加载数据后未正确调整大小

在Xcode SWIFT中运行用Ionic编写的函数

在SwiftUI中,我如何确保带有符号的单词不会被拆分为两行?

Swift计时器未触发

Swift 并发任务与调度队列线程:是什么决定同时运行多少任务?

在领域 SwiftUI 异常中使用 Apple 登录:无法识别的 Select 器发送到实例

用户输入以更改通过点击生成的形状大小

对齐时如何避免图像尺寸缩小

Swift:withCheckedContinuation 和 Dispatch QoSClass

macOS 守护进程应该由Command Line ToolXcode 模板制作吗?

仅在 Swift 中创建 Setter

Xcode 6 中的嵌入式内容包含 Swift 代码构建设置有什么作用?

如何从 Button 获取标签名称?

如何快速识别字符串中的大写和小写字符?

swift中静态函数和单例类的区别

应用程序包不包含有效标识符