简体   繁体   中英

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?

What you need to add is:

self.view.wantsLayer = true

I tried running the following code; the layer does show as well as the shadow:

let layer = CALayer()
layer.frame = CGRect(x: 50, y: 50, width: 100, height: 100)

layer.backgroundColor = NSColor.redColor().CGColor

layer.shadowOffset = CGSize(width: 0, height: -3)
layer.shadowRadius = 3
layer.shadowColor = NSColor.blackColor().CGColor
layer.shadowOpacity = 1.0 //Or: 0.3 as you originally have
self.view.wantsLayer = true
self.view.layer?.addSublayer(layer)

By the way, you have a typo on the following line:

NSColor.black().cgColor

as it should be:

NSColor.blackColor().CGColor

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 .

NSView sets the shadow related properties on CALayer at predictable times, like when wantsLayer is set to true and when the view is added to a superview. Set the layer shadow properties after NSView and the shadow will be visible. I set the shadow properties during resizeSubviews .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM