Search code examples
swiftcocoacore-animationcalayernsview

macOS: CALayer shadow not showing


I need a shadow on my view. I tried using the view’s NSShadow capability, but it is too slow to use in a scroll view. I want to try using the layer’s shadow properties to hopefully improve performance.

In my NSView.updateLayer() method, I set the following properties:

layer.shadowOffset = CGSize(width: 0, height: -3)
layer.shadowRadius = 3
layer.shadowColor = NSColor.black().cgColor
layer.shadowOpacity = 0.3

No shadow is shown. I tried also setting NSView.wantsDefaultClipping and CALayer.masksToBounds to false, but there is still no shadow.

Why is there no shadow when using the CALayer shadow properties?


Solution

  • I looked at the disassembly of CALayer.render(in:) and it looked like it was properly accessing the layer shadow properties. So NSView probably overwrites the layer shadow properties with its own on every draw cycle. The bottom line is that you can only add shadows to view-backing layers by using the shadow property on the view.

    I did solve my scroll performance problem though. I profiled my app during a scroll and noticed that the creation of the shadow image in CGContextEndTransparencyLayer was causing the CPU to spike.

    There are two steps for creating a shadow. First, a path for the shadow has to be computed based on the alpha channel of the pixels above. Second, a gaussian blur is applied in order to soften the edges of the shadow.

    Since I know the view on top of the shadow is fully opaque, I know the path will be simply the view’s bounds. I could have skipped the first step by setting the layer’s shadowPath property. But unfortunately, the layer’s shadow properties are overridden by the view which doesn’t have a shadowPath property.

    My solution was to create a container view that draws a rectangular shadow image underneath the content view. This shadow image is created once and cached, increasing scroll performance dramatically. And thanks to the power of Auto Layout (specifically, the alignment rect insets), the container view can be used without having to manually adjust for the shadow.

    You can view my code on GitHub.