52 Replies
      Latest reply on Nov 14, 2017 3:00 PM by UITextFielder
      ddunham Level 1 Level 1 (10 points)

        I’ve got a UIVisualEffectView that I mask with a path. This works fine in iOS 9 (and I think iOS 8, though I don’t support that any more).

         

        In iOS 10, there is no actual blur effect (though the masking still happens). There’s just an alpha background. Is this a known change?

         

        My initializer does

         

        CAShapeLayer* mask = [CAShapeLayer layer];
        mask.path = path.CGPath;
        self.layer.mask = mask;
        
        • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
          KMT Level 9 Level 9 (14,015 points)

               >Is this a known change?

           

          For that, check the release notes and diffs.

          • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
            wkiefer Level 1 Level 1 (0 points)

            I'm seeing the same issue - I've filed radar 27189321

              • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                ddunham Level 1 Level 1 (10 points)

                OK, Radar 27227280 for me.

                  • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                    Chad S. Level 1 Level 1 (15 points)

                    Seeing the same, Radar is 27393759

                      • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                        Chad S. Level 1 Level 1 (15 points)

                        Still an issue in beta 3, updated Radar 27393759 with a sample project


                        import UIKit
                        class BlurBacking: UIView {
                            var blurView : UIVisualEffectView!
                            override init(frame: CGRect) {
                                super.init(frame: frame)
                                self.backgroundColor = UIColor.blueColor()
                                let colorView = UIView(frame: CGRect(origin: CGPoint(x:20.0, y:20.0), size: CGSize(width:120.0, height:120.0)))
                                colorView.backgroundColor = UIColor.redColor()
                                self.addSubview(colorView)
                                let blur = UIBlurEffect(style: .Light)
                                let blurView = UIVisualEffectView(effect: blur)
                                blurView.frame = CGRect(origin: CGPoint(x:40.0, y:40.0), size: CGSize(width:240.0, height:240.0))
                                self.addSubview(blurView)
                                self.blurView = blurView
                            }
                        
                            required init?(coder aDecoder: NSCoder) {
                                fatalError("init(coder:) has not been implemented")
                            }
                        }
                        class ViewController: UIViewController {
                            override func viewDidLoad() {
                                super.viewDidLoad()
                        
                        //UIVisual effects view behaves as expected without masking a layer
                                let unmaskedBlur = BlurBacking(frame:CGRect(origin: CGPoint(x:0.0, y:0.0), size: CGSize(width:view.frame.size.width, height:view.frame.size.height * 0.5)))
                                view.addSubview(unmaskedBlur)
                        
                        // Adding a mask layer to the UIVisualEffects view breaks the blur
                                let maskedBlur = BlurBacking(frame:CGRect(origin: CGPoint(x:0.0, y:view.frame.size.height * 0.5), size: CGSize(width:view.frame.size.width, height:view.frame.size.height * 0.5)))
                                view.addSubview(maskedBlur)
                                let maskLayer = CAShapeLayer()
                                maskLayer.frame = CGRect(origin: CGPoint(x:10.0, y:10.0), size: CGSize(width:140.0, height:140.0))
                                maskLayer.path = UIBezierPath(ovalInRect: maskLayer.bounds).CGPath
                                maskedBlur.blurView.layer.mask = maskLayer
                            }
                        }
                        
                        
                          • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                            Chad S. Level 1 Level 1 (15 points)

                            I received a message from my Radar 27393759 that masking the layer property of a UIVisualEffectsView will not work, and was encouraged to use the maskView property on the UIVisualEffects view.  However, this does not work for me on either iOS 9 or 10.  I've updated the Radar and sent them an updated sample project showing how the suggested technique does not work.  Will update with more info when I hear more.

                             

                            Quoted from Radar:

                            Masking the layer of a visual effect view is not guaranteed to produce the correct results – in some cases on iOS 9 it would produce an effect that looked correct, but potentially sourced the wrong content. Visual effect view will no longer source the incorrect content, but the only supported way to mask the view is to either use cornerRadius directly on the visual effect view’s layer (which should produce the same result as you are attempting here) or to use the visual effect view’s maskView property.

                             

                            My updated sample code:

                            import UIKit
                            class BlurBacking: UIView {
                                var blurView : UIVisualEffectView!
                                var regularView : UIView!
                                override init(frame: CGRect) {
                                    super.init(frame: frame)
                                    self.backgroundColor = UIColor.blueColor()
                                    let colorView = UIView(frame: CGRect(origin: CGPoint(x:20.0, y:80.0), size: CGSize(width:280.0, height:80.0)))
                                    colorView.backgroundColor = UIColor.redColor()
                                    self.addSubview(colorView)
                            
                                    let blur = UIBlurEffect(style: .Light)
                                    let blurView = UIVisualEffectView(effect: blur)
                                    blurView.frame = CGRect(origin: CGPoint(x:40.0, y:40.0), size: CGSize(width:80.0, height:80.0))
                                    self.addSubview(blurView)
                                    self.blurView = blurView
                            
                                    let normalView = UIView(frame: CGRect(origin: CGPoint(x:140.0, y:40.0), size: CGSize(width:80.0, height:80.0)))
                                    normalView.backgroundColor = UIColor.greenColor()
                                    self.addSubview(normalView)
                                    regularView = normalView
                                }
                                required init?(coder aDecoder: NSCoder) {
                                    fatalError("init(coder:) has not been implemented")
                                }
                            }
                            
                            class ViewController: UIViewController {
                                func maskFrame() -> CGRect {
                                    return CGRect(origin: CGPoint(x:10.0, y:10.0), size: CGSize(width:60.0, height:60.0))
                                }
                            
                                func maskView() -> UIView {
                                    let maskView = UIView(frame: maskFrame())
                                    maskView.backgroundColor = UIColor.blackColor()
                                    maskView.layer.cornerRadius = 8.0
                                    return maskView
                                }
                            
                                func maskLayer() -> CALayer {
                                    let maskLayer = CAShapeLayer()
                                    maskLayer.frame = maskFrame()
                                    maskLayer.path = UIBezierPath(roundedRect: maskLayer.bounds, cornerRadius: 8.0).CGPath
                                    return maskLayer
                                }
                            
                                override func viewDidLoad() {
                                    super.viewDidLoad()
                            
                                    //UIVisual effects view behaves as expected without masking a layer
                                    let unmaskedBlur = BlurBacking(frame:CGRect(origin: CGPoint(x:0.0, y:0.0), size: CGSize(width:view.frame.size.width, height:view.frame.size.height * (1.0/3.0))))
                                    view.addSubview(unmaskedBlur)
                            
                                    // Adding a mask layer to the UIVisualEffects view breaks the blur, works with UIView
                                    let maskedBlur = BlurBacking(frame:CGRect(origin: CGPoint(x:0.0, y:view.frame.size.height * (1.0/3.0)), size: CGSize(width:view.frame.size.width, height:view.frame.size.height * (1.0/3.0))))
                                    view.addSubview(maskedBlur)
                                    maskedBlur.blurView.layer.mask = maskLayer()
                                    maskedBlur.regularView.layer.mask = maskLayer()
                            
                                    //Use View to mask a UIVisualEffectsView and a UIView
                                    let maskViewBlur = BlurBacking(frame: CGRect(origin: CGPoint(x:0.0, y:view.frame.size.height * ((1.0/3.0) * 2.0)), size: CGSize(width:view.frame.size.width, height:view.frame.size.height * (1.0/3.0))))
                                    view.addSubview(maskViewBlur)
                                    maskViewBlur.blurView.maskView = maskView()
                                    maskViewBlur.regularView.maskView = maskView()
                                }
                            }
                            
                              • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                Rincewind Apple Staff Apple Staff (280 points)

                                Try setting the mask after -viewDidLoad (such as in -viewWillLayoutSubviews). If that works, then the likely issue was an additional bug we fixed after beta3.

                                 

                                Masking the visual effect view's layer still ends up breaking the effect, but we found another issue where sometimes we didn't capture the mask properly in the first place.

                                 

                                That said, if you don't mind using more tranditional rounded corners, just setting cornerRadius=8 and clipsToBounds=YES would probably do what you want without nearly the complexity here.

                                  • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                    ddunham Level 1 Level 1 (10 points)

                                    Going to look into this but corner radius is not the issue in 27227280, my mask is a cartoon text balloon.

                                      • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                        Rincewind Apple Staff Apple Staff (280 points)

                                        I'm not certain what you were told, but the root issue isn't anything like corner radius, but rather how visual effect view works, and how masking impacts that functionality.

                                         

                                        UIVisualEffectView works by capturing the content behind it, and then applying filters (such as blur) to that content. This is important to understand because when visual effect view goes to do that capture, it can only capture what is in the current render buffer. But when you do various tasks that require an offscreen pass (such as masking) that creates a new render buffer that then causes the capture phase to capture less content – only what is in that current render buffer.

                                         

                                        So if you apply a mask to a UIVisualEffectView via the CALayer.mask property, you end up putting the whole visual effect view into an offscreen pass, which means it cannot capture the content it needs to render. If you use the UIView.maskView UIVisualEffectView works around this by taking a snapshot of the mask instead, and applying it in the correct places to ensure that the capture can still do the correct thing.

                                         

                                        One bug that we fixed recently had to do with snapshots of that maskView not working – that bug exists in Beta3.

                                         

                                        That is why it is important to use the UIView.maskView property when working with a UIVisualEffectView instead of the CALayer.mask property, as otherwise the effect will be broken. Other bugs allowed this to work prior to iOS 10 in limited circumstances, but usually by causing incorrect rendering.

                                         

                                        Now corner radius came up with one of the reported issues because the developer was basically just using a rounded rectangle mask – cornerRadius+clipsToBounds does not have the issue that general masking does, so that would work without any effort. For most of your masking needs, I suspect the advice is irrelevant.

                                         

                                        I would also highly recommend that anyone with questions on broken visual effects watch the 2015 WWDC session on Whats New in UIKit Dynamics and Visual Effects, where we discuss all the ways your effect can break due to offscreen passes.

                                          • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                            Josh_at_Tulip Level 1 Level 1 (0 points)

                                            Hi Rincewind (hope you're still listening),

                                             

                                            (TL;DR: Read underlined question.)

                                             

                                            I understand the description and subtleties of the offscreen rendering of UIVisualEffectViews, and I've implemented our effects to work in iOS 10 with your instructions here vis-à-vis the maskView property.  However, this implementation is failing on iOS 9.  Specifically, on iOS 9 the views are failing to render at all, as if they had their hidden property set to true.

                                             

                                            This has 'nerd-sniped' a large portion of our iOS team here at Tulip, and we just can't figure it out.  I've also recently watched the WWDC session "What's New in UIKit Dynamics and Visual Effects" and that hasn't helped me with this one.  None of the posts here reference both iOS 9 and 10 and the same time, and so we're beginning to wonder if it's just not possible.  Is it possible to have a single code-path implementation of a visual effect view that will work for both iOS 9 and iOS 10?

                                             

                                            Our requirements include that only two corners be rounded, which means that we cannot simply use the cornerRadius & clipsToBounds method.  Our requirements also include supporting both iOS 9 & 10 concurrently, and we hate divergent code paths as a solution to "well, this way used to work" problems.

                                             

                                            Here is the pseudo code for our issue:

                                            @implementation UIView (RoundedMask)
                                            - (void)roundCorners:(UIRectCorner)corners withRadius:(CGFloat)radius {
                                                CGSize radii = CGSizeMake(radius, radius);
                                            
                                                UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds
                                                                                          byRoundingCorners:corners
                                                                                                cornerRadii:radii];
                                            
                                                CAShapeLayer *layer = [CAShapeLayer layer];    // Fill colour defaults to opaque black
                                                layer.path = path.CGPath;
                                            
                                                UIView *view = [[UIView alloc] initWithFrame:self.bounds];    // Background colour defaults to transparent.
                                                [view.layer addSublayer:layer]
                                            
                                                self.maskView = view;    // Masks self by removing pixels where maskView is transparent.
                                            }
                                            @end
                                            
                                            
                                            
                                            
                                            
                                            

                                            (This method is sometimes used on non-effect views as well.  We are aware of the "but what if your view resizes?" issue.)

                                             

                                            If I break on line 15 and use the debugger QuickLook feature on the view variable, I do see a black opaque rectangle with the appropriate corners rounded and a transparent background.  If I replace line 13 with the following three lines:

                                                view.backgroundColor = [UIColor blackColor];
                                                view.layer.cornerRadius = radius;
                                                view.clipsToBounds = YES;
                                            
                                            
                                            
                                            
                                            
                                            

                                            … then the effect is properly masked and composited on both iOS versions, but of course fails our design requirement of partially-rounded rectangles.  Previously, lines 12-14 were omitted, and line 15 read:

                                                self.layer.mask = layer;
                                            
                                            
                                            
                                            
                                            
                                            

                                            … but of course this does not composite properly on iOS 10.

                                             

                                            This method is invoked from a method that is invoked in a viewDidLoad method, but even if I move that invocation to the viewWillAppear: method as you recommend elsewhere (to get around a β bug) I get the same behaviour on iOS 9.

                                             

                                            Also, you mentioned subclassing UIView to have a custom layerClass, and I thought perhaps there was a remote possibility that maskView does not support sublayers on iOS 9.  So: I tried creating a subview, 'UIShapeView', with CAShapeLayer as its layerClass and with forwarding accessors for the path property.  Changing line 12 to create one of these views and 13 to set its path (bypassing lines 9 & 10) was also futile.

                                             

                                            When I use the View Hierarchy Debugger, and inspect _whatsWrongWithThisEffect of the non-visible view in iOS 9, I get the empty string.  What's more, the content of the visual effect view is visible in that debugger, and all the way up and down the hierarchy (relative to the visual effect view) alpha is set to 1 and hidden is set to Off (clipsToBounds is set to Off all the way up the hierarchy (including the visual effect view itself), but subviews of the visual effect view do have clipsToBounds set to On).

                                             

                                            The specific systems I'm getting these results from are both iPad minis running iOS 9.3.5 and iOS 10.2.1, and the app is built with Xcode 8.2.1 on macOS 10.12.3.

                                             

                                            I am more that happy to investigate more on your prompting, or to send you screenshots via private message, but I think I've been as thorough as I possibly can.  We'd sincerely appreciate any guidance you can give us on this issue, and we thank you for your time on this forum (this thread alone has been indispensable).

                                        • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                          Chad S. Level 1 Level 1 (15 points)

                                          Thanks Rincewind, in my sample, I used a simple shape, but i actually need to be able to mask an arbitrary path, so that's why I included creating a CAShapeLayer with a path.

                                           

                                          I've confirmed that beta 4 resolves my issue in setting the maskView property on the UIVisualEffectView.  I'm able to create the arbitrary mask by:

                                          1. creating a CAShapeLayer
                                          2. creating a UIBezierPath and set the layer's path property with it
                                          3. creating a UIView
                                          4. setting the mask property of the view's layer property to my mask layer
                                          5. then setting the UIVisualEffectView's maskView property to the mask view

                                           

                                           

                                              UIBlurEffect *blur = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
                                              UIVisualEffectView *visualEffect = [[UIVisualEffectView alloc] initWithEffect:blur];
                                              visualEffect.frame = self.view.bounds;
                                          
                                              /// 1.
                                              CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
                                              maskLayer.frame = view.bounds;
                                             
                                              /// 2.
                                              UIBezierPath *bezier = [UIBezierPath bezierPathWithOvalInRect:someRect];
                                              maskLayer.path = bezier.CGPath;
                                          
                                              ///3.
                                              UIView *maskView = [[UIView alloc] initWithFrame:self.view.bounds];
                                              maskView.backgroundColor = [UIColor blackColor];
                                          
                                              /// 4.
                                              maskView.layer.mask = maskLayer;
                                          
                                              /// 5.
                                              visualEffect.maskView = maskView;
                                          
                                          

                                           

                                           

                                           

                                          I've noticed that the Prominent and Regular  effects are broken with and without masking, but will report in another bug when I get time.

                                           

                                          I've marked the masking bug Radar 27393759 as being resolved.

                                           

                                          Thanks for your help!

                                            • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                              Rincewind Apple Staff Apple Staff (280 points)

                                              I would avoid using 2-level masking like that. For one it isn't guaranteed to work (see the comments on CALayer.mask), as nested masking isn't guaranteed by Core Animation. But even if it does work it is less efficient, especially in a case like this.

                                               

                                              Instead you can create a simple UIView subclass that uses a CAShapeLayer as its backing layer by overriding +layerClass.

                                               

                                              Also keep in mind that masking only cares about the alpha coverage. In this case, seeing the fillColor of the CAShapeLayer would be more efficient than using the CAShapeLayer as a mask of a view with a background color. And even then, only the alpha value of that fillColor matters.

                                                • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                  Chad S. Level 1 Level 1 (15 points)

                                                  Thanks Rincewind, I'll refactor my code based on your advice.

                                                   

                                                  To clarify is this what you mean when you advise to "see the comments on CALayer.mask"?:

                                                  So if you apply a mask to a UIVisualEffectView via the CALayer.mask property, you end up putting the whole visual effect view into an offscreen pass, which means it cannot capture the content it needs to render. If you use the UIView.maskView UIVisualEffectView works around this by taking a snapshot of the mask instead, and applying it in the correct places to ensure that the capture can still do the correct thing.

                                                   

                                                  I appreciate the complexities of rending something like the blur effect and appreciate you taking the time to tell us the right way to work with them while masking, but documentation like this buried deep in the forums is only helpful to a few.  Even if this were documented somewhere, it's complicated.  I feel like I need to know an awful lot about the inner workings of UIVisualEffect views in order to mask them.

                                                   

                                                  I'd just like to pose a rhetorical question:  Could this be simpler?

                                                   

                                                  Again, thanks for the help and I appreciate the responsiveness.

                                                    • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                      Rincewind Apple Staff Apple Staff (280 points)

                                                      My primary concern is that If you setup something like this:

                                                       

                                                      CALayer *layer1 = ..., *mask1 = ..., *mask2 = ...;
                                                      mask1.mask = mask2;
                                                      layer1.mask = mask1;
                                                      

                                                       

                                                      Core Animation specifically states that this is an unsupported situation. I haven't tried it, so I can't tell you if it "works" or not right now, but that it works with UIVisualEffectView would be a side effect of its implementation, and is an implementation detail that we would very much like to eliminate if at all possible someday (i.e. make this simpler!).

                                                       

                                                      The differences you are seeing between iOS 9 and iOS 10 are unfortunately along the same lines of the above - something "worked" and now it doesn't (it never really worked reliably, but it worked well enough to not notice the difference). And we could have sent a stronger message on what the correct things to do here are. If nothing else, maybe we'll have more talks on visual effects at WWDC .

                                                    • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                      Shannon Potter Level 1 Level 1 (0 points)

                                                      Can you elaborate on this a bit? I haven't seen anything in the documentation on Core Animation that mentions that nested masking is problematic. Why would it be? (Just curious.) If there's a layer tree with multiple masks at various levels, wouldn't it need to just perform offscreen renders of each subtree that has a mask then composite the final result? Not saying it's efficient, but I don't understand why this would be particularly problematic.

                                                        • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                          Rincewind Apple Staff Apple Staff (280 points)

                                                          In this case it is the comment on CALayer.mask:

                                                           

                                                          /* A layer whose alpha channel is used as a mask to select between the

                                                          * layer's background and the result of compositing the layer's

                                                          * contents with its filtered background. Defaults to nil. When used as

                                                          * a mask the layer's `compositingFilter' and `backgroundFilters'

                                                          * properties are ignored. When setting the mask to a new layer, the

                                                          * new layer must have a nil superlayer, otherwise the behavior is

                                                          * undefined. Nested masks (mask layers with their own masks) are

                                                          * unsupported. */

                                                           

                                                          In the case of UIVisualEffectView today this ends up being irrelevant because UIVisualEffectView snapshots your mask view – thus doing essentially what you propose. However if in the future UIVisualEffectView stops snapshotting and uses your mask live, then you will fall into this undefined behavior from Core Animation, where UIVisualEffectView uses your view as a mask, and then CoreAnimation determines what it decides to do when rendering that mask.

                                                           

                                                          So my comment is a warning to not use unsupported behavior, as you may end up with avoidable breakage.

                                                        • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                          troppoli Level 1 Level 1 (0 points)

                                                          I spent some time with this and came up with nothing working... It seems to me that having a simple CAShapeLayer as the backing layer for a view won't work because the area that isn't part of the shape is rendered as black even if I set the layer and view's background color to clear.... so basically the shape will be ignored.

                                                           

                                                          Here's my example. Make a new single view application project and add a single large jpg file called image.jpg to the project, then replace the contents of ViewController.m the code below. #define qJustShowMask to 1 to see the mask, and you'll see the black areas that should be the photo beneath... then to 0 to see the effect masked to the whole rect, ugh. This used to be a pretty easy thing to do, what am I missing here?!?

                                                           

                                                          #import "ViewController.h"
                                                          #define qJustShowMask 1
                                                          @interface ShapeView : UIView
                                                          @end
                                                          @implementation ShapeView
                                                          + (Class) layerClass
                                                          {
                                                            return [CAShapeLayer class];
                                                          }
                                                          -(void)layoutSubviews
                                                          {
                                                            [super layoutSubviews];
                                                            [self updateMask];
                                                          }
                                                          -(void) updateMask
                                                          {
                                                            self.backgroundColor = [UIColor clearColor];
                                                            self.layer.backgroundColor = [UIColor clearColor].CGColor;
                                                            UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds
                                                              byRoundingCorners: UIRectCornerBottomRight |  UIRectCornerTopLeft
                                                            cornerRadii:CGSizeMake(200, 200)];
                                                            CAShapeLayer *maskLayer = (CAShapeLayer*) self.layer;
                                                            maskLayer.fillColor = [UIColor cyanColor].CGColor;
                                                            maskLayer.frame = self.bounds;
                                                            maskLayer.path = maskPath.CGPath;
                                                            [self setNeedsDisplay];
                                                          }
                                                          @end
                                                          @interface ViewController ()
                                                          @end
                                                          @implementation ViewController
                                                          - (void)viewDidLoad {
                                                            [super viewDidLoad];
                                                            UIImageView* backDrop = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"image.jpg"]];
                                                            backDrop.frame = self.view.bounds;
                                                            [self.view addSubview:backDrop];
                                                            ShapeView* sv = [[ShapeView alloc] initWithFrame:backDrop.bounds];
                                                          #if qJustShowMask
                                                            [self.view addSubview:sv];
                                                          #else
                                                            UIVisualEffect* effect = [UIBlurEffect effectWithStyle: UIBlurEffectStyleLight];
                                                            UIVisualEffectView* vfx = [[UIVisualEffectView alloc] initWithEffect:effect];
                                                            vfx.frame = sv.frame;
                                                            backDrop.maskView = sv;
                                                            [self.view addSubview:vfx];
                                                          #endif
                                                          }
                                                          @end
                                                          
                                                            • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                              Rincewind Apple Staff Apple Staff (280 points)

                                                              Lines 17 & 18 are redundant – UIView.backgroundColor sets CALayer.backgroundColor.

                                                              Line 24 is just wrong – your maskLayer is the layer of 'self', and you are basically having the view (mask) reposition itself in the wrong coordinate system. Just delete this line.

                                                              Line 23 isn't wrong, but note that when used as a mask only the alpha value counts. But using say, cyanColor over blackColor isn't harmful.

                                                              Line 26 is also wrong – you do not want the view to display, as that will call -drawRect:, which you don't want with a shape layer. Its probably not actually calling out (because you don't implement -drawRect:) but it is semantically wrong given you are trying to use the shapelayer to provide your mask.

                                                              Line 43 is semantically backwards – you want the mask view's frame to be the visual effect view's bounds, not the other way around. This probably isn't an issue in your setup (because everything fully consumes the region of its superview) but it is conceptually wrong.

                                                                • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                  troppoli Level 1 Level 1 (0 points)

                                                                  So, though inspite of some ineffective, semanitcally wrong but basically a nopp lines in this example, what is preventing the shape from masking the visual effect when qJustShowMask is 0?

                                                                    • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                      Rincewind Apple Staff Apple Staff (280 points)

                                                                      The -setNeedsDisplayCall and the frame=bounds calls were the most likely to be at issue. Beyond that I'm not certain precisely what may have gone wrong – but my first stop would be to simplify (for example using a rect for the shape layer path, as well as the other typical debugging steps).

                                                                       

                                                                      That said, you can also, at least for this case, avoid masking the visual effect all together by using 2 image views and an inverted mask. You place your image view and visual effect view as you already have, then you apply the inverted mask to another image view with the same image in the visual effect view's content view. You can invert the mask by setting the shape layer's fill mode to even-odd, and adding an additional rect with the bounds of the shape layer.

                                                                        • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                          troppoli Level 1 Level 1 (0 points)

                                                                          So for those interested in the output, I think I see what's going on.... and it's that we're talking about different expectations.

                                                                           

                                                                          Apparently I can't post images here, that seems unfortunate, but I've posted it here https://s16.postimg.org/j9800la5x/masks.png

                                                                           

                                                                          The UIVisualEffectView's frame is a little different than in the above exaple:

                                                                            ShapeView* sv = [[ShapeView alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height/4, self.view.bounds.size.width, self.view.bounds.size.height/2.0)];
                                                                          

                                                                           

                                                                          The first image is the UIVisualEffectView without a mask view.

                                                                           

                                                                          The second is the compiler switch to see the mask view.

                                                                           

                                                                          The third is the UIVisualEffectView masked with the mask view. This result is that the source for the effect is masked, and I don't know what the use case is for that.

                                                                           

                                                                          The fourth is the effect I was expecting to acheive and the effect that I think many others used to be able to aceive by applying a mask to the UIVisualEffectView or one of its superviews. What technique would you use to acheive that?

                                                    • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                      MacTeo Level 1 Level 1 (0 points)

                                                      Same here 27467837

                                                        • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                          Rincewind Apple Staff Apple Staff (280 points)

                                                          Your issue is also using CALayer.mask, using UIView.maskView would probably work better for your needs (modulo bugs that exist in Beta3 at the moment).

                                                           

                                                          For all concerned, the fastest way to test if you don't have the bug in Beta3 that prevents UIVisualEffectView's snapshotting from working would be to set your maskView inside of -[UIView layoutSubviews] or -[UIViewController viewWillLayoutSubiews].

                                                            • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                              Rincewind Apple Staff Apple Staff (280 points)

                                                              Oh, and I just noticed that Beta4 should be out now – if you were having issues with UIView.maskView, give that a try there.

                                                              • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                ddunham Level 1 Level 1 (10 points)

                                                                David, if I can ask a dumb question: how do you use a maskView? I didn't have any success when I tried (nothing at all showed). Does the view need to be inserted into any hierarchy? Is its frame supposed to be the same as the UIVisualEffectView, or is it at 0,0? I find the documentation unclear, and no sample code.

                                                                 

                                                                I'm guessing that in my case I can (in theory) have a clear view, and draw a text balloon into it, then use it as maskView.

                                                                  • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                    Rincewind Apple Staff Apple Staff (280 points)

                                                                    The maskView is in the coordinate system of the visual effect view (i.e. maskView.frame = visualEffectView.bounds is typical).

                                                                     

                                                                    The alpha channel of the composited maskView is used to blend the content of the view that is masked. So for example if you had a horizontal alpha gradient from left to right of alpha=0 to alpha=1, then you would see progressively more view from left to right.

                                                                     

                                                                    Note that the actual color (R/G/B) information doesn't matter, just the alpha.

                                                                     

                                                                    Now, the fact that the mask's frame is typically the same as the view's bounds is only a typical case – you can actually use other values if you like and use non-zero origins. That just means that you are going to be selecting parts of the view rather than the whole view. While not common, this can be useful for various effects.

                                                                     

                                                                    But in the general case UIView.maskView and CALayer.mask should operate fairly similarly. The exception is UIVisualEffectView, where due to its nature we need to snapshot the mask and apply it over multiple subviews. This can be problematic in cases such as when using auto layout as you need to recalculate the mask during layout (due to the hierarchy requirements a mask can't be in the layer tree, so it won't laid out by auto layout or springs & struts).

                                                                      • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                        blackjacx Level 1 Level 1 (0 points)

                                                                        Hello,

                                                                         

                                                                        i have the following view hierarchies:

                                                                         

                                                                        1)

                                                                        UIView (clearColor background)

                                                                                |---UIView (clearColor background)

                                                                                      |--- BlurView (inherits UIVisualEffectsView)

                                                                         

                                                                        2)

                                                                        UIView (clearColor background)

                                                                                |--- BlurView (inherits UIVisualEffectsView)

                                                                         

                                                                        3)

                                                                        UIViewController

                                                                               |---MKMapView

                                                                               |--- .... some other views

                                                                               |---UIView == 1)

                                                                               |---UIView == 2)

                                                                         

                                                                        The code if the blurView is as follows:

                                                                         

                                                                        
                                                                        class BlurView: UIVisualEffectView {
                                                                            var cornersToRound = UIRectCorner.AllCorners
                                                                            private override init(effect: UIVisualEffect?) {
                                                                                super.init(effect: effect)
                                                                            }
                                                                            convenience init(effect: UIVisualEffect?, corners: UIRectCorner) {
                                                                                self.init(effect: effect)
                                                                                cornersToRound = corners
                                                                                translatesAutoresizingMaskIntoConstraints = false
                                                                            }
                                                                           
                                                                            required init?(coder aDecoder: NSCoder) {
                                                                                fatalError("init(coder:) has not been implemented")
                                                                            }
                                                                            override func layoutSubviews() {
                                                                                super.layoutSubviews()
                                                                                let radius = kDefaultCornerRadius
                                                                                let path = UIBezierPath(roundedRect:bounds, byRoundingCorners:cornersToRound, cornerRadii: CGSizeMake(radius, radius))
                                                                        
                                                                                let shapeLayer = CAShapeLayer()
                                                                                let maskView = UIView(frame: bounds)
                                                                                shapeLayer.path = path.CGPath
                                                                                maskView.layer.mask = shapeLayer
                                                                                maskView.backgroundColor = UIColor.blackColor()
                                                                                self.maskView = maskView
                                                                            }
                                                                        }
                                                                        

                                                                         

                                                                        In the first view hierarchy I don't get the blur plus the rounded corners but in the second one it works. The main differences are

                                                                        a) Hierarchy 2) is added as reaction on a user button press when all views are already set up. Hierarchy 1) is added and shown in viewDidLoad of the viewController as shown in 3).

                                                                        b) The blurView in hierarchy 1) must sample through 2 clear colored views. In hierarchy 2) it must sample through only 1 view.

                                                                        c) In hierarchy 1) the rounded corners are only top left and top right. In hierarchy 2) all corners are rounded.

                                                                         

                                                                        The effect appears only in iOS10 using either Xcodes 7 or 8. In iOS 9 it looks as expected (rounded corners + blurred background).

                                                                          • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                            Rincewind Apple Staff Apple Staff (280 points)

                                                                            Given what you've said, there shouldn't be any difference between #1 and #2. The number of views to "sample through" isn't generally relevant, unless one of those views cause an offscreen pass (such as by setting alpha on that view). Similarly the number of corners rounded shouldn't matter, since that is captured in the mask (but again, see my comment above about this pattern for masking – is it not guaranteed to work by Core Animation and thus may be broken in the future).

                                                                              • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                kruttinangopal Level 1 Level 1 (0 points)

                                                                                Hi Rincewind,

                                                                                 

                                                                                Thank you for your explanations.

                                                                                I'm doing all things correctly as you said. But the UIVisualEffectView is not visible.

                                                                                 

                                                                                Here's my code.

                                                                                 

                                                                                //UIView subclass - CAShapeLayer is the backing layer.
                                                                                class ShapeLayerView: UIView
                                                                                {
                                                                                    let shapeLayer = CAShapeLayer()
                                                                                
                                                                                    override var layer: CALayer
                                                                                    {
                                                                                        return shapeLayer
                                                                                    }
                                                                                    init(path: UIBezierPath)
                                                                                    {
                                                                                        super.init(frame: .zero)
                                                                                
                                                                                //Setting path and fillColor for the shapeLayer
                                                                                        shapeLayer.fillColor = UIColor.white.cgColor
                                                                                        shapeLayer.path = path.cgPath
                                                                                    }
                                                                                    required init?(coder aDecoder: NSCoder) {
                                                                                        fatalError("init(coder:) has not been implemented")
                                                                                    }
                                                                                }
                                                                                
                                                                                //UITableViewCell subclass - has blurView
                                                                                class BluredCell: UITableViewCell
                                                                                {
                                                                                    override func layoutSubviews()
                                                                                    {
                                                                                        super.layoutSubviews()
                                                                                   
                                                                                        let cornerRadii = CGSize(width: 10, height: 10)
                                                                                     
                                                                                        let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: [.bottomLeft, .bottomRight], cornerRadii: cornerRadii)
                                                                                        let view = ShapeLayerView(path: path)
                                                                                        view.frame = self.bounds
                                                                                //Masking the blurView
                                                                                        self.blurView.mask = view
                                                                                    }
                                                                                }
                                                                                
                                                                                
                                                                                

                                                                                 

                                                                                 

                                                                                Am I missing something?

                                                                              • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                flainez Level 1 Level 1 (0 points)

                                                                                blackjacx, I have tested with a code very similar to the one you provided and obtained similar results (blur not displaying when maskView is used). But I have also found that forcing a layout on the view after it is added to the hierarchy makes it to display as expected:

                                                                                 

                                                                                        let test = OverlayPanel()
                                                                                        test.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
                                                                                        test.cornersToRound = .allCorners
                                                                                        view.addSubview(test)
                                                                                        test.setNeedsLayout()
                                                                                        test.layoutIfNeeded()
                                                                                

                                                                                 

                                                                                My testing is fairly limited and, as this issue seems inconsistent, I don't know if it will help in your case; but I wanted to let you know in case you find it useful.

                                                                                • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                  emrahgunduz Level 1 Level 1 (0 points)

                                                                                  @blackjack 's code worked for me. I was on the right path, what i was missing was:

                                                                                   

                                                                                  maskView.backgroundColor = [UIColor blackColor];
                                                                                  

                                                                                   

                                                                                  Code also works if you override didMoveToSuperview method, too.

                                                                      • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                        philipamour Level 1 Level 1 (0 points)

                                                                        I would like to confirm that this is indeed broken in iOS 10 GM and none of the proposed solutions in this thread work.

                                                                          • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                            Rincewind Apple Staff Apple Staff (280 points)

                                                                            What in particular is broken for you?

                                                                              • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                alex_kac Level 1 Level 1 (15 points)

                                                                                Same issue here. I get what you guys are saying, but for me its a transition that uses a mask and I can't seem to get it to work - where I know it was said it didn't really work in iOS 9 - but the hundreds of times I used it, it seemed to work.

                                                                                 

                                                                                class FSShapeMaskView : UIView {
                                                                                  override class var layerClass: Swift.AnyClass {
                                                                                  get {
                                                                                       return CAShapeLayer.self
                                                                                  }
                                                                                  }
                                                                                  override open var layer: CALayer {
                                                                                       return shapeLayer
                                                                                  }
                                                                                  var shapeLayer : CAShapeLayer!
                                                                                  init(shape: CAShapeLayer) {
                                                                                       shapeLayer = shape
                                                                                       super.init(frame: CGRect.zero)
                                                                                  }
                                                                                
                                                                                
                                                                                  required init?(coder aDecoder: NSCoder) {
                                                                                       fatalError("init(coder:) has not been implemented")
                                                                                  }
                                                                                }
                                                                                
                                                                                
                                                                                

                                                                                 

                                                                                then ( the forums don't let me post my code for some reason…)

                                                                                 

                                                                                  let maskLayer = CAShapeLayer()
                                                                                  maskLayer.path = circleMaskPathFinal.cgPath
                                                                                  let maskView = FSShapeMaskView(shape: maskLayer)
                                                                                  toViewController.view.mask = maskView
                                                                                
                                                                                  let maskLayerAnimation = CABasicAnimation(keyPath: "path")
                                                                                  maskLayerAnimation.fromValue = circleMaskPathInitial.cgPath
                                                                                  maskLayerAnimation.toValue = circleMaskPathFinal.cgPath
                                                                                  maskLayerAnimation.duration = self.transitionDuration(using: transitionContext)
                                                                                  maskLayerAnimation.delegate = self
                                                                                  maskLayer.add(maskLayerAnimation, forKey: "path")
                                                                                  toViewController.view.frame = fromViewController.view.frame
                                                                                
                                                                                

                                                                                 

                                                                                After 5 days of Swift3 conversion, perhaps my brain is mush - but do you have any suggestions to move me forward?

                                                                                  • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                    alex_kac Level 1 Level 1 (15 points)

                                                                                    I should add it looks like this:

                                                                                     

                                                                                    UINavigationController

                                                                                         |

                                                                                         |--- UIView

                                                                                                   |

                                                                                                   |

                                                                                              UIViewController

                                                                                                        |    

                                                                                                        |---- UIView - this is who is getting the mask

                                                                                                                      |

                                                                                                                      |--- UIVisualEffectsView

                                                                                    • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                      Rincewind Apple Staff Apple Staff (280 points)

                                                                                      Transitions are tough in this realm, as maskView is the only way to ensure that the effect works, but maskView also isn't animatable.

                                                                                       

                                                                                      What you might try instead – if possible – is to invert the masking. Instead of masking out the visual effect, copy in the content you want to cover the effect. That is, you do something like this:

                                                                                       

                                                                                      ImageView

                                                                                           VisualEffectView

                                                                                                ImageView <with inverted mask>

                                                                                       

                                                                                      Also do not override UIView.layer. I cannot overemphasize how often you will encounter strange bugs when you do so. Most of UIView accesses its ivar directly, meaning that your override will not apply when interacting with UIView, but also many other parts of UIKit will also interact directly with the UIView layer ivar directly for performance reasons.

                                                                                       

                                                                                      It is never a good idea to override UIView.layer. It is also unnecessary to do so – when you override layerClass, we construct the layer with your given class, so UIView's own layer is of the layer class that you want. If you want to get back to the CoreAnimation behavior of all actions animate, then override UIView.action(for:forKey:) and return nil.

                                                                                        • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                          db_q Level 1 Level 1 (0 points)

                                                                                          Honestly guys, you really boneheaded this thing up.

                                                                                           

                                                                                          So, your buggy broken version that lasted through a major release end to end works, for me, for OP, for other people, really nicely. You decide you need to "fix" it and by fixing it it means you completely broke it for all of us. Now, in order to understand what we need to do to fix what you broke when you broke what you fixed on something that was working fine for 99.9% of people, we have to dig through 37 comments and multiple websites and nary a straight forward comment.

                                                                                           

                                                                                          The last time I touched this chunk of my code was 2 years ago. You are living and breathing this every day. When you read things like in SQLite they point out the importance of not going around smashing things because religion tells you to smash them because you have millions of users that are depending on the functionality is it is. You need to find a way to fork away from the stuff that for whatever reason you decided is not right.

                                                                                           

                                                                                          Because for us, it was right. We had results that we worked at and we put away. Now you make us drag all this old stuff out again to try to slap some patches and redesign it. A just touch more courtesy to compatibility, even when you decide you need to fix things, is good. Simple instructions to warn us that this is coming is good. Simple instructions on how to deal with it is good.

                                                                                           

                                                                                          Following the rules, finding something doesn't work suddenly in iOS10 and having to trawl the internet to find out why illustrates a broken contract between provider and users. If you are doing this here, and other teams are doing this elsewhere, with the massive complexity you have going on now PLUS two languages going side by side so half of us have to look at examples in a language that we're not using and try to port a solution to something that worked fine in the first place makes me want to delete my app.

                                                                                           

                                                                                          I have a feeling that when I'm done, after 6 years of full time development you're just going to cut the cord on ObjC and tie my hands behind my back and shove me into the ocean with lead shoes on. This is the Apple approach to fixing problems. Always has been ever since NeXT nuked the Phone Kit because it had some bugs.

                                                                                           

                                                                                          What you needed to do here was leave the existing path and flag it as being depricated and then develop a new path that didn't have the limitations the old one has. And then encourage people to move over. You deprecate constantly and change constantly and we need to book a serious portion of the year to trying to adapt to your ever shifting ideas. An app like mine that has spanned several different 270 degree turns from Apple ends up embracing all of the current trends in development that lasted a year or two and got superceded. I don't have the resources to rewrite 100k lines of code every time one of you guys has a bright idea. You need to focus a little more on stability of the development platform, it's just as important a concept os adding features.

                                                                                           

                                                                                          I don't use Swift much but I have some and when your conversion tool to 3.0 broke all the swift and left in a bunch of manual things to change, it was amateur hour with a score of 10 out of 10. What ever happened to "it just works?" You give us these tools, you break them, your fixes break them and force us to redevelop. What is going on over there?

                                                                                          • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                            akaralar Level 1 Level 1 (0 points)

                                                                                            Hey @Rincewind,

                                                                                             

                                                                                            Thanks for your continuous commitment to answering questions in this thread, I feel I can now get my head around how to do masking on UIVisualEffectView.

                                                                                             

                                                                                            However, my problem is a bit more peculiar. I'm trying to build the folllowing UI design: https://dl.dropboxusercontent.com/spa/6d4j003yz96ozkz/smg3lrmp.png

                                                                                             

                                                                                            The problem here is that the blurred view in the background is actually a video. So while the video plays, it will be blurred, and a small part of it will be visible while playing.

                                                                                             

                                                                                            I tried to do this masking but couldn't achieve the desired result. Is it possible to do with just masking the UIVisualEffectView?

                                                                                             

                                                                                            Or should I make the hierarchy like this:

                                                                                             

                                                                                            AVPlayerLayer

                                                                                                 UIVisualEffectView

                                                                                                      AVPlayerLayer (with inverse masking?)

                                                                                             

                                                                                            Any hints would be much appreciated, thanks!

                                                                                    • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                      savitha2410 Level 1 Level 1 (0 points)
                                                                                      
                                                                                      import UIKit
                                                                                      class BlurView: UIView{
                                                                                          /
                                                                                          /
                                                                                          override init(frame: CGRect) {
                                                                                              super.init(frame: frame)
                                                                                              self.backgroundColor = UIColor.clear
                                                                                      
                                                                                          }
                                                                                      
                                                                                          required init?(coder aDecoder: NSCoder) {
                                                                                              super.init(coder: aDecoder)
                                                                                              self.backgroundColor = UIColor.clear
                                                                                          }
                                                                                          override func draw(_ rect: CGRect) {
                                                                                          
                                                                                              super.draw(rect)
                                                                                         self.transparent()
                                                                                          }
                                                                                      
                                                                                      
                                                                                          func transparent()
                                                                                          {  let path = UIBezierPath(rect: bounds)
                                                                                              let tpath = UIBezierPath(rect: CGRect(x:60,y:120,width:200,height:200))
                                                                                              path.append(tpath)
                                                                                              path.usesEvenOddFillRule = true
                                                                                          
                                                                                          
                                                                                              let layer = CAShapeLayer()
                                                                                              layer.path = path.cgPath
                                                                                              layer.fillRule = kCAFillRuleEvenOdd
                                                                                              layer.fillColor = UIColor.black.cgColor
                                                                                              self.layer.mask = layer
                                                                                          
                                                                                          }
                                                                                      
                                                                                      }
                                                                                      

                                                                                      i want a view with a transparent sqaure hole through which the image at the back can be seen  and i want this view to be blurred. I created a custom class and this is how i use it.

                                                                                      
                                                                                      let blurview = BlurView(frame: self.view.frame)
                                                                                      
                                                                                      let  blur = UIVisualEffectView (effect: UIBlurEffect (style: UIBlurEffectStyle.dark))
                                                                                              blur.frame = blurview.bounds
                                                                                      
                                                                                      blurview.addSubview(blur)
                                                                                              let imview = UIImageView(frame: self.view.frame)
                                                                                              imview.image = UIImage(named: "embed2")
                                                                                          view.addSubview(imview)
                                                                                             
                                                                                            
                                                                                             
                                                                                             
                                                                                              view.addSubview(blurview)
                                                                                      

                                                                                       

                                                                                      But i dont get any blur effect. i am able to see the image at the back through the square, but the rest of the screen is just black in colour. how do i achieve the blur effect . i even tried masking the visualeffectview.mask property , that works only if i present the view controller, if i embed the view controller in a navigationcontroller , i am able to see only the image. why is that? 

                                                                                        • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                          Rincewind Apple Staff Apple Staff (280 points)

                                                                                          My first instinct would be because you are implementing UIView.draw(rect:). You basically never want to implement that method unless you are using Core Graphics to draw, which you are not here. The work you are doing would be better done in UIView.layoutSubviews().

                                                                                           

                                                                                          But assuming you are doing this on iOS 10.x (or lower) this is expected because you are masking the blur's superview. Masking requires an offscreen render pass, and before iOS 11 that breaks blurs.

                                                                                           

                                                                                          Now you say this works if you do a modal presentation, but not if you add the view to a navigation controller – how are you doing the latter? I wouldn't expect a significant difference in these two cases unless there is something that I don't understand about your configuration.

                                                                                        • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                          UITextFielder Level 1 Level 1 (0 points)

                                                                                          Did anyone figure out the proper solution to only rounding the top corners of a UIVisualEffectView in iOS 10? None of these solutions are working for me.

                                                                                            • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                              Rincewind Apple Staff Apple Staff (280 points)

                                                                                              The only solution that can work is to use UIView.maskView on the UIVisualEffectView. But without knowing what you tried its hard to understand where you might have gone wrong.

                                                                                                • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                                  UITextFielder Level 1 Level 1 (0 points)
                                                                                                  override func viewDidLayoutSubviews() {
                                                                                                      super.viewDidLayoutSubviews()
                                                                                                  
                                                                                                      view.backgroundColor = .clear
                                                                                                  
                                                                                                      let shapeLayer = CAShapeLayer()
                                                                                                      let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: [.topRight, .topLeft], cornerRadii: CGSize(width: 10, height: 10))
                                                                                                      shapeLayer.path = path.cgPath
                                                                                                  
                                                                                                      let mask = UIView(frame: view.bounds)
                                                                                                      mask.backgroundColor = .clear
                                                                                                      let areaToReveal = UIView(frame: view.bounds)
                                                                                                      areaToReveal.layer.mask = shapeLayer
                                                                                                      areaToReveal.backgroundColor = .white
                                                                                                      mask.addSubview(areaToReveal)
                                                                                                      view.mask = mask
                                                                                                  }
                                                                                                  
                                                                                                  func setupViews() {
                                                                                                      view.addSubview(blurredView)
                                                                                                  }
                                                                                                  
                                                                                                  func addStaticConstraints() {
                                                                                                      blurredView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
                                                                                                      blurredView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
                                                                                                      blurredView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
                                                                                                      blurredView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
                                                                                                  }
                                                                                                  
                                                                                                  
                                                                                                  
                                                                                                  

                                                                                                   

                                                                                                  Above is what I've tried. Blur shows in iOS 11, but not in iOS 10. I've also tried adding the mask to the `blurredView` directly on line 16 to no avail.

                                                                                                    • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                                      Rincewind Apple Staff Apple Staff (280 points)

                                                                                                      On iOS 10 blurs can't sample content "through" a mask – because you make the visual effect view a subview of a view with a mask you prevent it from sampling the content it needs to do the masking.

                                                                                                       

                                                                                                      You need to apply the mask *directly* to the UIVisualEffectView, not to a superview. This works on iOS 11 due to improvements specifially made on that OS version.

                                                                                                        • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                                          UITextFielder Level 1 Level 1 (0 points)

                                                                                                          See my edit above. Is that the approach you're describing?

                                                                                                           

                                                                                                          "I've also tried adding the mask to the `blurredView` directly on line 16 to no avail."

                                                                                                            • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                                              Rincewind Apple Staff Apple Staff (280 points)

                                                                                                              I would recommend process of elimination then – try a simpler mask first and see if that resolves the issue (such as just setting corner radius on a plain view instead of going through the shape layer business).

                                                                                                               

                                                                                                              However, fundamentally the best way to create a mask view that uses a shape layer is to create your own UIView subclass that overrides .layerClass() to return CAShapeLayer.self. That removes the extra 2 views/layers in your example and simplifies greatly by not using a mask of a subview of a view mean to be a mask.

                                                                                                                • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                                                  UITextFielder Level 1 Level 1 (0 points)
                                                                                                                  let mask = UIView(frame: blurredView.bounds)
                                                                                                                  mask.backgroundColor = .clear
                                                                                                                  let areaToReveal = UIView(frame: blurredView.bounds)
                                                                                                                  areaToReveal.layer.cornerRadius = 10.0
                                                                                                                  areaToReveal.backgroundColor = .black
                                                                                                                  mask.addSubview(areaToReveal)
                                                                                                                  blurredView.mask = mask
                                                                                                                  

                                                                                                                   

                                                                                                                  Not sure if this is what you meant; but it still doesnt work.

                                                                                                                  • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                                                    UITextFielder Level 1 Level 1 (0 points)
                                                                                                                    class PassthroughView: UIView {
                                                                                                                       
                                                                                                                        override class var layerClass: AnyClass {
                                                                                                                            get {
                                                                                                                                return CAShapeLayer.self
                                                                                                                            }
                                                                                                                        }
                                                                                                                        private var corners = UIRectCorner.allCorners
                                                                                                                        private var radius: CGFloat = 10.0
                                                                                                                       
                                                                                                                        required init(frame: CGRect, corners: UIRectCorner = .allCorners, radius: CGFloat = 10.0) {
                                                                                                                            super.init(frame: frame)
                                                                                                                            self.corners = corners
                                                                                                                            self.radius = radius
                                                                                                                            backgroundColor = .clear
                                                                                                                        }
                                                                                                                        required init?(coder aDecoder: NSCoder) {
                                                                                                                            fatalError("init(coder:) has not been implemented")
                                                                                                                        }
                                                                                                                       
                                                                                                                        override func layoutSubviews() {
                                                                                                                            super.layoutSubviews()
                                                                                                                           
                                                                                                                            guard let layer = layer as? CAShapeLayer else {
                                                                                                                                return
                                                                                                                            }
                                                                                                                           
                                                                                                                            let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
                                                                                                                            layer.path = path.cgPath
                                                                                                                        }
                                                                                                                    }
                                                                                                                    
                                                                                                                    
                                                                                                                    MyViewController: UIViewController {
                                                                                                                      override func viewDidLayoutSubviews() {
                                                                                                                          super.viewDidLayoutSubviews()
                                                                                                                          blurredView.mask = PassthroughView(frame: blurredView.bounds,
                                                                                                                                                           corners: [.topLeft, .topRight],
                                                                                                                                                            radius: 10.0)
                                                                                                                      }
                                                                                                                      
                                                                                                                      func setupViews() {
                                                                                                                          view.addSubview(blurredView)
                                                                                                                      }
                                                                                                                    }
                                                                                                                    

                                                                                                                     

                                                                                                                     

                                                                                                                    This works on iOS 11, but not on iOS 10. Seems to be the common denominator with regards to rounding/masking top corners of a UIVisualEffectView. Not sure what else to try.

                                                                                                                    • Re: UIVisualEffectView with mask doesn’t blur in iOS 10
                                                                                                                      UITextFielder Level 1 Level 1 (0 points)

                                                                                                                      @Rincewind, here's my summary of findings:

                                                                                                                       

                                                                                                                      There seems to be an issue in iOS 10 that causes the UIView.mask property to not work when set on UIVisualEffectView when done so in viewWillLayoutSubviews(). When done in viewDidLoad(), works fine (But wont work if views are setup with Auto Layout). That same code works on UIView, with and without auto layout in iOS 10 in viewWillLayoutSubviews(). The same code also works in iOS 11 for both UIView and UIVisualEffectView in viewWillLayoutSubviews().

                                                                                                                       

                                                                                                                      I'm not sure where to go from here to try to get top rounded corners for a UIVisualEffectView on iOS 10. Any help is much appreciated.

                                                                                                                       

                                                                                                                      class PassthroughView: UIView {
                                                                                                                      
                                                                                                                          override class var layerClass: AnyClass {
                                                                                                                              get {
                                                                                                                                  return CAShapeLayer.self
                                                                                                                              }
                                                                                                                          }
                                                                                                                          private var corners = UIRectCorner.allCorners
                                                                                                                          private var radius: CGFloat = 10.0
                                                                                                                      
                                                                                                                          required init(frame: CGRect, corners: UIRectCorner = .allCorners, radius: CGFloat = 10.0) {
                                                                                                                              super.init(frame: frame)
                                                                                                                              self.corners = corners
                                                                                                                              self.radius = radius
                                                                                                                          }
                                                                                                                      
                                                                                                                          required init?(coder aDecoder: NSCoder) {
                                                                                                                              fatalError("init(coder:) has not been implemented")
                                                                                                                          }
                                                                                                                      
                                                                                                                          override func layoutSubviews() {
                                                                                                                              super.layoutSubviews()
                                                                                                                      
                                                                                                                              guard let layer = layer as? CAShapeLayer else {
                                                                                                                                  return
                                                                                                                              }
                                                                                                                      
                                                                                                                              layer.fillColor = UIColor.black.cgColor
                                                                                                                      
                                                                                                                              let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
                                                                                                                              layer.path = path.cgPath
                                                                                                                          }
                                                                                                                      }
                                                                                                                      
                                                                                                                      
                                                                                                                      
                                                                                                                      

                                                                                                                       

                                                                                                                      class ViewController: UIViewController {
                                                                                                                          @IBOutlet weak var myView: UIView!
                                                                                                                          private let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
                                                                                                                      
                                                                                                                          override func viewDidLoad() {
                                                                                                                              super.viewDidLoad()
                                                                                                                         
                                                                                                                              view.addSubview(blurView)
                                                                                                                              blurView.frame = CGRect(x: 16, y: 328, width: 337, height: 218)
                                                                                                                         
                                                                                                                              / / This works for UIVisualEffectView in iOS 10, but fails if views are setup via Auto Layout
                                                                                                                              let passView = PassthroughView(frame: blurView.bounds, corners: [.topRight, .topLeft], radius: 10.0)
                                                                                                                              blurView.mask = passView
                                                                                                                          }
                                                                                                                      
                                                                                                                          override func viewWillLayoutSubviews() {
                                                                                                                              super.viewWillLayoutSubviews()
                                                                                                                              / / This causes UIVisualEffectView to not be shown in iOS 10.
                                                                                                                              let passView = PassthroughView(frame: blurView.bounds, corners: [.topRight, .topLeft], radius: 10.0)
                                                                                                                              blurView.mask = passView
                                                                                                                         
                                                                                                                              / / This works
                                                                                                                              let passView2 = PassthroughView(frame: myView.bounds, corners: [.topRight, .topLeft], radius: 10.0)
                                                                                                                              myView.mask = passView2
                                                                                                                          }
                                                                                                                      }