예전에 코어 그래픽을 사용해서 UIView
위에 손가락으로 그림을 그릴 수 있는 간단한 핑거 드로잉 캔버스를 구현해본 바 있는데, 똑같은 내용을 NSView
에 적용해보고자 한다. 이전글이 Objective-C로 작성되어 있는데, 이번에는 Swift로 간단하게 작성해보려 한다. 원리는 동일하다. CGLayer
를 하나 만들고, 마우스를 사용해서 뷰를 긁을 때(드래그할 때)마다 코어 그래픽을 사용해서 레이어에 그림을 그리고, 다시 뷰 리드로잉 사이클에서는 뷰에 레이어를 그리는 것이다.
class TrackDrawCanvasView: NSView {
var previousPoint: NSPoint? = nil
lazy var drawingLayer: CGLayer? = { [unowned self] in
let cs = CGColorSpace(name:CGColorSpace.sRGB)!
let ctx = CGContext(data: nil,
width: 100,
height: 100,
bitsPerComponent: 8,
bytesPerRow: 0,
space: cs,
BitmapInfo: CGImageAlphaInfo.nonSkipLast.rawValue)
if let ctx = ctx {
let layer = CGLayer(ctx, size: self.bounds.size, auxiliaryInfo: nil)
return layer
}
return nil
}()
lazy var drawingContext: CGContext? = { [unowned self] in
let ctx = self.drawingLayer?.context
// 그래픽 컨텍스트 셋업
ctx?.setStrokeColor(NSColor.green.cgColor)
ctx?.setLineWidth(3.0)
// 이벤트 위치를 프레임만큼 보정
ctx?.translateBy(x:-self.frame.origin.x, y:-self.frame.origin.y)
return ctx
}
}
터치할 때 마우스 위치와 선이 그려지는 위치를 맞추기 위해서 좌표계를 뷰의 위치만큼 거꾸로 이동시켰다. 다음은 화면을 그릴 차례이다. NSEvent는 드래그에 대해서 이전 위치를 가지고 있지 않기 때문에 이전 위치를 추적해 나가야 한다.
///
var previousPoint: NSPoint? = nil
override func mouseDown(wit틀h event: NSEvent) {
previousPoint = event.locationInWindow
}
override func mouseDown(with event: NSEvent) {
previousPoint = nil
}
override func mouseDragged(with event: NSEvent) {
let currentPoint = event.locationInWindow
drawingContext?.beginPath()
drawingContext?.move(to: previousPoint)
drawingContext?.addLine(to: currentPoint)
drawingContext?.strokePath()
previousPoint = currentPoint
needsDisplay = true
}
최종적으로 뷰를 그릴 때는 레이어를 그려주면 된다.
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
if let ctx = NSGraphicsContext.current?.cgContext,
let layer = drawingLayer {
ctx.draw(layer, at: CGPoint.zero)
}
}
인터페이스 빌더에서 뷰를 윈도에 하나 올린 후 드래실행해보자. 끝!
참고
CGLayer
를 이용한 핑거 드로잉 구현NSGraphicsContext
Class Referenceinit?(data:width:height:bitsPerComponents:bytesPerRow:space:bitmapInfo:)
–CGContext
CGImageAlphaInfo