0

I am building a demo drawing application. I am using touchesMoved:withEvent to collect my points and adding them to a CGMutablePathRef. To stroke the path, I override DrawRect, add the path to the context and stroke the path:

   override func drawRect(rect: CGRect) {
    self.backgroundColor?.set()
    UIRectFill(rect)

    let context : CGContextRef = UIGraphicsGetCurrentContext()
    for line in pathArray {
        CGContextAddPath(context, line.structPath)
        CGContextSetLineWidth(context, line.structLineWidth)
        CGContextSetStrokeColorWithColor(context, line.structLineColor.CGColor)
        CGContextSetAlpha(context, lineOpacity)

    }
    CGContextSetLineCap(context, kCGLineCapRound)
    CGContextStrokePath(context)

    self.empty = false
}

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    if let touch = touches.first as UITouch! {
        previousPoint = touch.previousLocationInView(self)
        previousPreviousPoint = touch.previousLocationInView(self)
        currentPoint = touch.locationInView(self)
    }
    self.touchesMoved(touches, withEvent: event)
}

override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {


     if let touch = touches.first as UITouch! {

        previousPreviousPoint = previousPoint
        previousPoint = touch.previousLocationInView(self)
        currentPoint = touch.locationInView(self)

        let mid1 : CGPoint = getMidPoint(previousPoint, p2: previousPreviousPoint)
        let mid2 : CGPoint = getMidPoint(currentPoint, p2: previousPoint)

        let subpath : CGMutablePathRef = CGPathCreateMutable()
        CGPathMoveToPoint(subpath, nil, mid1.x, mid1.y)
        CGPathAddQuadCurveToPoint(subpath, nil, previousPoint.x, previousPoint.y, mid2.x, mid2.y)

        let bounds : CGRect = CGPathGetBoundingBox(subpath)
        let drawBox : CGRect = CGRectInset(bounds, -2.0 * lineWidth, -2.0 * lineWidth)

        let newLine = line(newPath: subpath)
        pathArray.append(newLine)
        self.setNeedsDisplayInRect(drawBox)
    }
}

The above code works as expected, except I am seeing a unexpected result. The "draw box" which gets the bandbox and sets a CGRectInset changes the lineColor of other paths already drawn:

Drawing Problem

I understand (sort of) why this is happening, but cannot find a solution to this problem. Any suggestions would be most appreciated!

Andrew Walz
  • 890
  • 2
  • 8
  • 27
  • What exactly does "more flexible" mean to you? What additional functionality do you want? – Kurt Revis Jul 17 '15 at 06:39
  • I'm having real problems with redrawing the view. When I want to change stroke color, the entire drawing changes color. When I try to remove the last path added, it doesn't redraw. I guess in general, I can't find a good way to add different CGContexts for different paths and have them all drawn in DrawRect – Andrew Walz Jul 17 '15 at 07:15
  • It's entirely possible to draw multiple lines, with different stroke colors, in a single CGContext inside -drawRect:. You might want to rephrase your question and include more details about how you're trying to do things. (Hint: each time -drawRect: is called, you are starting over from scratch -- you can't paint on top of what you drew the last time, or erase it. If you want to draw more than one path or color, you need to keep more than one `path` or `lineColor` value.) – Kurt Revis Jul 18 '15 at 03:40
  • Thank you for the assistance and the hint. I have updated the question with the major issue I am encountering – Andrew Walz Jul 18 '15 at 04:49

1 Answers1

2

You want to stroke each path separately, like this:

override func drawRect(rect: CGRect) {
    let context : CGContextRef = UIGraphicsGetCurrentContext()

    // Set parameters in the context that are the same for all lines
    CGContextSetLineCap(context, kCGLineCapRound)
    CGContextSetAlpha(context, lineOpacity)

    for line in pathArray {
        // Set parameters in the context that are specific to this line
        CGContextSetLineWidth(context, line.structLineWidth)
        CGContextSetStrokeColorWithColor(context, line.structLineColor.CGColor)

        // Add the line's path to the context's current path:
        CGContextAddPath(context, line.structPath)
        // And stroke it.
        CGContextStrokePath(context)
        // (This has the side effect of clearing the context's current path.)
    }
}

The CGContext keeps track of a current path. Think of it as a CGMutablePath that you don't have direct access to. You can affect it by calling functions like CGContextAddPath or many others.

The CGContext's current path is not actually visible until you tell the context to draw it, by calling a function like CGContextStrokePath. At that time, the context uses the current path and the other values in the context (stroke color, alpha, line width, etc.) to figure out what to draw, and draws it.

Your code was adding each line's path into the current path, so you ended up with the current path containing several disconnected subpaths. Then you called CGContextStrokePath once at the end, and all of those subpaths were drawn using the values of the parameters that you most recently set. So you saw only the last line's width and color.

Kurt Revis
  • 27,695
  • 5
  • 68
  • 74
  • I appreciate your time Kurt. This makes a lot of sense and has resolved the issues I was experiencing. It has created an unexpected side effect of drawing all the endpoints over again where the paths meet, which is visible when the opacity is set to something other than 1. Is this a side effect of cleaning the context's current path? [Link](http://i.imgur.com/6oA0z92.png?1) – Andrew Walz Jul 20 '15 at 01:58
  • That's because the ends of each line's path overlap, so you are drawing two things on top of each other, and the result is darker. To fix that, you could use a [transparency layer](https://developer.apple.com/library/mac/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_trans_layers/dq_trans_layers.html#//apple_ref/doc/uid/TP30001066-CH210-TPXREF101) around the drawing of all the lines, or you could set your view's `alpha` instead of the drawing's alpha. The latter is almost certainly faster, but less flexible. – Kurt Revis Jul 20 '15 at 03:28
  • @KurtRevis Could you help me with my drawRect issue? http://stackoverflow.com/questions/39855349/make-2-contradictory-methods-work-in-drawrect – Andy Jazz Oct 16 '16 at 15:27