iOS CoreAnimation (七) shadowPath 指定阴影,mask 图层蒙板

通过上一篇文章,我们知道了图层阴影的形状是由图层 “内容” 计算得出的,但实时计算阴影非常消耗资源,尤其是图层有多个子图层,每个子图层还有一个有透明效果的寄宿图的时候。所以如果你事先知道你的阴影形状是什么,可以通过指定一个 shadowPath 来提高性能(CGPath 对象)。它可以是你想要的任何形状(下图)。

 上图代码如下:  

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
      
        let img = UIImage(named:"IMG_0651")
        let size = img!.size
        
        let view1 = UIView(frame:CGRect(x:100, y:160, width:size.width, height:size.height))
        let view2 = UIView(frame:CGRect(x:100, y:380, width:size.width, height:size.height))
     
        view1.layer.contents = img?.cgImage
        view2.layer.contents = img?.cgImage

        view1.layer.contentsGravity = .center
        view2.layer.contentsGravity = .center
        
        view1.layer.contentsScale = UIScreen.main.scale
        view2.layer.contentsScale = UIScreen.main.scale
        
        view1.layer.shadowOpacity = 0.5
        view2.layer.shadowOpacity = 0.5
        
        let path1 = CGPath(rect: view1.bounds.insetBy(dx: -5, dy: -5), transform:nil)
        view1.layer.shadowPath =  path1
        
        let path2 = CGMutablePath()
        path2.addEllipse(in: view2.bounds.insetBy(dx: -5, dy: -5))
        view2.layer.shadowPath =  path2
         
        view.addSubview(view1)
        view.addSubview(view2)
    }
}

其他复杂的 path 根据业务需求去画吧。

图层蒙板

             

上面是两个例子。蒙板的概念其实非常容易理解,原理如下:

现在有两张图片分别叫 image 和 mask,生成的图片叫 result。遍历 mask 中的像素,设位置 i 上的像素是 p,伪代码:

for (i, p) in mask:
    if (p.alpha != 0) 
        result[i] = image[i] 
    else
        result[i] = mask[i] // 透明

如果仍不理解,简单说就是,如果蒙板上某个点是透明的,那么最终图片该点就是透明的;否则该点的颜色是原图的颜色。整个过程只参考了蒙板的 alpha,蒙板的颜色我们不考虑。

 

落到 iOS 代码上,有略微的区别。有两种设置蒙板的方式,第一种是设置 CALayer 的属性 mask,这个属性是 CALayer 类型。

上篇文章提到的计算阴影的方式一样,mask 图层的实际 “内容” 区域将被用作蒙板。(回顾一下:背景色,寄宿图,递归子图层,等结合)

另一种方式是设置 UIView 的属性 mask,这个属性是 UIView 类型,本质是用了 layer,一样的。蒙板相当于定义了视图的可见区域。

 

现在再看上面两个例子:第一个例子就是两个 UIImageView,其中一个作为 mask,太容易了。用 layer 的话设置寄宿图、mask 就完事了。

第二个例子乍一看挺炫的效果,实际仔细一想,下面是一个 UIImageView,然后自定义可见区域,可见区域是文字的轮廓,而 UILabel 恰好是绘制文字作为寄宿图,那就直接用 UILabel 作为 mask。代码如下:

 

 

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
       
        let v = UIImageView(frame: CGRect(x: 100, y: 200, width: 200, height: 200))
        v.image = UIImage(named: "IMG_0651")
        v.contentMode = .scaleAspectFill
        
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
        label.text = "TEST"
        label.font = .boldSystemFont(ofSize: 50)
        v.mask = label
         
        view.addSubview(v)
    }
}

如果想用 layer,可以直接用 label.layer;如果不想用 UILabel,可以用 CATextLayer;如果想用自定义的 path 作为 mask,可以用 CAShapeLayer... 这些专用图层后面还会提到。

(因为 view.mask 和 layer.mask 本质相通的,所以可以设置 layer.mask = view.layer)

前面的文章提到过 裁剪任意几个角是圆角,用上面的思路,只需要一个蒙板 mask 就可以了,用什么作蒙板呢?view 大小不一样,肯定是实时绘制蒙板,一个办法是自定义的 view drawRect 绘制寄宿图,另一个办法就是画一个 CGPath,然后构造一个 CAShapeLayer,这个 path 会在 CAShapeLayer 内算入 “内容” 区域,蒙板就实现了。具体链接:任两个角是圆角的 UIView 的实现

 

最近两篇文章的核心其实都在于对图层 “内容” 的理解,这个概念对很多方面都有影响,属于比较重要的渲染机制。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值