要求:
- Platform: iOS8.0+
- Language: Swift3.1
- Editor: Xcode8.3+
2018-06-27補充
要想實現(xiàn)任意圓角+陰影只恨,則只能通過兩個view(也可以是一個view的多個layer)實現(xiàn)了,其中一個view只負責投影(注意背景色為nil)抬虽,另外一個負責圓角顯示(顯示內容主要在這個view上)官觅。
- view方式
// 陰影
self.shadowView.backgroundColor = nil;
self.shadowView.layer.masksToBounds = NO;
self.shadowView.layer.shadowOpacity = 1;
self.shadowView.layer.shadowOffset = CGSizeZero;
self.shadowView.layer.shadowRadius = 10;
// 任意圓角
CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:self.cornerView.bounds
byRoundingCorners:UIRectCornerTopLeft cornerRadii:CGSizeMake(40, 40)].CGPath;
CAShapeLayer *lay = [CAShapeLayer layer];
lay.path = path;
self.cornerView.layer.mask = lay;
- layer方式
// 陰影
self.shadowView.backgroundColor = nil;
self.shadowView.layer.masksToBounds = NO;
self.shadowView.layer.shadowOpacity = 1;
self.shadowView.layer.shadowOffset = CGSizeZero;
self.shadowView.layer.shadowRadius = 10;
CALayer *cornerLayer = [CALayer layer];
cornerLayer.frame = self.shadowView.bounds;
cornerLayer.backgroundColor = self.cornerView.backgroundColor.CGColor;
// 任意圓角
CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:self.cornerView.bounds
byRoundingCorners:UIRectCornerTopLeft cornerRadii:CGSizeMake(40, 40)].CGPath;
CAShapeLayer *lay = [CAShapeLayer layer];
lay.path = path;
cornerLayer.mask = lay;
[self.shadowView.layer addSublayer:cornerLayer];
2018-04-28補充
后來嘗試發(fā)現(xiàn):view的圓角亏推、陰影公荧、邊框其實是可以共存的(注意不要設置
maskToBounds
為YES、并且最為關鍵的是borderWidth
不要設置為0诫舅,這樣就可以達到圓角-borderWidth導致的圓角笛辟,和陰影共存了)
_backView.layer.shadowColor = UIColor.greenColor.CGColor;
_backView.layer.borderColor = _backView.layer.shadowColor; // 邊框顏色建議和陰影顏色一致
_backView.layer.borderWidth = 0.000001; // 只要不為0就行
_backView.layer.cornerRadius = 40;
_backView.layer.shadowOpacity = 1;
_backView.layer.shadowRadius = 20;
_backView.layer.shadowOffset = CGSizeZero;
1. 圓角
給一個UIView設置圓角主要通過:
view.layer.cornerRadius = 10
也可以設置一些屬性功氨,增加繪制效率
// 設置緩存
view.layer.shouldRasterize = true
// 設置抗鋸齒邊緣
view.layer.rasterizationScale = UIScreen.main.scale
// 內容縮放比
view.layer.contentsScale = UIScreen.main.scale
2. 陰影
給一個UIView設置陰影主要通過:
view.shadowColor = color.cgColor
view.shadowOffset = CGSize(width: offsetX, height: offsetY)
view.shadowRadius = radius
view.shadowOpacity = opacity //默認為0
// view.shadowPath = UIBezierPath(rect: view.bounds).CGPath
3. 任意圓角
如果要給一個view設置任意的一個/多個圓角,那么上面的方法就不適用了手幢,得設置view.layer.mask達到任意圓角/形狀的目的:
let corners: UIRectCorner = [UIRectCorner.topLeft, UIRectCorner.topRight]
// 或者設置所有圓角.allCorners
let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
maskLayer.frame = bounds
view.layer.mask = maskLayer
4. 繪制陰影
這種情況下,就無法通過屬性實現(xiàn)了弯菊,而是繪制矩形纵势,并繪制和其綁定的陰影圖形:
override func draw(_ rect: CGRect) {
super.draw(rect)
let path = UIBezierPath(rect: bounds.insetBy(dx: 20, dy: 20))
UIColor.white.setFill() //路徑填充顏色
let conext = UIGraphicsGetCurrentContext()
conext?.setShadow(offset: CGSize(width: 10, height: 10), blur: 10, color: UIColor.black.cgColor) //陰影繪制踱阿,直接附加到path上了
path.fill()
}
5. 任意圓角+陰影
如果同時要實現(xiàn)任意圓角+陰影管钳,只能通過4+3的方式在draw(_ rect: CGRect)
方法中:
- 繪制陰影
let corners: UIRectCorner = [UIRectCorner.topLeft, UIRectCorner.topRight]
let shadowPath = UIBezierPath(roundedRect: rect.insetBy(dx: shadowRadius, dy: shadowRadius), byRoundingCorners: corners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
let context = UIGraphicsGetCurrentContext()
context?.setShadow(offset: shadowOffset, blur: shadowRadius, color: shadowColor.cgColor)
fillColor.setFill()
shadowPath.fill()
- 繪制圓角
let corners: UIRectCorner = [UIRectCorner.topLeft, UIRectCorner.topRight]
let maskPath = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
let maskLayer = CAShapeLayer()
maskLayer.path = maskPath.cgPath
maskLayer.frame = rect
layer.mask = maskLayer
6. 源碼
- 支持xib & stroyboard
// CornerShadowView.swift
import UIKit
@IBDesignable
class CornerShadowView: UIView {
// MARK: - IBOutlet
// MARK: - properties
@IBInspectable
public var topLeft: Bool = false{
didSet{
redraw()
}
}
@IBInspectable
public var bottomLeft: Bool = false{
didSet{
redraw()
}
}
@IBInspectable
public var topRight: Bool = false{
didSet{
redraw()
}
}
@IBInspectable
public var bottomRight: Bool = false{
didSet{
redraw()
}
}
@IBInspectable
public var cornerRadius: CGFloat = 10{
didSet{
redraw()
}
}
@IBInspectable
public var shadowOffset: CGSize = .zero{
didSet{
redraw()
}
}
@IBInspectable
public var shadowRadius: CGFloat = 0{
didSet{
redraw()
}
}
@IBInspectable
public var shadowColor: UIColor = .black{
didSet{
redraw()
}
}
@IBInspectable
public var fillColor: UIColor = .white{
didSet{
redraw()
}
}
public var margins: UIEdgeInsets = .zero{
didSet{
redraw()
}
}
// MARK: - initial method
override func draw(_ rect: CGRect) {
super.draw(rect)
var corners: UIRectCorner = []
if topLeft {
corners.insert(.topLeft)
}
if bottomLeft {
corners.insert(.bottomLeft)
}
if topRight {
corners.insert(.topRight)
}
if bottomRight {
corners.insert(.bottomRight)
}
if shadRadius > 0 {
//繪制陰影
let shadowPath = UIBezierPath(roundedRect: rect.insetsWithMargins(margins), byRoundingCorners: corners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
let context = UIGraphicsGetCurrentContext()
context?.setShadow(offset: shadOffset, blur: shadowRadius, color: shadowColor.cgColor)
fillColor.setFill()
shadowPath.fill()
}
//繪制mask
let maskPath = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
let maskLayer = CAShapeLayer()
maskLayer.path = maskPath.cgPath
maskLayer.frame = rect
layer.mask = maskLayer
}
// MARK: - lifecycle
override func awakeFromNib() {
super.awakeFromNib()
}
// MARK: - private method
fileprivate func redraw(){
setNeedsDisplay()
if shadowRadius > 0 {
backgroundColor = UIColor.clear
}
}
}