Draw shadow only from 3 sides of UIView

后端 未结 4 1147
夕颜
夕颜 2020-12-07 19:13

I\'ve successfully implemented drawing a shadow around my UIView like this:

block1.layer.masksToBounds = NO;
block1.layer.shadowOffset = CGSizeM         


        
相关标签:
4条回答
  • 2020-12-07 19:38

    Updating Ryan Poolos Answer to Swift 3.0

    Thanks to Ryan Poolos

    class sampleViewController: UIViewController {
        var block1: UIView! = nil
    
        override func viewDidLoad() {
    
            super.viewDidLoad()
            block1 = UIView(frame: CGRect(x: 32.0, y: 32.0, width: 128.0, height: 128.0))
            block1.backgroundColor = UIColor.orange
            self.view.addSubview(block1)
    
            block1.layer.masksToBounds = false
            block1.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
            block1.layer.shadowRadius = 1.0
            block1.layer.shadowOpacity = 0.7
    
            let path = UIBezierPath()
    
            // Start at the Top Left Corner
            path.move(to: CGPoint(x: 0.0, y: 0.0))
    
            // Move to the Top Right Corner
            path.addLine(to: CGPoint(x: block1.frame.size.width, y: 0.0))
    
            // Move to the Bottom Right Corner
            path.addLine(to: CGPoint(x: block1.frame.size.width, y: block1.frame.size.height))
    
            // This is the extra point in the middle :) Its the secret sauce.
            path.addLine(to: CGPoint(x: block1.frame.size.width/2.0, y: block1.frame.size.height/2.0))
    
            // Move to the Bottom Left Corner
            path.addLine(to: CGPoint(x: 0.0, y: block1.frame.size.height))
    
            path.close()
    
            block1.layer.shadowPath = path.cgPath
        }
    }
    

    Result:

    0 讨论(0)
  • 2020-12-07 19:51

    A bit of improvement for other answers, thanks to Ashok R for swift code.

    Since we were creating a triangular view in the background of the view with shadow on all sides, and a white triangle on the sides shadow is not needed.

    It breaks in case of views with width comparatively larger than height.

    A workaround will be to shift the path for the line where shadow is not needed a bit towards that side of view, instead of creating the triangular view Path completely.

    I have created an extension for that -

    extension UIView {
        func addshadow(top: Bool,
                       left: Bool,
                       bottom: Bool,
                       right: Bool,
                       shadowRadius: CGFloat = 2.0) {
    
            self.layer.masksToBounds = false
            self.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
            self.layer.shadowRadius = shadowRadius
            self.layer.shadowOpacity = 1.0
    
            let path = UIBezierPath()
            var x: CGFloat = 0
            var y: CGFloat = 0
            var viewWidth = self.frame.width
            var viewHeight = self.frame.height
    
            // here x, y, viewWidth, and viewHeight can be changed in
            // order to play around with the shadow paths.
            if (!top) {
                y+=(shadowRadius+1)
            }
            if (!bottom) {
                viewHeight-=(shadowRadius+1)
            }
            if (!left) {
                x+=(shadowRadius+1)
            }
            if (!right) {
                viewWidth-=(shadowRadius+1)
            }
            // selecting top most point
            path.move(to: CGPoint(x: x, y: y))
            // Move to the Bottom Left Corner, this will cover left edges
            /*
             |☐
             */
            path.addLine(to: CGPoint(x: x, y: viewHeight))
            // Move to the Bottom Right Corner, this will cover bottom edge
            /*
             ☐
             -
             */
            path.addLine(to: CGPoint(x: viewWidth, y: viewHeight))
            // Move to the Top Right Corner, this will cover right edge
            /*
             ☐|
             */
            path.addLine(to: CGPoint(x: viewWidth, y: y))
            // Move back to the initial point, this will cover the top edge
            /*
             _
             ☐
             */        
            path.close()
            self.layer.shadowPath = path.cgPath
        }
    

    and set the boolean true for whichever side you want the shadow to appear

    myView.addshadow(top: false, left: true, bottom: true, right: true, shadowRadius: 2.0)

    // shadow radius is optional above and is set as default at 2.0

    or myView.addshadow(top: true, left: true, bottom: true, right: true, shadowRadius: 2.0)

    or myView.addshadow(top: false, left: false, bottom: true, right: true, shadowRadius: 2.0)

    0 讨论(0)
  • 2020-12-07 19:56

    Try this

    extension CALayer {
    func applySketchShadow(color: UIColor, alpha: CGFloat, x: CGFloat, y: CGFloat, blur: CGFloat, spread: CGFloat)
    {
        shadowColor = color.cgColor
        shadowOpacity = alpha
        shadowOffset = CGSize(width: x, height: y)
        shadowRadius = blur / 2.0
        if spread == 0 {
            shadowPath = nil
        } else {
            let dx = -spread
            let rect = bounds.insetBy(dx: dx, dy: dx)
            shadowPath = UIBezierPath(rect: rect).cgPath
        }
    }
    
    0 讨论(0)
  • 2020-12-07 20:02

    I know you say setting layer.shadowOffset won't work for you, but you are allowed to put in negative values so setting it layer.shadowOffset = CGSizeMake(0.0, -2.0) would come close to the effect you're looking for but of course I expect you want it to be even on the three sides.

    So here we go with layer.shadowPath!

    UIView *block1 = [[UIView alloc] initWithFrame:CGRectMake(32.0, 32.0, 128.0, 128.0)];
    [block1 setBackgroundColor:[UIColor orangeColor]];
    [self.view addSubview:block1];
    
    block1.layer.masksToBounds = NO;
    block1.layer.shadowOffset = CGSizeMake(0, 0);
    block1.layer.shadowRadius = 1;
    block1.layer.shadowOpacity = 0.7;
    
    UIBezierPath *path = [UIBezierPath bezierPath];
    
    // Start at the Top Left Corner
    [path moveToPoint:CGPointMake(0.0, 0.0)];
    
    // Move to the Top Right Corner
    [path addLineToPoint:CGPointMake(CGRectGetWidth(block1.frame), 0.0)];
    
    // Move to the Bottom Right Corner
    [path addLineToPoint:CGPointMake(CGRectGetWidth(block1.frame), CGRectGetHeight(block1.frame))];
    
    // This is the extra point in the middle :) Its the secret sauce.
    [path addLineToPoint:CGPointMake(CGRectGetWidth(block1.frame) / 2.0, CGRectGetHeight(block1.frame) / 2.0)];
    
    // Move to the Bottom Left Corner
    [path addLineToPoint:CGPointMake(0.0, CGRectGetHeight(block1.frame))];
    
    // Move to the Close the Path
    [path closePath];
    
    block1.layer.shadowPath = path.CGPath;
    

    And to give you an idea of whats going on, here is the actual shadow path you just drew :)

    enter image description here

    Its possible to just shift that extra middle point before or after the other lines to choose which side will be omitted.

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