CALayer.mask

先看看最终要实现的效果
notion image
一开始看到这种效果我有点懵,这和普通的阴影不太一样,并不是带有颜色的蒙层,绞尽脑汁想了一会,应该是在上面加一个 CAGradientLayer 吧,但是这层 layer 的颜色到底是什么才对呢?翻了翻 CALayer 的文档,发现了这样一个属性 mask: CALayer?,它是这样说的,
A layer whose alpha channel is used as a mask to select between the layer's background and the result of compositing the layer's contents with its filtered background. Defaults to nil. When used as a mask the layer's compositingFilter and backgroundFilters properties are ignored. When setting the mask to a new layer, the new layer must have a nil superlayer, otherwise the behavior is undefined. Nested masks (mask layers with their own masks) are unsupported.
大概的解释就是,这个 mask 的 alpha 通道会作用在当前的 layer 上,你可以把它想像成一个 filter,layer 后面的 backgroud 如果能透过这个 alpha 层,那它就能与当前 layer 在一起显示。经过一些实验,这个 mask 并不会影响它后面内容的显示。那我们就可以用这个 layer 的 mask 属性搞一些事情出来了。
要想实现上图的效果大概需要要做以下几个事情,
创建一个 CAGradientLayer
lazy var gradientLayer: CAGradientLayer = {
    let v = CAGradientLayer()
    // 这里的颜色是什么都无所谓,因为只有 alpha 通道会起作用
    v.colors = [UIColor.clear.withAlphaComponent(0).cgColor, UIColor.clear.withAlphaComponent(1).cgColor]
    v.locations = [0.0, 1]
    v.rasterizationScale = UIScreen.main.scale
    v.startPoint = CGPoint(x: 0, y: 1)
    v.endPoint = CGPoint(x: 0, y: 0.8)
    return v
}()
因为要透过后面内容的显示,tableView 的背景需要是透明的,
tableView.backgroundColor = UIColor.clear
考虑到列表是会滚动的,把这个 layer 加到 tableView 的 mask 上的固定位置,那列表一滚动,效果就不对了,所以需要把 tableView 加到一个 background 上面去,然后把 layer.mask 设置成这我们之前创建好的 gradientLayer
lazy var tableViewBackgroundView: UIView = {
    let v = UIView()
    v.backgroundColor = UIColor.clear
    v.layer.mask = self.gradientLayer
    return v
}()
由于是 AutoLayout 布局,而 layer 却不能加约束,所以我们监听 backgroundView 的 bounds 手动更新 layer 的 frame(这里用了 RxSwift,其它方式也都 OK)
tableViewBackgroundView.rx.observe(CGRect.self, "bounds")
  .subscribe(onNext: { [weak self] bounds in
      guard let bounds = bounds else { return }
      self?.gradientLayer.frame = bounds
  }).addDisposableTo(bag)
Boom,带有边界渐隐效果的列表就 OK 了,是不是很方便捏

© Xinyu 2014 - 2024