UIView with shadow, rounded corners and custom drawRect

前端 未结 16 1439
南旧
南旧 2020-12-02 05:22

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

16条回答
  •  情书的邮戳
    2020-12-02 05:46

    Shadow is dropped from whatever is inside view's layer. When you disable clipping, entire layer rectangle gets filled with default backgroundColor so the shadow becomes rectangular too. Instead of clipping it with rounded mask just make layer's contents rounded, draw them yourself. And layer's border is drawn around its bounds, so you need to draw it yourself too.

    For example, in backgroundColor setter set actual background color to clearColor and use passed color in drawRect to draw a rounded rect with.

    In example below I declare properties as IBInspectable and the whole class as IBDesignable, so everything can be set in storyboard. This way you can even use default Background selector to change your rounded rect color.

    Swift

    @IBDesignable class RoundRectView: UIView {
    
        @IBInspectable var cornerRadius: CGFloat = 0.0
        @IBInspectable var borderColor: UIColor = UIColor.blackColor()
        @IBInspectable var borderWidth: CGFloat = 0.5
        private var customBackgroundColor = UIColor.whiteColor()
        override var backgroundColor: UIColor?{
            didSet {
                customBackgroundColor = backgroundColor!
                super.backgroundColor = UIColor.clearColor()
            }
        }
    
        func setup() {
            layer.shadowColor = UIColor.blackColor().CGColor;
            layer.shadowOffset = CGSizeZero;
            layer.shadowRadius = 5.0;
            layer.shadowOpacity = 0.5;
            super.backgroundColor = UIColor.clearColor()
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            self.setup()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            self.setup()
        }
    
        override func drawRect(rect: CGRect) {
            customBackgroundColor.setFill()
            UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius ?? 0).fill()
    
            let borderRect = CGRectInset(bounds, borderWidth/2, borderWidth/2)
            let borderPath = UIBezierPath(roundedRect: borderRect, cornerRadius: cornerRadius - borderWidth/2)
            borderColor.setStroke()
            borderPath.lineWidth = borderWidth
            borderPath.stroke()
    
            // whatever else you need drawn
        }
    }
    

    Swift 3

    @IBDesignable class RoundedView: 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
    }
    }
    

    Objective-C .h

    IB_DESIGNABLE
    @interface RoundRectView : UIView
    @property IBInspectable CGFloat cornerRadius;
    @property IBInspectable UIColor *borderColor;
    @property IBInspectable CGFloat borderWidth;
    @end
    

    Objective-C .m

    @interface RoundRectView()
    @property UIColor *customBackgroundColor;
    @end
    
    @implementation RoundRectView
    
    -(void)setup{
        self.layer.shadowColor = [UIColor blackColor].CGColor;
        self.layer.shadowOffset = CGSizeZero;
        self.layer.shadowRadius = 5.0;
        self.layer.shadowOpacity = 0.5;
        [super setBackgroundColor:[UIColor clearColor]];
    }
    
    - (instancetype)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            [self setup];
        }
        return self;
    }
    
    - (instancetype)initWithCoder:(NSCoder *)coder
    {
        self = [super initWithCoder:coder];
        if (self) {
            [self setup];
        }
        return self;
    }
    
    -(void)setBackgroundColor:(UIColor *)backgroundColor{
        self.customBackgroundColor = backgroundColor;
        super.backgroundColor = [UIColor clearColor];
    }
    
    -(void)drawRect:(CGRect)rect{
        [self.customBackgroundColor setFill];
        [[UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.cornerRadius] fill];
    
        CGFloat borderInset = self.borderWidth/2;
        CGRect borderRect = CGRectInset(self.bounds, borderInset, borderInset);
        UIBezierPath *borderPath = [UIBezierPath bezierPathWithRoundedRect:borderRect cornerRadius:self.cornerRadius - borderInset];
        [self.borderColor setStroke];
        borderPath.lineWidth = self.borderWidth;
        [borderPath stroke];
    
        // whatever else you need drawn
    }
    
    @end
    

    Result

提交回复
热议问题