如何给 NSOutlineView 一个滚动的平铺背景图像?

How to give an NSOutlineView a tiled background image that scrolls?

我有一个标准NSOutlineView。我希望它有一个背景图像,垂直平铺,并与轮廓视图单元格一起滚动。

我在我的ViewController中使用以下方法在一定程度上实现了这一点:

class ViewController: NSViewController {

    @IBOutlet weak var outlineView: NSOutlineView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        if let image = NSImage(named: "tile") {
            let color = NSColor.init(patternImage: image)
            outlineView.backgroundColor = color
        }
    }
}

这有效,除非您滚动到视图的顶部或底部(包含滚动视图提供的拉伸)。

我试过将背景图像放在滚动视图上,但它是静态的,不随大纲视图的内容滚动。

我还尝试在视图层次结构中子类化各种对象并覆盖它们的 draw(_ dirtyRect: NSRect) 方法并执行:

self.wantsLayer = true
self.layer?.backgroundColor = ...etc

但也没有成功。

任何人都可以提供任何建议吗?

我最终创建了一个新的自定义 NSView:

class MyView: NSView {

    override func draw(_ dirtyRect: NSRect) {
        if let image = NSImage(named: "Tile") {
            let color = NSColor.init(patternImage: image)
            color.setFill()
            dirtyRect.fill()
        }

        super.draw(dirtyRect)
    }
}

然后在我的 ViewController class 中,我添加了一个自定义视图的实例,并使用自动布局约束将新视图固定到我的 outlineView 的剪辑视图中,从它上方 2000 点开始,到下方 2000 点结束.这意味着无论您过度滚动到拉伸区域多远,您仍然会看到平铺背景。

class MyViewController: NSViewController {
    @IBOutlet weak var outlineView: NSOutlineView!

    override func viewDidLoad() {
        super.viewDidLoad()
        guard let clipView = self.outlineView.superview else { return }

        let newView = MyView(frame: .zero) // Frame is set by autolayout below.
        newView.translatesAutoresizingMaskIntoConstraints = false

        clipView.addSubview(newView, positioned: .below, relativeTo: self.outlineView)

        // Add autolayout constraints to pin the new view to the clipView.
        // See https://apple.co/3c6EMcH
        newView.leadingAnchor.constraint(equalTo: clipView.leadingAnchor).isActive = true
        newView.widthAnchor.constraint(equalTo: clipView.widthAnchor).isActive = true
        newView.topAnchor.constraint(equalTo: clipView.topAnchor, constant: -2000).isActive = true
        newView.bottomAnchor.constraint(equalTo: clipView.bottomAnchor, constant: 2000).isActive = true
    }
}

我已经从上面删除了其他代码,所以希望我已经留下了说明解决方案所需的一切。