import UIKit
@IBDesignable
class ShadowView: UIView {
// MARK: - 背景色优化(核心修改点)
override var backgroundColor: UIColor? {
didSet {
// 当外部设置背景色时,优先使用自定义值
if backgroundColor != nil {
layer.backgroundColor = backgroundColor?.cgColor
} else {
// 未设置时使用默认白色
layer.backgroundColor = UIColor.white.cgColor
}
}
}
// MARK: - 阴影属性
@IBInspectable var shadowColor: UIColor = .black.withAlphaComponent(0.3) {
didSet { layer.shadowColor = shadowColor.cgColor }
}
@IBInspectable var shadowOpacity: Float = 0.5 {
didSet { layer.shadowOpacity = shadowOpacity }
}
@IBInspectable var shadowOffset: CGSize = CGSize(width: 0, height: 2) {
didSet { layer.shadowOffset = shadowOffset }
}
@IBInspectable var shadowRadius: CGFloat = 4 {
didSet {
layer.shadowRadius = shadowRadius
setNeedsShadowPathUpdate()
}
}
// MARK: - 圆角属性
@IBInspectable var cornerRadius: CGFloat = 0 {
didSet {
if !hasCustomRadius {
layer.cornerRadius = cornerRadius
setNeedsShadowPathUpdate()
}
}
}
// 独立圆角控制
@IBInspectable var topLeftRadius: CGFloat = 0 {
didSet { setNeedsCornerMaskUpdate() }
}
@IBInspectable var topRightRadius: CGFloat = 0 {
didSet { setNeedsCornerMaskUpdate() }
}
@IBInspectable var bottomLeftRadius: CGFloat = 0 {
didSet { setNeedsCornerMaskUpdate() }
}
@IBInspectable var bottomRightRadius: CGFloat = 0 {
didSet { setNeedsCornerMaskUpdate() }
}
// MARK: - 边框属性
@IBInspectable var borderColor: UIColor = .clear {
didSet { layer.borderColor = borderColor.cgColor }
}
@IBInspectable var borderWidth: CGFloat = 0 {
didSet { layer.borderWidth = borderWidth }
}
// MARK: - 私有属性
private var needsShadowPathUpdate = true
private var cornerMaskLayer: CAShapeLayer?
// MARK: - 初始化
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
layer.masksToBounds = false
// 初始化时强制设置默认背景色
if backgroundColor == nil {
layer.backgroundColor = UIColor.white.cgColor
}
updateCornerMask()
updateShadowPath()
}
// MARK: - 布局更新
override func layoutSubviews() {
super.layoutSubviews()
if needsShadowPathUpdate {
updateShadowPath()
needsShadowPathUpdate = false
}
updateCornerMask()
}
// MARK: - 圆角处理
private var hasCustomRadius: Bool {
[topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius].contains { $0 > 0 }
}
private func setNeedsCornerMaskUpdate() {
updateCornerMask()
setNeedsShadowPathUpdate()
}
private func updateCornerMask() {
guard hasCustomRadius else {
cornerMaskLayer = nil
layer.mask = nil
layer.cornerRadius = cornerRadius
return
}
let path = UIBezierPath(
roundedRect: bounds,
topLeftRadius: topLeftRadius,
topRightRadius: topRightRadius,
bottomLeftRadius: bottomLeftRadius,
bottomRightRadius: bottomRightRadius
)
let maskLayer = cornerMaskLayer ?? CAShapeLayer()
maskLayer.path = path.cgPath
layer.mask = maskLayer
cornerMaskLayer = maskLayer
}
// MARK: - 阴影路径优化
private func setNeedsShadowPathUpdate() {
needsShadowPathUpdate = true
setNeedsLayout()
}
private func updateShadowPath() {
guard !bounds.isEmpty else { return }
let shadowSpread = shadowRadius * 1.5
let shadowRect = bounds.insetBy(dx: -shadowSpread, dy: -shadowSpread)
let path: UIBezierPath
if hasCustomRadius {
path = UIBezierPath(
roundedRect: shadowRect,
topLeftRadius: topLeftRadius + shadowSpread,
topRightRadius: topRightRadius + shadowSpread,
bottomLeftRadius: bottomLeftRadius + shadowSpread,
bottomRightRadius: bottomRightRadius + shadowSpread
)
} else {
path = UIBezierPath(roundedRect: shadowRect, cornerRadius: cornerRadius + shadowSpread)
}
layer.shadowPath = path.cgPath
}
// MARK: - 暗黑模式适配
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
adjustShadowForCurrentTheme()
}
}
private func adjustShadowForCurrentTheme() {
let isDarkMode = traitCollection.userInterfaceStyle == .dark
shadowOpacity = isDarkMode ? min(0.8, shadowOpacity * 1.2) : max(0.3, shadowOpacity * 0.8)
}
}
// MARK: - UIBezierPath扩展
private extension UIBezierPath {
convenience init(
roundedRect rect: CGRect,
topLeftRadius: CGFloat,
topRightRadius: CGFloat,
bottomLeftRadius: CGFloat,
bottomRightRadius: CGFloat
) {
self.init()
let width = rect.width
let height = rect.height
// 确保圆角不超过视图尺寸的一半
let minSide = min(width, height) / 2
let tl = min(topLeftRadius, minSide)
let tr = min(topRightRadius, minSide)
let bl = min(bottomLeftRadius, minSide)
let br = min(bottomRightRadius, minSide)
move(to: CGPoint(x: tl, y: 0))
addLine(to: CGPoint(x: width - tr, y: 0))
addArc(
withCenter: CGPoint(x: width - tr, y: tr),
radius: tr,
startAngle: -.pi / 2,
endAngle: 0,
clockwise: true
)
addLine(to: CGPoint(x: width, y: height - br))
addArc(
withCenter: CGPoint(x: width - br, y: height - br),
radius: br,
startAngle: 0,
endAngle: .pi / 2,
clockwise: true
)
addLine(to: CGPoint(x: bl, y: height))
addArc(
withCenter: CGPoint(x: bl, y: height - bl),
radius: bl,
startAngle: .pi / 2,
endAngle: .pi,
clockwise: true
)
addLine(to: CGPoint(x: 0, y: tl))
addArc(
withCenter: CGPoint(x: tl, y: tl),
radius: tl,
startAngle: .pi,
endAngle: -.pi / 2,
clockwise: true
)
close()
}
}
IOS-封装了一个好用的给view阴影圆角边框的控件ShadowView
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
推荐阅读更多精彩内容
- 平常按以下方法就可以切圆角 view.layer.cornerRadius = 10 但是imageView切圆角...
- 小Tips:如何知道有没有离屏渲染Off-Screen Rendering?Do:通过模拟器->debug->Co...