Swift: 打造滑动解锁文字动画
最近木事,找出来玩了玩facebook的paper。到处都是那个"slide to unlock your phone"的效果啊。忽闪忽闪的小有点炫酷的感觉。于是准备研究一下。木有想到的是居然可以用CAGradientLayer和一个小小的动画就可以实现这个效果。“滑动解锁”的效果:
当然啦,首先你需要显示出这个“滑动解锁”的文本。这里咱们就用一个简单的UILabel来解决这个问题。
var textExampleLabel: UILabel! override func viewDidLoad() {
super.viewDidLoad() textExampleLabel = UILabel(frame: CGRectMake(, , UIScreen.mainScreen().bounds.size.width - , ))
textExampleLabel.text = "slide to unlock your phone, slide to unlock your"
self.view.addSubview(textExampleLabel)
}
label作为成员变量(属性),在viewDidLoad方法中初始化并赋给“滑动解锁”的文本。
之后就用Gradient Layer来mask这段文本。来看看怎么准备这mask。要使用CALayer这个东东,千万不能少得就是前提条件需要引入QuartzCore。
import QuartzCore
之后为了迎接随后到来的gradient mask,需要重构一部分代码。让整个背景的颜色都为黑色,让label的文字为白色。这样看起来这个解锁的动画效果在强烈的黑白对比下更加明显。重构之后的代码:
self.view.backgroundColor = UIColor.blackColor() textExampleLabel = UILabel(frame: CGRectMake(, , UIScreen.mainScreen().bounds.size.width - , ))
textExampleLabel.text = "slide to unlock your phone, slide to unlock yo"
textExampleLabel.backgroundColor = UIColor.blackColor() // background color -> black
textExampleLabel.textColor = UIColor.whiteColor() // foreground color -> white
self.view.addSubview(textExampleLabel)
这些都是在viewDidLoad方法中的。
下面开始添加mask:
var gradientMask = CAGradientLayer()
let colors: Array<AnyObject> = [UIColor.blackColor().CGColor, UIColor.whiteColor().CGColor, UIColor.blackColor().CGColor]
gradientMask.colors = colors textExampleLabel.layer.mask = gradientMask
运行出来之后,你会吓一跳。因为,全部都是黑得了。。。
这是因为,mask属性需要用到的是颜色的透明部分。也就是说给定的变化的颜色需要出现一部分透明或者半透明的颜色,这样的gradient layer作为另外一个layer的mask才能发挥作用。另外需要千万注意的一个问题是,上面的gradientMask还需要有一个frame。一般这个值是mask属性所在view的bounds。不要错用了frame,那样gradientMask就mask到错误的地方了。那么补全代码之后,如下:
var testGradient = CAGradientLayer()
testGradient.frame = self.textExampleLabel.bounds testGradient.colors = [UIColor(white: 1.0, alpha: 0.3).CGColor, UIColor.yellowColor().CGColor, UIColor(white: 1.0, alpha: 0.3).CGColor]
testGradient.startPoint = CGPointMake(, 0.5)
testGradient.endPoint = CGPointMake(, 0.5)
testGradient.locations = [, 0.15, 0.3]
start point和end point这两个点分别是在layer的坐标体系中的表示。不是一般的定位frame中使用的x和y。比如,当(0.5,0.5)时,表示的是这个layer的中心,center point。(0,0.5)表示的是layer的伤沿的中点,而对应的(0,0.5)表示的下沿的中点。这里的start point和end point分别是左侧边线和右侧边线的中点。最后出现的渐变色的分界线和这点得连线垂直。这也是一个很重要的规律。
gradient layer的locations是用来指定各个颜色的终止位置的。这里分别是0,0.15和0.3。这里可以把这些点理解为顺着颜色渐变线,也就是start point和end point的连线,的百分比的分布。不过,最后的一点效果是没有的。所以,最后的一种颜色一直延续到gradient layer的终点。
这里可以清楚的看到颜色的变化是从UIColor(white: 1.0, alpha: 0.3).CGColor一直到白色然后剩下的都是UIColor(white: 1.0, alpha: 0.3).CGColor。
到这里,就应该让我们的这个效果动起来了。不动的怎么能试动画呢?!
先补上刚刚漏掉的一句:
self.textExampleLabel.layer.mask = testGradient
添加动画的方式就比较简单了。用得就是远古的core animation:CABasicAnimation。这个animation是作用在gradient layer上得locations属性的。所以这个animation的初始化应该是这样的:
var testAnimation = CABasicAnimation(keyPath: "locations")
然后,这个animation就是从一个locations到另外的一个locations。就像我们看到的iphone的解锁画面一样,高亮一部分的文字,从头到尾,一次又一次的重复。一直到屏幕变暗为止。
var testAnimation = CABasicAnimation(keyPath: "locations")
testAnimation.fromValue = [, 0.15, 0.3]
testAnimation.toValue = [ - 0.3, - 0.15, 1.0];
testAnimation.repeatCount =
testAnimation.duration = 0.3
testAnimation.delegate = self
我们这里,设定的颜色渐变是从0到0.15,然后到0.3。这是开始,那么最后的应该是什么样呢,就是长度为0.3的长度上又三个颜色,所以是从1.0 - 0.3,然后到1.0 - 0.15最后到1.0。中间所缺少的由动画自动补上。
好的,让动画起作用:
testGradient.addAnimation(testAnimation, forKey: "TEST")
只需要给layer添加刚刚初始化并配置好的动画之后。layer的动画就会开始运作。至于forKey的值可以很随意,什么都不给也可以。运行代码你就会看到这个动画的效果了。
但是,如果你仔细观察这个看似很完美的动画,就会发现。使用白色高亮的文字效果没有作用在最开始的几个字母上。而在最后,这个白色的高亮效果也没有出现在最后的几个文字上。所以,这个时候就会用到我们前面讲到的layer的坐标系了。start point和end point的x值都是制定在了0和1上。也就是说灰色会在0到0.15上出现。在动画最后的时候灰色会在1.0 - 0.15到1.0上出现。所以,需要把开始的x值往前移,而把最后的x值往后移动。也就是开始的x值变为负数,而最后的x值应该是1.0 + 某个值。所以,这里我们把start point和end point分别设定为:
testGradient.startPoint = CGPointMake(-0.3, 0.5)
testGradient.endPoint = CGPointMake( + 0.3, 0.5)
这时,在运行这个动画。嗯,一切都完美了。。。
但是。。。又是但是,如果在app中有很多的地方都出现这个效果呢?难道我们要用最简单,最直接的方法来复用这段代码么?这是非常初级和非常可耻的行为,也会给自己埋下定时炸弹。如果需要修改渐变颜色等情况出现,你又忘记修改某一处的代码的时候。。。
我们要重构这段代码,这样在任何的地方使用这个效果的时候可以直接使用我们重构出来的功能代码非常简单的实现这个效果。
添加一个新的swift文件。在这个文件中抽象出我们的“滑动解锁”动画功能:
这里的新的类是继承自NSObject的,因为我们只是需要作用在以后在这里添加的UIView属性,而本身不需要是UIView的子类。
在这个类里面我们需要什么?一个可以设置mask的UIView子类。mask的gradient layer的半透明颜色(么有高亮的时候)和高亮的颜色(白色)。这个动画要持续的重复执行多少次,每次执行的时间是多长...总之,大体上就是这些东西。那么来看看我们的定义:
var animationView: UIView?
var notHighlightColor: UIColor!
var highlightColor: UIColor!
var currentAnimation: CABasicAnimation?
var effectWidth: CGFloat = 20.0 // width of the gradient colors will take effect let repeatCount: Float = // here, we will let the animation repeat like forever
let animationDuration: CFTimeInterval = 0.5
let kTextAnimationKey = "TextAnimation"
出了上面我们说的主要需要的东西以外,就是一些动画的成员变量和动画的key值。为之后动画停止执行的时候删除相对应的动画,而不是删除可能给这个layer添加的别的动画。
开始执行动画:
override init(){
notHighlightColor = UIColor(white: 1.0, alpha: 0.3)
highlightColor = UIColor.whiteColor()
}
在初始化的时候,初始化非高亮的颜色和高亮的颜色。
开始执行动画的方法:
func start(){
if self.animationView == nil {
print("animtion view is nil!")
return
} // clear things used last time
self.stop() var gradientMask = CAGradientLayer()
gradientMask.frame = self.animationView!.bounds var gradientSize = self.effectWidth / CGRectGetWidth(self.animationView!.frame) var startLocations: Array<AnyObject> = [, gradientSize / 2.0, gradientSize]
var endLocations: Array<AnyObject> = [1.0 - gradientSize, 1.0 - (gradientSize / 2.0), 1.0] var colors: Array<AnyObject> = [self.notHighlightColor.CGColor, self.highlightColor.CGColor, self.notHighlightColor.CGColor]
gradientMask.colors = colors
gradientMask.locations = startLocations
gradientMask.startPoint = CGPointMake(-gradientSize, 0.5)
gradientMask.endPoint = CGPointMake( + gradientSize, 0.5) self.animationView!.layer.mask = gradientMask self.currentAnimation = CABasicAnimation(keyPath: "locations")
self.currentAnimation!.fromValue = startLocations
self.currentAnimation!.toValue = endLocations
self.currentAnimation!.repeatCount = self.repeatCount
self.currentAnimation!.duration = self.animationDuration
self.currentAnimation!.delegate = self gradientMask.addAnimation(self.currentAnimation, forKey: kTextAnimationKey)
}
在方法中首先判断动画作用的UIView子类是否存在,如果不存在的时候则立即返回。如果存在,则把刚才我们重构的主要功能的代码全部都放在这里来执行动画。这里有变化的是渐变颜色的区域是在程序中可以指定的,不是刚开始的时候是我们hard code在代码中得。在编程中需要注意,最好不要出现hard code的情况。最不好的情况也需要把这个设定为常量属性。遗留在代码中得hard code代码几乎肯定会产生bug!
下面看看在start方法中调用的stop方法。主要是清楚在上一次的动画执行中的相关的东西。避免干扰。
func stop(){
if self.animationView != nil && self.animationView?.layer.mask != nil {
self.animationView?.layer.mask.removeAnimationForKey(kTextAnimationKey)
self.animationView?.layer.mask = nil
self.currentAnimation = nil
}
}
这里清楚了layer得mask和在mask上面的动画。
最后是代理的方法animationDidStop(anim: CAAnimation!, finished flag: Bool) 。执行这个方法的时候就表明动画已经停止。这里也可以执行我们的stop方法。
override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
if anim == self.currentAnimation {
self.stop()
}
}
重构完成之后,看看应该怎么使用。我们暴露给其他代码的调用接口,前面已经提到过。执行这个动画的UIView子类,和渐变色的长度:
self.view.backgroundColor = UIColor.blackColor() textExampleLabel = UILabel(frame: CGRectMake(, , UIScreen.mainScreen().bounds.size.width - , ))
textExampleLabel.text = "slide to unlock your phone, slide to unlock yo"
textExampleLabel.backgroundColor = UIColor.blackColor() // background color -> black
textExampleLabel.textColor = UIColor.whiteColor() // foreground color -> white
self.view.addSubview(textExampleLabel) self.textAnimation = TextAnimation()
self.textAnimation.animationView = textExampleLabel
self.textAnimation.effectWidth = 50.0
然后在view appear之后执行动画:
self.textAnimation.start()
好的,现在来看重构之后的全部的代码:
import UIKit
import QuartzCore class ViewController: UIViewController { var textExampleLabel: UILabel!
var textAnimation: TextAnimation! override func viewDidLoad() {
super.viewDidLoad() self.view.backgroundColor = UIColor.blackColor() textExampleLabel = UILabel(frame: CGRectMake(, , UIScreen.mainScreen().bounds.size.width - , ))
textExampleLabel.text = "slide to unlock your phone, slide to unlock yo"
textExampleLabel.backgroundColor = UIColor.blackColor() // background color -> black
textExampleLabel.textColor = UIColor.whiteColor() // foreground color -> white
self.view.addSubview(textExampleLabel) self.textAnimation = TextAnimation()
self.textAnimation.animationView = textExampleLabel
self.textAnimation.effectWidth = 50.0
} override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated) self.textAnimation.start()
} override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
全文完!
参考:https://github.com/jonathantribouharet/JTSlideShadowAnimation
Swift: 打造滑动解锁文字动画的更多相关文章
- css3做ipone当时的滑动解锁闪亮条
现在一般的登录 注册 什么 的页面,都是会做个滑动验证.一般都是像IPONE早期那个 滑动开屏的效果 ,这个效果现在可以用CSS3来实现. 主要用到几个属性 background 背景使用渐变属性, ...
- hiho_1054_滑动解锁
题目大意 智能手机九点屏幕滑动解锁,如果给出某些连接线段,求出经过所有给出线段的合法的滑动解锁手势的总数.题目链接: 滑动解锁 题目分析 首先,尝试求解没有给定线段情况下,所有合法的路径的总数.可以使 ...
- 分享十个CSS3鼠标滑过文字动画特效
介绍10组基于CSS3的鼠标滑过文字动画特效,有上凸.下凹等文字动画.这些炫酷的CSS3文字效果可以让网页变得更加绚丽.效果图如下: 在线预览 源码下载 实现的代码. html代码: <h2 ...
- hihocoder#1054 : 滑动解锁(深度优先搜索)
描述 滑动解锁是智能手机一项常用的功能.你需要在3x3的点阵上,从任意一个点开始,反复移动到一个尚未经过的"相邻"的点.这些划过的点所组成的有向折线,如果与预设的折线在图案.方向上 ...
- 仿知乎/途家导航栏渐变文字动画效果-b
demo.gif 效果图如上,简单分析下 1.导航栏一开始是隐藏的,随着scrollView滚动而渐变 2.导航栏左右两边的navigationItem是一直显示的 3.导航栏参考了途家app,使用了 ...
- Android之自定义画图文字动画
结构: BaseView: package com.caiduping.canvas; import android.content.Context; import android.graphics. ...
- Android自己定义组件系列【8】——面膜文字动画
我们掩盖文字动画Flash中非经货共同体共同,由于Android应用程序开发人员做你想要做这个动画在应用程序中去?本文中,我们看的是如何自己的定义ImageView来实现让一张文字图片实现文字的遮罩闪 ...
- jquery使用CSS3实现文字动画效果插件Textillate.js
Textillate是一款基于jquery的使用CSS3实现文字动画的小巧插件.Textillate.js集成了一些很棒的使用CSS3动画效果的 JavaScript 库,您可非常轻轻松地把这些动画效 ...
- Selenium模拟JQuery滑动解锁
滑动解锁一直做UI自动化的难点之一,我补一篇滑动解锁的例子,希望能给初做Web UI自动化测试的同学一些思路. 首先先看个例子. https://www.helloweba.com/demo/2017 ...
随机推荐
- 要引入java吸管工具
Java调用颜色选择器robot方法getPixelColor让我们来看看下面的方法robot详细的实施类方法中,例如,下面的 getPixelColor public Color getPixelC ...
- crontab,想说爱你不easy
悲剧的背景 跑自己主动化脚本的机器连不上toastserver了, 仅仅能自己写个脚本每天跑了. 当然要放在crontab里了. 5 3 * * * sh ~/nosecron.sh 更悲剧的结果 第 ...
- hdu 2074 堆放篮 好开心图纸标题
堆放篮 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submis ...
- sql server基本流程语句
- 重新想象 Windows 8 Store Apps (19) - 动画: 线性动画, 关键帧动画, 缓动动画
原文:重新想象 Windows 8 Store Apps (19) - 动画: 线性动画, 关键帧动画, 缓动动画 [源码下载] 重新想象 Windows 8 Store Apps (19) - 动画 ...
- 活动图(Activity Diagram) - 项目分解文章
案例基础上登录用户进行操作的每个模块. 1. 员 (1) 列车顺序表 (2) 货车装卸报告(数据处理) (3) 货车装卸报告(查看) 2. 管理员 (1) password管理 (2) 查看日志 (3 ...
- Android 学习历程摘要(二)
1.资源文件命名仅仅能小写,否则会报错生成不了R.java文件 2.R文件导包时应该导入自己project的包,而不是android.R 3.数据库操作使用SqliteOpenHelper 4.val ...
- 【Spring】Spring学习笔记-01-入门级实例
听说当前Spring框架很流行,我也准备好好学学Spring开发,并将学习的过程和大家分享,希望能对志同道合的同学有所帮助. 以下是我学习Spring的第一个样例. 1.Spring开发环境的搭建 我 ...
- 于win7使用虚拟磁盘隐藏文件
于win7使用虚拟磁盘隐藏文件,我只是win7在验证.其他型号未知. 一.创建虚拟磁盘 1.右键点击"计算机"-----"管理" ------"磁盘管 ...
- Lua中的require(转)
lua中的require机制 为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来.现在看看lua的require的处理流程.1.require机制相关 ...