『有趣的 Layer』CAGradientLayer 与 mask

CALayer 有个 mask 属性,用作 layer 的遮罩。这个遮罩和普通盖在上面的显示层不同,普通的遮罩是,盖上去,就遮住了下面的内容,而 mask 则是遮什么显示什么。

这一特性,在实现某些效果时,往往会有奇效。本文就来看看如何用 mask 结合 CAGradientLayer 做出有趣的效果。

环境信息:

Mac OS X 10.11.3

iOS 9.3

Swift 2.2

正文

热身

先来看看常见的设置 layer 的方式:

let layer = CALayer()
// 第一种:盖上去的图层(遮住就看不到下面的内容)
view.layer.addSubLayer(layer)
// 第二种:设置为 mask (遮住什么,下面的内容就显示出什么)
view.layer.mask = layer

圆角头像

 

所以对于圆角头像的设置,除了 layer.cornerRadus 以外,还可以使用头像视图的 layer.mask 来达到效果(这里不谈他们的性能问题,仅说实现。对于在性能上的优化,建议大家看耀源的博客:iOS 保持界面流畅的技巧):

// 这里设置什么形状的的 path,就可以显示出什么形状的头像
let path = UIBezierPath(ovalInRect: view.bounds)
let layer = CAShapeLayer()
layer.frame = view.bounds
layer.path = path.CGPath
view.layer.mask = layer

接下来,进入到今天的正题:maskCAGradientLayer

消息渐隐

https://github.com/saitjr/HappyLayerFriends/tree/master/FadeMessage

1. 用到的类

这种渐隐效果,没办法用图片遮罩来代替,因为背景完全可能是个视频,多出现在播放器歌词、视频直播消息这类 app 中。不难想到的是,这里肯定有个颜色透明度的变化,但是,颜色透明度,总要有种颜色啊。白色 0 ~ 1?黑色 0 ~ 1?还是其他颜色?总不可能 clearColor 0 ~ 1 。

对于这种,要什么颜色,说不上来,但是又有渐变效果的,我首先会想到 mask。因为它独有的遮什么显示什么的特性,分析起来实在容易:一个渐变,什么颜色无所谓,但是渐变效果是下面透明,然后越往上,透明度越高(刚好和 addSubLayer 的方式相反)。

2. 层级分析

可以看出,消息列表的渐隐,并没有影响到背景图,所以 mask 不是加在 self.viewimageView 上的,那么应该加在 tableView 上。

但是有个问题,加在 tableView 上,滚动时,会跟随 tableView 一起滚动,mask 不会一直停留在顶部位置。那应该加在哪个视图上面呢?来看看下面这个图:

应该是给 tableViewsuperView 加上渐变的遮罩,这样,只要是放在 tableViewSuperView 上的视图,都是上层渐隐的效果。

3. 实现

具体实现的代码,请见:

https://github.com/saitjr/HappyLayerFriends/tree/master/FadeMessage

为了使渐隐效果更好,需要设置 CAGradientLayerlocations 属性,来设置渐变的分段。

let gradientLayer = CAGradientLayer()
gradientLayer.colors = [UIColor.blackColor().colorWithAlphaComponent(0.2).CGColor, UIColor.blackColor().CGColor]
gradientLayer.frame = maskView.bounds;
gradientLayer.locations = [0, 0.2, 1]

iPhone 解锁

https://github.com/saitjr/HappyLayerFriends/tree/master/UnlockIPhone

iOS 设备有个非常精美的滑动解锁动画。要简单的实现它,并不难,同样是 CAGradientLayermask,玩法咋就那么多呢!

1. 实现思路

或许我们应该先看看另一种效果(暂且称为歌词动画吧,实现代码见 GitHub/TheLyrics/),找找思路:

橙色的滑块明显在字的下方,然后文字是镂空的。为了尽量的减少学习成本,我将前面的镂空文字,在 Sketch 中处理为了一张图(之后实现解锁效果时,我们再用代码去生成)。并不难看出,这个动画的层级:

歌词动画和我们要做的解锁动画非常相似。只不过它参与动画的是 UIViewframe,而解锁动画的是 CAGradientLayerlocationslocations 属性决定了渐变的起始位置(更准确的说,应该是渐变的分段位置),所以,通过改变 locations 属性,就可以做出白色光晕在移动的效果:

让我们来看看解锁动画的视图层级:

2. 实现

1.初始化渐变背景

let gradientLayer = CAGradientLayer()
gradientLayer.frame = containerView.bounds
// 设置渐变色:黑 -> 白 -> 黑
gradientLayer.colors = [UIColor.blackColor().CGColor, UIColor.whiteColor().CGColor, UIColor.blackColor().CGColor]
// locations 之后会做动画,所以这里不必在意
gradientLayer.locations = [0.25, 0.5, 0.75]
// 将默认的上下渐变改为左右渐变(苹果的好像是对角线上的渐变,也可以通过这两个属性修改)
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
// 将渐变层放到父容器上去
containerView.layer.addSublayer(gradientLayer)

2.将文本绘制为图片

let text: NSString = "Saitjr"
// 设置文本样式,这个 demo 中仅设置了居中样式
let style = NSMutableParagraphStyle()
style.alignment = .Center
let textAttribute: [String: AnyObject] = [NSFontAttributeName: UIFont(name: "HelveticaNeue-Thin", size: 30)!, NSParagraphStyleAttributeName: style]
// 初始画布
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, 0)
// 绘制文本
text.drawInRect(containerView.bounds, withAttributes: textAttribute)
// 从上下文中取出
let image = UIGraphicsGetImageFromCurrentImageContext()
// 关闭上下文
UIGraphicsEndImageContext()

3.合成渐变图层与文本图片

到这一步,大家可能会问,说好的 mask 呢?不是 CAGradientLayermask 的组合吗?不要急,马上就来。

因为生成的文本图片并不是镂空的,相反,文本有颜色,而图片的背景色是透明的,这就刚好用上 mask 的特性。所以,我们需要将文本图片设置为渐变层的 mask

let textLayer = CALayer()
textLayer.frame = view.bounds.offsetBy(dx: 0, dy: 20)
textLayer.contents = image.CGImage
gradientLayer.mask = textLayer

4.动画

let animation = CABasicAnimation(keyPath: "locations")
animation.fromValue = [0.0, 0.0, 0.25]
animation.toValue = [0.75, 1.0, 1.0]
animation.duration = 3.0
animation.repeatCount = Float.infinity
gradientLayer.addAnimation(animation, forKey: "LocationAnimation")

完整代码见:

https://github.com/saitjr/HappyLayerFriends/tree/master/UnlockIPhone

最后

CAGradientLayermask 的玩法还有很多,不仅如此,通过 layer 的组合,能做出很多炫酷的动画。为此我专门开了个叫 HappyLayerFriends 的 repo,希望能收集到更多有趣的动画实现,欢迎大家提供动画设计。

发表评论

电子邮件地址不会被公开。