0

I have the function Track.getShadowPath() that is returning a CGMutablePath.

Function returning CGMutablePath

static func getShadowPath() -> CGMutablePath{

    let trackPath = CGMutablePath()

    let initPoint = Track.tracks.first?.initPoint


    let heigthShift = CGPoint(x: 0, y: 100)
    trackPath.move(to: initPoint! - heigthShift)

    var yShift = CGPoint(x: 0, y: 0)

    for track:Track in Track.tracks{

        let q = track.getQuadrant()
        if(q == 2 || q == 5){
            yShift.y = yShift.y + track.endPoint.y - track.initPoint.y
        }
        else {
            trackPath.addLine(to: track.endPoint - heigthShift - yShift)
        }
    }

    return trackPath
}

This path is then added to a CAShapeLayer and the layer added to my view as a sublayer. I want this path to have a shadow aspect. But when it renders, it produces a shadow as if the path was closed. How can I achieve my purpose?

NSView

class DemoView: NSView {
    var trackShape:CAShapeLayer!
    var trackShadowShape:CAShapeLayer!

override init(frame: CGRect){
    super.init(frame: frame)

    trackShape = CAShapeLayer()

    trackShape.strokeColor = NSColor.black.cgColor
    trackShape.lineWidth = 3
    trackShape.lineJoin = kCALineJoinBevel
    trackShape.lineCap = kCALineCapRound
    trackShape.fillColor = nil

    trackShadowShape = CAShapeLayer()

    trackShadowShape.lineWidth = 14
    trackShadowShape.lineJoin = kCALineJoinBevel
    trackShadowShape.lineCap = kCALineCapRound
    trackShadowShape.fillColor = nil
    trackShadowShape.shadowColor = NSColor.blue.cgColor
    trackShadowShape.shadowRadius = 10

    self.layer?.addSublayer(trackShadowShape)
    self.layer?.addSublayer(trackShape)
 }

func redrawTrack(){
    trackShape.path = Track.getCompletePath()
    trackShadowShape.shadowPath = Track.getShadowPath()
    trackShadowShape.shadowRadius = 20
    trackShadowShape.shadowOpacity = 1
    trackShadowShape.fillColor = NSColor.clear.cgColor
    trackShadowShape.lineWidth = 10

 }
}

The non-expected result

Non-expected result

gotramaval
  • 59
  • 9
  • Why are you using two shape layers? Why not just configure the track layer to draw a shadow. Don't specify the shadow path, just let Core Graphics compute it from the shape it's drawing. The docs for `shadowPath` specifically say it will be filled. – Ken Thomases Jul 18 '18 at 18:04
  • I need to use two shape layers because for my application I need the shadow not to follow the track layer sometimes. On the other hand, if shadowPath will be always filled, how do people do when they want a line-shaped shadow? – gotramaval Jul 18 '18 at 21:18
  • What do you get if, for the shadow layer, you set the `path`, you don't set the `shadowPath`, you set the `shadowOffset` to `CGSizeZero`, and the `strokeColor` the same as the `shadowColor`? That should draw the path and a shadow, and the path should be in the middle of the shadow and the same color as it, so hopefully will blend in. It might not if you're drawing over something other than white, because the path will be opaque while the shadow will be semi-transparent. You could try fiddling with the alpha of the `strokeColor`, the the more transparent it is, I think the lighter the shadow. – Ken Thomases Jul 18 '18 at 22:36
  • Tried. It draws the shadow along the path, but can't get rid of the stroke. If I set the `lineWidth to 1, shadow gets thin as well and you can't almost see it. Changing the alpha on the line color changes the alpha of the shadow as well... – gotramaval Jul 19 '18 at 09:47

1 Answers1

0

I solved this using the:

copy(strokingWithWidth lineWidth: CGFloat, lineCap: CGLineCap, lineJoin: CGLineJoin, miterLimit: CGFloat, transform: CGAffineTransform = default)

According to the docs

Returns a new path equivalent to the results of drawing the path with a solid stroke.

So basically it creates a new CGPath which is the stroke's outline of the path you provide, with the desired width, cap, etc... I made my getShadowPath function return this new calculated path:

return trackPath.copy(strokingWithWidth: 6, lineCap: CGLineCap.round, lineJoin: CGLineJoin.round, miterLimit: 10) as! CGMutablePath

Then I used that path as shadowPath for my layer. The results are now the expected ones:

Expected results

A similar answer in Objective-c can be found here

gotramaval
  • 59
  • 9