2

I am trying to achieve a transparency path in the drawRect method. This is the simple code I have created:

override func drawRect(rect: CGRect) {
    let clippingPath = UIBezierPath()

    UIColor.whiteColor().set();
    clippingPath.moveToPoint(CGPoint(x: 10, y: CGRectGetHeight(self.bounds) / 2))
    clippingPath.addLineToPoint(CGPoint(x: CGRectGetWidth(self.bounds) - 10, y: CGRectGetHeight(self.bounds) / 2))
    clippingPath.lineWidth = 6
    clippingPath.lineCapStyle = .Round
    clippingPath.stroke()
}

And this is the result:

enter image description here

Is there a way of trying to keep the background solid but the path line transparent. If I change the second line to UIColor.clearColor().set() nothing seems to happen, I just get a full solid background colour (in this case black.

Hamish
  • 78,605
  • 19
  • 187
  • 280
Tomus85
  • 57
  • 1
  • 6

2 Answers2

3

You want to draw the path with a blend mode of kCGBlendModeDestinationOut (and a solid color stroke).

According to the docs, this blend mode does the following:

R = D*(1 - Sa)

Where...

  • R is the premultiplied result.

  • S is the source color, and includes alpha

  • D is the destination color, and includes alpha

  • Ra, Sa, and Da are the alpha components of R, S, and D

This way, when used with a solid stroke color, the drawn path will be 'transparent'.

The reason clearColor won't work for you is because the default blend mode is additive, therefore the resultant color will be unaffected by the drawing a color with an alpha of 0 over it. DestinationOut on the other hand is subtractive.

So you'll want to do something like the following:

override func drawRect(rect: CGRect) {

    let clippingPath = UIBezierPath()

    let context = UIGraphicsGetCurrentContext() // get your current context

    // draw your background color
    UIColor.greenColor().set();
    CGContextFillRect(context, bounds)

    CGContextSaveGState(context) // save the current state

    CGContextSetBlendMode(context, .DestinationOut) // change blend mode to DestinationOut (R = D * (1-Sa))

    // do 'transparent' drawing

    UIColor.whiteColor().set();
    clippingPath.moveToPoint(CGPoint(x: 10, y: CGRectGetHeight(self.bounds) / 2))
    clippingPath.addLineToPoint(CGPoint(x: CGRectGetWidth(self.bounds) - 10, y: CGRectGetHeight(self.bounds) / 2))
    clippingPath.lineWidth = 6
    clippingPath.lineCapStyle = .Round
    clippingPath.stroke()

    CGContextRestoreGState(context) // restore state of context

    // do further drawing if needed
}

Note:

You'll have to set the opaque property of the view to false for this to work, otherwise UIKit will assume it has opaque contents. For example:

override init(frame: CGRect) {
    super.init(frame: frame)
    opaque = false
}
Hamish
  • 78,605
  • 19
  • 187
  • 280
  • Thank you. I have tried this, however I am getting a black line now. I've added this view on top of a red uiview expecting to see a red line but it is still showing it as black – Tomus85 Mar 01 '16 at 14:43
  • ah yes, I forgot to mention that you should set the `backgroundColor` of the `UIView` to `clearColor`. You can then draw your background color from within `drawRect`. Doing so should fix the problem. – Hamish Mar 01 '16 at 14:56
  • @Tomus85 hmm, weird... works for me. [Have a look at this project I just put together](https://github.com/originaluser2/Transparent-Paths). Are you sure you aren't changing the `backgroundColor` to anything other than `clearColor` from within the subclass itself? – Hamish Mar 01 '16 at 15:04
  • thank you, you're a star. I missed out `UIColor.greenColor().set(); CGContextFillRect(context, bounds)` – Tomus85 Mar 01 '16 at 15:10
  • @Tomus85 actually a better way of ensuring UIKit keeps the view transparent is simply to set the `opaque` property to `false`, rather than messing with the `backgroundColor`. I have updated my answer with this :) – Hamish Mar 01 '16 at 15:11
0

A different approach, which might be simpler, is to just add the stroke's CAShapeLayer as a maskLayer of the view to be revealed, and then put the color overlay in another view behind the view being masked. Thus, where ever the marked view is masked, your solid color would appear.

Rob
  • 415,655
  • 72
  • 787
  • 1,044