Johannes Jakob
Johannes Jakob

Reputation: 37

Save Path from CGContext in Cocoa

I'm trying to write a small drawing app for macOS in Swift using Cocoa. The problem is that the last drawn line disappears when the NSView is drawn again. Is there a way to keep the drawn lines? I could save the points in an array, but that would drastically decrease the performance. Unfortunately, the CGContextSaveGState function doesn't save paths.

Here's my class for the NSView I'm drawing in (with a mouse or a stylus):

import Cocoa

class DrawingView: NSView {
  // Variables

  var lastPoint = CGPoint.zero
  var currentPoint = CGPoint.zero

  var red: CGFloat = 0.0
  var green: CGFloat = 0.0
  var blue: CGFloat = 0.0
  var alpha: CGFloat = 1.0

  var brushSize: CGFloat = 2.0

  var dragged = false

  // Draw function

  override func drawRect(rect: NSRect) {
    super.drawRect(rect)

    let context = NSGraphicsContext.currentContext()?.CGContext

    CGContextMoveToPoint(context, lastPoint.x, lastPoint.y)

    if !dragged {
      CGContextAddLineToPoint(context, lastPoint.x, lastPoint.y)
    } else {
      CGContextAddLineToPoint(context, currentPoint.x, currentPoint.y)

      lastPoint = currentPoint
    }

    CGContextSetLineCap(context, .Round)
    CGContextSetLineWidth(context, brushSize)
    CGContextSetRGBStrokeColor(context, red, green, blue, alpha)
    CGContextSetBlendMode(context, .Normal)

    CGContextDrawPath(context, .Stroke)
  }

  // Mouse event functions

  override func mouseDown(event: NSEvent) {
    dragged = false

    lastPoint = event.locationInWindow

    self.setNeedsDisplayInRect(self.frame)
  }

  override func mouseDragged(event: NSEvent) {
    dragged = true

    currentPoint = event.locationInWindow

    self.setNeedsDisplayInRect(self.frame)
  }

  override func mouseUp(event: NSEvent) {
    self.setNeedsDisplayInRect(self.frame)
  }
}

Upvotes: 2

Views: 527

Answers (2)

matt
matt

Reputation: 535086

You are looking for CGContextCopyPath. It hands you a CGPath pseudo-object. The easiest way to preserve that it is to stuff into an NSBezierPath as its path, because an NSBezierPath is a real object and ARC will memory-manage it for you.

Upvotes: 1

Rob
Rob

Reputation: 437482

For this scenario, I'd step out of CoreGraphics and use NSBezierPath. You can save that in a property. You can then just call lineToPoint as more points come in and then call stroke to draw it. For example:

let strokeColor = NSColor(red: red, green: green, blue: blue, alpha: 1.0)

let path = NSBezierPath()
path.lineWidth = brushSize
path.lineCapStyle = .RoundLineCapStyle
path.lineJoinStyle = .RoundLineJoinStyle

path.moveToPoint(lastPoint)
path.lineToPoint(currentPoint)
...

strokeColor.setStroke()
path.stroke()

Upvotes: 1

Related Questions