iOS中圆角视图加阴影的方案对比及封装。

3,137 阅读2分钟

熟悉iOS开发的同学可能都知道圆角视图加阴影一直都是一件比较头疼的事情。

有两种常见的方式:

1.

只设置layercornerRadius, 不设置 layermaskToBounds 弊端就是设置当设置了maskToBounds = true 时阴影会失效。

2.

在圆角的UIView后面藏一个稍微小一些的视图,圆角的视图挡住后面的小视图, 设置后面的小视图的阴影即可。 弊端,需要根据圆角的大小去调试背后小视图的大小。

方案二

除了上面的两种方法外是否还有更好的方法去给圆角视图加阴影?

iOS Core Animation: Advanced Techniques 一书中讲Shadow Clipping的时候有下面一段话和示意图。

layer’s shadow derives from the exact shape of its contents, not just the bounds and cornerRadius. To calculate the shape of the shadow, Core Animation looks at the backing image (as well as the sublayers, if there are any) and uses these to create a shadow that perfectly matches the shape of the layer

layer的阴影并不像bounds 和 cornerRadius一样是固定的,而是由其具体的内容的形状而生成。 为了得到阴影的形状,Core Animation 会根据其内容来完美的适配阴影效果。

Shadow Clipping

根据这个原理我们就有了方案3:

将需要圆角的视图包裹到一个同等大小的透明视图中,圆角的视图负责圆角,透明的视图负责添加阴影。

WeCom20210425-194442.png

封装

方案3确定后就开始做圆角阴影视图的封装:

针对于UIButton的封装

class CornerShadowButton: UIView {
    
    var childView: UIButton = UIButton()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        configBaseUI()
    }
    
    private func configBaseUI() {
        childView = UIButton()
        addSubview(childView)
        childView.frame = bounds
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

同理,UIImageView, UIView, UILabel都可以用相似的方法封装。

但其实,并没有必要这样。。

下面本文的重点来了:

在Swift中可以通过泛型写出很优美便捷的代码。

/// 阴影圆角的视图
class CornerShadowView<T: UIView>: UIView {
    
    var childView: T = T()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        configBaseUI()
    }
    
    private func configBaseUI() {
        childView = T()
        addSubview(childView)
        childView.frame = bounds
    }
  
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

针对于UIButton类的使用:

// 设置泛型的具体类为 UIButton
let cornerShadowView = CornerShadowView<UIButton>(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
// UIButton的基本属性设置
cornerShadowView.childView.setTitle("Hi", for: .normal)

// UIButton的圆角属性设置 可以进行二次封装,略。
cornerShadowView.childView.backgroundColor = .red
cornerShadowView.childView.layer.cornerRadius = 50
cornerShadowView.childView.layer.masksToBounds = true

// 阴影设置 可以进行二次封装,略。
cornerShadowView.layer.shadowColor = UIColor.black.cgColor
cornerShadowView.layer.shadowOffset = .zero
cornerShadowView.layer.shadowRadius = 20
cornerShadowView.layer.shadowOpacity = 0.8

效果如下:

image.png

UIImageView, UIView, UILabel 同理,将泛型中的具体类替换即可。

文章内容仅为个人拙见,如有更好的方法或不对欢迎指正。Thx.