LoginSignup
0
1

More than 1 year has passed since last update.

[macOS Swift] ビューに図形を描く3種類の方法

Last updated at Posted at 2021-10-24

macOS Mojava 10.14.6 / Xcode 11.3.1 / Swift 5.0

(1) CALayerクラスを利用する

[v1]

図形を画面に貼り付けていくようなイメージである。図形も図形を貼り付ける画面もレイヤーオブジェクトの一つである。レイヤーは階層的に重ねていくことができる。

図形は、CALayerクラスから継承したCAShapeLayerオブジェクトとして作成し、描画は NSBezierPathクラスのメソッドを使う。作成した図形のレイヤーは addSublayerメソッドにより親ビューに付属するレイヤーに貼り付ける。サンプルコードでは、図形の貼り付け先はウィンドウのコンテントビューのレイヤーとしている。

なお、コード中の NSBezierPathクラスの cgPathメソッドは、NSBezierPath を CGPathに変換する極めて汎用的な処理にも関わらずフレームワークで提供されていないため、自前のExtensionとして実装している。コードは stack overflow を参考にした。

AppDelegate

func applicationDidFinishLaunching(_ aNotification: Notification) {
    self.window.contentView?.layer?.backgroundColor = NSColor.white.cgColor

    //円の作成
    let shape1 = CAShapeLayer()
    let path1 = NSBezierPath(ovalIn: NSRect(x: 10, y: 10,
                                            width: 100, height: 100))
    shape1.path = path1.cgPath
    shape1.fillColor = NSColor.cyan.cgColor
    shape1.lineWidth = 5
    shape1.strokeColor = NSColor.black.cgColor
    self.window.contentView?.layer?.addSublayer(shape1)

    //線の作成
    let shape2 = CAShapeLayer()
    let path2 = NSBezierPath()
    path2.move(to: NSPoint(x: 60, y: 60))
    path2.line(to: NSPoint(x: 260, y: 260))
    shape2.path = path2.cgPath
    shape2.lineWidth = 3
    shape2.strokeColor = NSColor.blue.cgColor
    self.window.contentView?.layer?.addSublayer(shape2)
}

(2) drawメソッドをオーバーライドする
NSBezierPathクラスにより描画する

[v2]

NSViewクラスのサブクラスを作成し、drawメソッドの中で NSBezierPathクラスのメソッドにより図形を描画する。

class ViewBezier: NSView {

    override func draw(_ dirtyRect: NSRect) {      
        //円の描画
        let path1 = NSBezierPath(ovalIn: NSRect(x: 10, y: 10, width: 100, height: 100))
        NSColor.cyan.set()
        path1.fill()
        NSColor.black.set()
        path1.lineWidth = 5  //枠線
        path1.stroke()

        //線の描画
        NSColor.blue.set()
        let path2 = NSBezierPath.init()
        path2.lineWidth = 3
        path2.move(to: NSPoint(x: 60, y: 60))
        path2.line(to: NSPoint(x: 260, y: 260))
        path2.stroke()
    }   
}

AppDelegate

func applicationDidFinishLaunching(_ aNotification: Notification) {
    self.window.contentView?.layer?.backgroundColor = NSColor.white.cgColor
    //ビューの作成
    let view = ViewBezier.init(frame: self.window.contentView!.frame)
    self.window.contentView?.addSubview(view)
}

(3) drawメソッドをオーバーライドする
Core Graphics機能により描画する

[v3]

NSViewクラスのサブクラスを作成し、drawメソッドの中でスクリーンの Graphic Contextを取得し、そこに CGContextクラスののメソッドを使用して図形を描画する。

class ViewContext: NSView {

    override func draw(_ dirtyRect: NSRect) {
        //円の描画
        let context:CGContext? = NSGraphicsContext.current?.cgContext
        context?.addEllipse(in: CGRect.init(x: 10, y: 10,
                                            width: 100, height: 100))
        context?.setFillColor(NSColor.cyan.cgColor)
        //context?.fillPath() //NG

        //枠線の描画
        context?.setLineWidth(5)
        context?.setStrokeColor(NSColor.black.cgColor)
        //context?.strokePath() //NG

        //コンテキストに円と枠線を一緒に出力する(NGのように別々に描画すると枠線が出力されない)
        context?.drawPath(using: .fillStroke)

        //線の描画
        context?.move(to: CGPoint(x: 60, y: 60))
        context?.addLine(to:CGPoint(x: 260, y: 260))
        context?.setStrokeColor(NSColor.blue.cgColor)
        context?.strokePath()    
    }
}

AppDelegate

func applicationDidFinishLaunching(_ aNotification: Notification) {
    self.window.contentView?.layer?.backgroundColor = NSColor.white.cgColor
    //ビューの作成
    let view = ViewContext.init(frame: self.window.contentView!.frame)
    self.window.contentView?.addSubview(view)
}

[補足]

上記3種類のうち、いずれの方法をとるかはアプリケーションの性格次第であろう。
パフォーマンスの良さは (3) > (2) > (1)
機能の豊富さ、使い勝手の良さから言えば (1) > (2) > (3) であろう。

多数の図形を描画する場合には、(2)の応用として、一つの図形を一つのNSViewのサブクラスに描画し、それらを親ビューに貼り付けるという方法でも可能である。CALayerクラスを利用する方法でも描画自体は NSBezierPathクラスを利用しているので、結局どちらも同じでような手順を踏んでいる。CALayerクラスの採用は、つまるところ、図形の変形や回転、アニメーション、色のグラデーションなどといった多様な機能を利用するか否かに依る。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1