Swift: NSImageViewにCAShapeLayerを重ねる。


要は添付画像のように画像の上にPathを重ねて表示したい。しかし、普通に何も気にせずにそれをやろうとすると、Pathが画像の下に隠れてしまうことがある。

まず仕様として、NSImageView.image = NSImage()で追加された画像は、NSImageView.layerのサブレイヤーになる。

NSImageViewにCAShapeLayerを追加する場合は追加するタイミングを気にするか、layerのzPositionを制御すればいい。
例えば、以下のような初期化について考える。

class CustomViewController: NSViewController {

    @IBOutlet weak var imageView: NSImageView!
    let shapeLayer = CAShapeLayer()

    override func viewDidLoad() {
        super.viewDidLoad()

        imageView.wantsLayer = true

        // この順番だと画像より下にshapeLayerが配置されてしまう。
        imageView.layer?.addSublayer(shapeLayer)
        imageView.image = NSImage(named: "Sample")

        // ちゃんと表示される一例
        imageView.image = NSImage(named: "Sample")
        imageView.layer?.addSublayer(shapeLayer)
    }
}

順番を気にするだけでいいじゃんとなるが、あとでimageを頻繁に変更する場合は、変更後に重なり順序が変わってしまい、思い通りにならない。
imageを更新する度にlayerを追加し直すというのが一つの解決策。

imageView.image = NSImage(named: "New Image")
shapeLayer.removeFromSuperlayer()
imageView.layer?.addSublayer(shapeLayer)

でも面倒臭いし、どこかでやり忘れたら重なり順が狂う。
そういう時は予めlayerのzPositionを指定しておくといい。

shapeLayer.zPosition = 5 // いい感じの値
imageView.layer?.addSublayer(shapeLayer)

複数CAShapeLayerを重ねて使いたい時は希望する順序になるようにzPositionを指定すればいい。