Reputation: 501
Is it possible to create a shadow around a UIVisualView with UIBlurEffect without letting the UIVisualView get coloured by the shadow underneath?
I basically just want the shadow around the view but with this code the shadow will cover the whole view which darkens the whole view to much:
let borderPath = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 15, height: 15)).cgPath
shadowView.frame = view.bounds
shadowView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
shadowView.layer.shadowOpacity = 0.3
shadowView.layer.shadowRadius = 3.0
shadowView.backgroundColor = UIColor.clear
shadowView.layer.shadowPath = borderPath
shadowView.layer.shadowOffset = CGSize(width: 0, height: 0)
self.view.insertSubview(shadowView, at: 0)
let blurEffect = UIBlurEffect(style: .extraLight)
let blurView = UIVisualEffectView(effect: blurEffect)
blurView.frame = view.bounds
blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
blurView.clipsToBounds = true
blurView.layer.cornerRadius = 15
view.insertSubview(blurView, aboveSubview: shadowView)
EDIT.
I need to achieve the same thing as in Apple's Maps application. Where the draggable favourite view both uses the UIVisualEffectView and a shadow around its top, without interfering with the UIVisualEffectView's background.
Upvotes: 9
Views: 3653
Reputation: 891
I usually solve this kind of situation using composition of views. Instead of setting the shadow in the target view, I create a ShadowView and I put it behind the target view, both views with the same frame.
I've posted and example and code in this question.
An example of the result of this approach is the following:
Upvotes: 0
Reputation: 54101
The trick is to not set the shadowPath
of the layer. If it is set, the shadow is painted below the visual effect view and, in consequence, darkens the blurred view.
The documentation for shadowPath
states:
If you specify a value for this property, the layer creates its shadow using the specified path instead of the layer’s composited alpha channel.
The "instead…" part is what we actually need: the shadow should be composed after rendering the content, based on what the content leaves transparent. Now you might be deterred from not setting a shadowPath
because the documentation also states that an "explicit path usually improves rendering performance". However, I didn't see any issues in real life so far. In comparison to the rendering cost of the UIVisualEffectView
, painting the shadow doesn't make a difference, I assume.
Also make sure to set the shadow on a superview of the UIVisualEffectView
, not on a sibling view.
Upvotes: 1
Reputation: 2260
I found in iOS 12, this is not a problem, the UIVisualEffectView
ignore the shadow of underneath views, it just sees through the shadow, like the shadow not exist.
Upvotes: 2
Reputation: 501
Ok, so the problem was that my background in the underlying view was white. And with the UIBlurEffect .extraLight used on a background which is lighter than the BlurEffect the shadow beneath a UIVisualView appears darker than with a more vivid background.
Also described in this question:
Fix UIVisualEffectView extra light blur being gray on white background
UPDATE
I found a project explaining how to solve this on Github The solution involves creating a 9-part UIImage to represent the shadow. The creator also explains the underlying layers of the iOS 10 Maps.
Upvotes: 5
Reputation: 12668
So i'm trying to recreate the look of iOS 10 maps. I decided to attach the maps app in the simulator to the debugger to see what was going on...
Apple actually get around this by having a UIImage over the top of the content with the border and shadow. Not the most elegant way to do it but i'm going for the exact look so I'm going to take the exact approach.
I also grabbed the asset (using this) from the Maps app to save making your own one. Shame they only have @2x artwork in it though :/
Upvotes: 3