UIView with shadow, rounded corners and custom dra

2019-01-12 18:13发布

I have to create a custom UIView that will have round corners, a border, a shadow and its drawRect() method is overridden to provide custom drawing code with which several straight lines are drawn into the view (I need to use a fast, lightweight approach here since many of these views may be rendered).

The problem I'm currently facing is that the shadow doesn't apply anymore to the round corners as soon as I override drawRect() in the view class (even without any custom code yet in it). See the attached image for the difference:

enter image description here

In the view controller I'm using the following code:

    view.layer.cornerRadius = 10;
    view.layer.masksToBounds = true;

    view.layer.borderColor = UIColor.grayColor().CGColor;
    view.layer.borderWidth = 0.5;

    view.layer.contentsScale = UIScreen.mainScreen().scale;
    view.layer.shadowColor = UIColor.blackColor().CGColor;
    view.layer.shadowOffset = CGSizeZero;
    view.layer.shadowRadius = 5.0;
    view.layer.shadowOpacity = 0.5;
    view.layer.masksToBounds = false;
    view.clipsToBounds = false;

In the overridden drawContext() I would use something like:

    var context:CGContext = UIGraphicsGetCurrentContext();
    CGContextSetStrokeColorWithColor(context, UIColor.redColor().CGColor);
    // Draw them with a 2.0 stroke width so they are a bit more visible.
    CGContextSetLineWidth(context, 2.0);
    CGContextMoveToPoint(context, 0.0, 0.0); //start at this point
    CGContextAddLineToPoint(context, 20.0, 20.0); //draw to this point
    CGContextStrokePath(context);

But as said above, the shadow problem occurs even without this code added.

Is there any other/better way to draw lightweight elements onto a view other than this approach that is compatible with round corners and shadows? I don't want to add any unnecessary extra views or image contexts to the view since these need to be light and performant.

16条回答
别忘想泡老子
2楼-- · 2019-01-12 18:31

I find the following link helpful to understand setting the dropshadow:

How to add a shadow to a UIView

To set the round corner for UIVIEW just set the layer.cornerRadius value in interface builder, Please check screenshot. enter image description here

查看更多
男人必须洒脱
3楼-- · 2019-01-12 18:34

you can use this function for your all views.

    extension UIView{

    func radiusAndBorder(radius:CGFloat, color:UIColor = UIColor.clear) -> UIView{
        var rounfView:UIView = self
        rounfView.layer.cornerRadius = CGFloat(radius)
        rounfView.layer.borderWidth = 1
        rounfView.layer.borderColor = color.cgColor
        rounfView.clipsToBounds = true
        return rounfView
    }
}
查看更多
兄弟一词,经得起流年.
4楼-- · 2019-01-12 18:35

In Swift. What did work for me was adding:

    self.noteImage.layer.masksToBounds = false

So, the full code is:

    self.noteImage.layer.masksToBounds = false
    self.noteImage.layer.shadowColor = UIColor.redColor().CGColor
    self.noteImage.layer.shadowOpacity = 0.5
    self.noteImage.layer.shadowOffset = CGSize(width: 2, height: 2)
    self.noteImage.layer.shadowRadius = 1

    self.noteImage.layer.shadowPath = UIBezierPath(rect: noteImage.bounds).CGPath
    self.noteImage.layer.shouldRasterize = true
查看更多
劳资没心,怎么记你
5楼-- · 2019-01-12 18:37

The solution seems much easier than the problem might suggest. I had this with one of my views and used the core part of @Hodit's answer to get it to work. This is all you need actually:

- (void) drawRect:(CGRect)rect {
    // make sure the background is set to a transparent color using IB or code
    // e.g.: self.backgroundColor = [UIColor clearColor]; 

    // draw a rounded rect in the view
    [[UIColor whiteColor] setFill];
    [[UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:5.0] fill];

    // apply shadow if you haven't already
    self.layer.masksToBounds = NO;
    self.layer.shadowColor = [[UIColor blackColor] CGColor];
    self.layer.shadowOffset = CGSizeMake(0.0,3.0);
    self.layer.shadowRadius= 1.0;
    self.layer.shadowOpacity = 0.1;

    // more code here

}

Note that this doesn't clip subviews. Anything positioned at 0,0 in the view will overlap the visible top left rounded corner.

查看更多
爷、活的狠高调
6楼-- · 2019-01-12 18:40

Here's the swift3 version of Hodit's answer, I had to use it and found it over here and did general corrections for XCode 8. Works like charm!

@IBDesignable class RoundRectView: UIView {

@IBInspectable var cornerRadius: CGFloat = 0.0
@IBInspectable var borderColor: UIColor = UIColor.black
@IBInspectable var borderWidth: CGFloat = 0.5
private var customBackgroundColor = UIColor.white
override var backgroundColor: UIColor?{
    didSet {
        customBackgroundColor = backgroundColor!
        super.backgroundColor = UIColor.clear
    }
}

func setup() {
    layer.shadowColor = UIColor.black.cgColor;
    layer.shadowOffset = CGSize.zero
    layer.shadowRadius = 5.0;
    layer.shadowOpacity = 0.5;
    super.backgroundColor = UIColor.clear
}

override init(frame: CGRect) {
    super.init(frame: frame)
    self.setup()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    self.setup()
}

override func draw(_ rect: CGRect) {
    customBackgroundColor.setFill()
    UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius ?? 0).fill()

    let borderRect = bounds.insetBy(dx: borderWidth/2, dy: borderWidth/2)
    let borderPath = UIBezierPath(roundedRect: borderRect, cornerRadius: cornerRadius - borderWidth/2)
    borderColor.setStroke()
    borderPath.lineWidth = borderWidth
    borderPath.stroke()

    // whatever else you need drawn
}
}
查看更多
家丑人穷心不美
7楼-- · 2019-01-12 18:40

This is an older question, but I would have just done everything in your custom draw method like below.

I usually will do this if I know I want to apply a drop shadow to my rounded view (which of course means I don't want to use masksToBounds)

You also don't have to add an extra "shadow view" to the hierarchy.

@IBDesignable
class RoundedView: UIView {

@IBInspectable
var cornerRadius: CGFloat = 0

override func draw(_ rect: CGRect) {
    guard let context = UIGraphicsGetCurrentContext() else { return }
    // You could use custom IBInspectable attributes
    // for the stroke and fill color.
    context.setFillColor(UIColor.white.cgColor)
    context.setStrokeColor(UIColor.orange.cgColor)
    // Add a clipping path to get the rounded look
    // you want.
    UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).addClip()
    // Fill and stroke your background.
    let background = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
    background.lineWidth = 2
    background.fill()
    background.stroke()
}

private func shadow() {
    layer.shadowColor = UIColor.black.cgColor
    layer.shadowRadius = 5
    layer.shadowOpacity = 0.5
    layer.shadowOffset = CGSize.zero
}

override func awakeFromNib() {
    super.awakeFromNib()
    shadow()
}
}
查看更多
登录 后发表回答