iOS Tip: 设置 View 的 cornerRadius 和 shadow

3,268 阅读2分钟

想使一个 View 的 cornerRadius 生效,需要设置 targetView.layer.masksToBounds = true 或者 targetView.clipsToBounds = true (其实是同一个东西,表示超出 view 外面的内容是否绘制,默认是 false)

想使一个 View 的 shadow 阴影效果生效,需要设置 targetView.layer.masksToBounds = false

所以在同一个 view 里面设置 cornerRadius 和 shadow 就冲突了。

阴影不生效

设置不是都生效的代码所示:

    private func shadowAndCornerNotWork() {
        let rect = CGRect(x: 100, y: 200, width: 200, height: 200)
        let cornerRadius: CGFloat = 20
        
        let imageView = UIImageView(frame: rect)
        imageView.image = UIImage(named: "img1") // 设置内容
        
        // cornerRadius ... 起左右
        imageView.layer.cornerRadius = cornerRadius
        imageView.layer.masksToBounds = true // 使内容的 cornerRadius 有效
        
        // shadow ... 不起左右
        imageView.layer.shadowOpacity = 0.7
        imageView.layer.shadowRadius = 20
        imageView.layer.shadowColor = UIColor.red.cgColor
        imageView.layer.shadowOffset = CGSize(width: 0, height: 0)
        
        view.addSubview(imageView)
    }

cornerRadius 和 shadow 都生效 1 (规则圆角)

解决办法其实很简单,设置2个 view。

  • 一个 view 显示内容,设置 cornerRadius
  • 一个 view 显示 shadow
    private func shadowAndCornerEfficient() {
        let rect = CGRect(x: 100, y: 200, width: 200, height: 200)
        let cornerRadius: CGFloat = 40
        
        // 不会离屏渲染
        let shadowView = UIView(frame: rect)

        shadowView.layer.shadowOpacity = 0.7
        shadowView.layer.shadowRadius = cornerRadius
        shadowView.layer.shadowColor = UIColor.red.cgColor
        shadowView.layer.shadowOffset = CGSize(width: 0, height: 0)

        let path0 = UIBezierPath(roundedRect: shadowView.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius)).cgPath
        shadowView.layer.shadowPath = path0 // 不会离屏渲染
        
        // 不会离屏渲染
        let cornerImageView = UIImageView(frame: rect)
        cornerImageView.image = UIImage(named: "img1")
        cornerImageView.layer.cornerRadius = cornerRadius
        cornerImageView.layer.masksToBounds = true
                
        // 3 Add
        view.addSubview(shadowView)
        view.addSubview(cornerImageView)
    }

(可以通过打开模拟器的 Debug -> Color off-screen Rendered 查看。离屏渲染会导致额外的开销,如果很多 view 都开了离屏渲染会导致屏幕卡顿现象)

cornerRadius 和 shadow 都生效 2 (不规则圆角)

    func shadowAndCornerNotEfficient() {
        let rect = CGRect(x: 100, y: 200, width: 200, height: 200)
        let cornerRadius: CGFloat = 40
        
        // 1 shadowView
        // 不会离屏渲染
        let shadowView = UIView(frame: rect)
        
        shadowView.layer.shadowOpacity = 0.7
        shadowView.layer.shadowRadius = cornerRadius
        shadowView.layer.shadowColor = UIColor.red.cgColor
        shadowView.layer.shadowOffset = CGSize(width: 0, height: 0)
        
        let path0 = UIBezierPath(roundedRect: shadowView.bounds, byRoundingCorners: [.topRight, .bottomLeft], cornerRadii: CGSize(width: cornerRadius, height: cornerRadius)).cgPath
        shadowView.layer.shadowPath = path0 // 不会离屏渲染

        // 2 cornerRadius
        let cornerView = UIView(frame: rect)
        let cornerLayer = CAShapeLayer()
        
        let path1 = UIBezierPath(roundedRect: cornerView.bounds, byRoundingCorners: [.topRight, .bottomLeft], cornerRadii: CGSize(width: cornerRadius, height: cornerRadius)).cgPath
        cornerLayer.path = path1
        
        cornerView.layer.mask = cornerLayer // 离屏渲染
        cornerView.layer.contents = UIImage(named: "img1")?.cgImage
        
        // 3 Add
        view.addSubview(shadowView)
        view.addSubview(cornerView)
    }