Why masksToBounds = YES prevents CALayer shadow?

后端 未结 6 1229
借酒劲吻你
借酒劲吻你 2020-11-28 01:44

With the following snippet, I\'m adding a drop shadow effect to one my UIView. Which works pretty well. But as soon as I set the view\'s masksToBounds prope

相关标签:
6条回答
  • 2020-11-28 02:18

    Because shadow is an effect done outside the View, and that masksToBounds set to YES will tell the UIView not to draw anything that is outside itself.

    If you want a roundedCorner view with shadow I suggest you do it with 2 views:

    UIView *view1 = [[UIView alloc] init];
    UIView *view2 = [[UIView alloc] init];
    
    view1.layer.cornerRadius = 5.0;
    view1.layer.masksToBounds = YES;
    view2.layer.cornerRadius = 5.0;
    view2.layer.shadowColor = [[UIColor blackColor] CGColor];
    view2.layer.shadowOpacity = 1.0;
    view2.layer.shadowRadius = 10.0;
    view2.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
    [view2 addSubview:view1];
    [view1 release];
    
    0 讨论(0)
  • 2020-11-28 02:26

    I also had drastic performance issues with shadows and rounded corners. Instead of using the shadowPath part, I used the following lines which perfectly solved the performance hit:

    self.layer.shouldRasterize = YES;
    self.layer.rasterizationScale = UIScreen.mainScreen.scale;
    
    0 讨论(0)
  • 2020-11-28 02:28

    Swift 3.0 version with StoryBoard

    The same idea with @TheSquad. Create a new view under the actual view and add shadow to the lower view.

    1. Create a view under the actual view

    Drag a UIView to StoryBoard with same constraint as your target view. Check clip to bound for the target view. Also make sure the new view is listed before the target view so that the target view will cover the new view.

    2. Now link the new view to your code add add shadow on it

    This is just a sample. You can do whatever way you want here

    shadowView.layer.masksToBounds = false
    shadowView.layer.shadowColor = UIColor.red.cgColor
    shadowView.layer.shadowOpacity = 0.5
    shadowView.layer.shadowOffset = CGSize(width: -1, height: 1)
    shadowView.layer.shadowRadius = 3
    
    shadowView.layer.shadowPath = UIBezierPath(rect: coverImage.bounds).cgPath
    shadowView.layer.shouldRasterize = true
    
    0 讨论(0)
  • 2020-11-28 02:33

    It's iOS 6 now, things might have changed. TheSquad's answer don't work for me until I managed to add one more line view2.layer.masksToBounds = NO;, otherwise shadow doesn't show. Although documentation says masksToBounds is NO by default, my code shows the opposite.

    Here is how I make a rounded corner button with shadow, which is among the most commonly used code snippet in my app.

    button.layer.masksToBounds = YES;
    button.layer.cornerRadius = 10.0f;
    
    view.layer.masksToBounds = NO;      // critical to add this line
    view.layer.cornerRadius = 10.0f;
    view.layer.shadowOpacity = 1.0f;
    // set shadow path to prevent horrible performance
    view.layer.shadowPath = 
        [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;      
    
    [view addSubview:button];
    

    EDIT

    If views need to be animated or scrolled, masksToBounds = YES tax performance significantly, which means animation will probably get stuttered. To get rounded corner and shadow AND smooth animation or scrolling, use following code instead:

    button.backgroundColor = [UIColor clearColor];
    button.layer.backgroundColor = [UIColor redColor].CGColor;
    button.layer.masksToBounds = NO;
    button.layer.cornerRadius = 10.0f;
    
    view.layer.shadowOpacity = 0.5f;
    view.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;
    view.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
    view.layer.shadowRadius = 2.0f;
    view.layer.masksToBounds = NO;
    view.layer.cornerRadius = 10.0f;  
    
    [view addSubview:button];
    
    0 讨论(0)
  • 2020-11-28 02:33

    Here is one of the solutions:

         @IBOutlet private weak var blockView: UIView! {
             didSet {
              blockView.backgroundColor = UIColor.white
              blockView.layer.shadowColor = UIColor.black.cgColor
              blockView.layer.shadowOpacity = 0.5
              blockView.layer.shadowOffset = CGSize.zero
    
              blockView.layer.cornerRadius = 10
            }
          }
          @IBOutlet private weak var imageView: UIImageView! {
            didSet {
              imageView.layer.cornerRadius = 10
              imageView.layer.masksToBounds = true
    
              imageView.layer.shouldRasterize = true
            }
          }
    

    0 讨论(0)
  • 2020-11-28 02:35

    This is the Swift 3 and IBDesignable version of the answer posted by @TheSquad.

    I used the same concept while making changes in the storyboard file. First I moved my targetView (the one which requires corner radius and shadow) inside a new containerView. Then I added the following lines of code (Reference: https://stackoverflow.com/a/35372901/419192) to add some IBDesignable attributes for UIView Class:

    @IBDesignable extension UIView {
    /* The color of the shadow. Defaults to opaque black. Colors created
     * from patterns are currently NOT supported. Animatable. */
    @IBInspectable var shadowColor: UIColor? {
        set {
            layer.shadowColor = newValue!.cgColor
        }
        get {
            if let color = layer.shadowColor {
                return UIColor(cgColor: color)
            }
            else {
                return nil
            }
        }
    }
    
    /* The opacity of the shadow. Defaults to 0. Specifying a value outside the
     * [0,1] range will give undefined results. Animatable. */
    @IBInspectable var shadowOpacity: Float {
        set {
            layer.shadowOpacity = newValue
        }
        get {
            return layer.shadowOpacity
        }
    }
    
    /* The shadow offset. Defaults to (0, -3). Animatable. */
    @IBInspectable var shadowOffset: CGPoint {
        set {
            layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
        }
        get {
            return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
        }
    }
    
    /* The blur radius used to create the shadow. Defaults to 3. Animatable. */
    @IBInspectable var shadowRadius: CGFloat {
        set {
            layer.shadowRadius = newValue
        }
        get {
            return layer.shadowRadius
        }
    }
    
    /* The corner radius of the view. */
    @IBInspectable var cornerRadius: CGFloat {
        set {
            layer.cornerRadius = newValue
        }
        get {
            return layer.cornerRadius
        }
    }
    

    After adding this code, I went back to the storyboard and on selecting my containerView I could now find a new set of attributes in the attributes inspector:

    Other than adding values for these attributes as per my choice, I also added a corner radius to my targetView and set the masksToBounds property as true.

    I hope this helps :)

    0 讨论(0)
提交回复
热议问题