iOS开荒之模仿Mac版QQ登入动画,构建滑动解锁文字

每次登录QQ的时候都觉得登录框的动画挺小清新的,今天想着自己实现一下这个小功能 ,首先效果如图

一、CAShapeLayer简介

CAShapeLayer属于QuartzCore框架,继承自CALayer。CAShapeLayer是在坐标系内绘制贝塞尔曲线的,通过绘制贝塞尔曲线,设置shape的path,从而绘制各种各样的图形以及不规则图形。因此,使用CAShapeLayer需要与UIBezierPath一起使用。UIBezierPath类允许你在自定义的 View 中绘制和渲染由直线和曲线组成的路径.。你可以在初始化的时候直接为你的UIBezierPath指定一个几何图形。通俗点就是UIBezierPath用来指定绘制图形路径,而CAShapeLayer就是根据路径来绘图的。

最近木事,找出来玩了玩facebook的paper。到处都是那个"slide to unlock your phone"的效果啊。忽闪忽闪的小有点炫酷的感觉。于是准备研究一下。木有想到的是居然可以用CAGradientLayer和一个小小的动画就可以实现这个效果。“滑动解锁”的效果:

分析动画。

图片 1效果图

二、CAShapeLayer属性介绍

图片 2

这个动画很简单,一个圆圈,一个slider,滑动slider圆圈会变,并且有spring的效果。

功能实现不难,就是刚开始的时候思路走了些弯路,刚开始我是打算先把动画的蓝色圆环先添加在界面上,然后通过改变该圆环的layer的strokestart和strokeend的数值来达到目标效果的,我首先设置了两个float类型的start和end属性,其中start默认0.25,end默认0.75,每秒钟start和end默认增加0.1,但是当end的值大于1后,为了使strokeend有效,我设置当end大于1时。end-1,然而并没用,甚至干脆动画都没了。然后我想过当end大于1后,交换start和end的值,发现圆环会突然变成反向的,所以我就放弃了使用strokestart和strokeend达到效果的方法。

1、[CAShapeLayer layer].path

图片 3path

@property CGPathRef path;

官方的解释是定义要呈现的形状的路径。如果路径扩展到层边界之外,只有当正常层屏蔽规则导致该条线时,它才会自动被剪辑到该层。赋值时,路径被复制。默认为null。(注意,虽然路径属性的动画,不隐将动画时创建的属性发生了变化。)

 

CircleView.m

然后我就用了这个方法,达到了最终的效果。

2、[CAShapeLayer layer].fillColor

图片 4fillColor

@property CGColorRef fillColor;

官方解释是填充路径的颜色,或不需要填充。默认颜色为不透明的黑色。

当然啦,首先你需要显示出这个“滑动解锁”的文本。这里咱们就用一个简单的UILabel来解决这个问题。

-initWithFrame:frame方法中首先进行断言:一个圆的高和宽一定是相同的。接着添加circleLayer,来到addCircleLayer方法。

图片 5主要代码

3、[CAShapeLayer layer].fillRule

图片 6fillRule

@property NSString *fillRule;

官方解释是当在填充颜色的时候则就需要这种填充规则,值有两种,非零和奇偶数,但默认是非零值。

    var textExampleLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        textExampleLabel = UILabel(frame: CGRectMake(10, 100, UIScreen.mainScreen().bounds.size.width - 20, 30))
        textExampleLabel.text = "slide to unlock your phone, slide to unlock your"
        self.view.addSubview(textExampleLabel)
    }
- addCircleLayer{ CGFloat lineWidth = 4.f; CGFloat radius = CGRectGetWidth(self.bounds)/2 - lineWidth/2; self.circleLayer = [CAShapeLayer layer]; CGRect rect = CGRectMake(lineWidth/2, lineWidth/2, radius * 2, radius * 2); self.circleLayer.path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius].CGPath; self.circleLayer.strokeColor = self.tintColor.CGColor; self.circleLayer.fillColor = nil; self.circleLayer.lineWidth = lineWidth; self.circleLayer.lineCap = kCALineCapRound; self.circleLayer.lineJoin = kCALineJoinRound; [self.layer addSublayer:self.circleLayer];}

if判断语句是为了看看在添加新的layer之前有没有旧的蓝色圆环,有的话就删除,然后添加,如果不加这个判断的话,就会导致圆环逐渐形成整环。另外使用insertSublayer是为了让蓝色圆环加在最底层从而达到滑过下面的绿色圆圈是不要遮盖住它。

4、[CAShapeLayer layer].strokeColor

图片 7strokeColor

@property CGColorRef strokeColor;

官方解释是设置描边色,默认无色。

label作为成员变量(属性),在viewDidLoad方法中初始化并赋给“滑动解锁”的文本。

我们首先得到一个CAShapeLayer,通过设置其path属性来画一个圆。

demo地址github.com/hmj0930/QQLogin.git

5、[CAShapeLayer layer].strokeStart与[CAShapeLayer layer].strokeEnd

图片 8strokeStart与strokeEnd

@property CGFloat strokeStart;@property CGFloat strokeEnd;

官方解释是这两个值被定义用于绘制边线轮廓路径的子区域。该值必须在[0,1]范围,0代表路径的开始,1代表路径的结束。在0和1之间的值沿路径长度进行线性插值。strokestart默认为0,strokeend默认为1。

之后就用Gradient Layer来mask这段文本。来看看怎么准备这mask。要使用CALayer这个东东,千万不能少得就是前提条件需要引入QuartzCore

path属性是通过UIBezierPath来设置的,注意其类型是CGPathRef

6、[CAShapeLayer layer]. lineWidth与[CAShapeLayer layer].miterLimit

图片 9lineWidth与miterLimit

@property CGFloat lineWidth;@property CGFloat miterLimit;

官方解释是lineWidth为线的宽度,默认为1;miterLimit为最大斜接长度。斜接长度指的是在两条线交汇处和外交之间的距离。只有lineJoin属性为kCALineJoinMiter时miterLimit才有效。边角的角度越小,斜接长度就会越大。为了避免斜接长度过长,我们可以使用miterLimit属性。如果斜接长度超过miterLimit的值,边角会以lineJoin的“bevel”即kCALineJoinBevel类型来显示。

import QuartzCore

接着设置shape layer的strokeColor, lineWidth等属性来画出我们想要的圆。

7、[CAShapeLayer layer]. lineCap与[CAShapeLayer layer].lineJoin

图片 10lineCap与lineJoin

@property NSString *lineCap;@property NSString *lineJoin;

lineCap为线端点类型,值有三个类型,分别为kCALineCapButt 、kCALineCapRound 、kCALineCapSquare,默认值为Butt;lineJoin为线连接类型,其值也有三个类型,分别为kCALineJoinMiter、kCALineJoinRound、kCALineJoinBevel,默认值是Miter。

之后为了迎接随后到来的gradient mask,需要重构一部分代码。让整个背景的颜色都为黑色,让label的文字为白色。这样看起来这个解锁的动画效果在强烈的黑白对比下更加明显。重构之后的代码:

接着到CircleViewController.m文件中。

8、[CAShapeLayer layer]. lineDashPhase与[CAShapeLayer layer]. lineDashPattern

图片 11lineDashPhase与lineDashPattern

@property CGFloat lineDashPhase;@property(nullable, copy) NSArray<NSNumber *> *lineDashPattern;

官方解释是lineDashPhase为线型模版的起始位置;lineDashPattern为线性模版,这是一个NSNumber的数组,索引从1开始记,奇数位数值表示实线长度,偶数位数值表示空白长度。注:fillColor与strokeColor都是在有UIBezierPath参数配置的情况下才能发生作用

        self.view.backgroundColor = UIColor.blackColor()

        textExampleLabel = UILabel(frame: CGRectMake(10, 100, UIScreen.mainScreen().bounds.size.width - 20, 30))
        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)

首先添加circleView,即我们刚才分析的对象。设置其宽度高度都为200,中点为屏幕的中点。

三、开始绘制

这些都是在viewDidLoad方法中的。

此时运行程序,应该会有一个完整的圆环。

1、颜色说明---矩形为例

图片 12颜色说明

CAShapeLayer *layer = [CAShapeLayer layer];layer.frame = CGRectMake(375/2-50, 667/2-50, 100, 100);//设置背景色layer.backgroundColor = [UIColor cyanColor].CGColor;//设置描边色layer.strokeColor = [UIColor blackColor].CGColor;//设置填充色layer.fillColor = [UIColor redColor].CGColor;[self.view.layer addSublayer:layer];

效果如图显示一个青色的矩形图:

图片 13绘图1读者估计会纳闷,为啥设置的描边色与填充色没有作用,这是因为这两种颜色需要UIBezierPath来绘制路径,然后使用CAShapeLayer进行渲染,所以接下来就就在上面的那段代码下简单的添加一个UIBezierPath类路径(温馨提示,layer的背景与这两种颜色最好不要共用)。

UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 100, 100)];layer.path = path.CGPath;

图片 14绘图2

可以发现填充色与描边色有效果了。

下面开始添加mask:

接下来我们需要添加一个slider。

2、绘制shape
//绘制矩形UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 100, 100)];//绘制圆形路径UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 100, 100)];//绘制自带圆角的路径UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 100, 100) cornerRadius:30];//指定矩形某一个角加圆角UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 100, 100) byRoundingCorners:UIRectCornerTopLeft cornerRadii:CGSizeMake];

图片 15绘图3

        var gradientMask = CAGradientLayer()
        let colors: Array<AnyObject> = [UIColor.blackColor().CGColor, UIColor.whiteColor().CGColor, UIColor.blackColor().CGColor]
        gradientMask.colors = colors


        textExampleLabel.layer.mask = gradientMask
- addSlider{ UISlider *slider = [UISlider new]; slider.value = 0.7f; slider.tintColor = [UIColor customBlueColor]; slider.translatesAutoresizingMaskIntoConstraints = NO; [slider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged]; [self.view addSubview:slider]; NSDictionary *views = NSDictionaryOfVariableBindings(slider, _circleView); [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_circleView]--[slider]" options:0 metrics:nil views:views]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[slider]-|" options:0 metrics:nil views:views]]; [self.circleView setStrokeEnd:slider.value animated:NO];}
3、绘制曲线

先来两张效果图:

图片 16绘图4图片 17绘图5

说明:图4只是比图5多了填充色而已。

- viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. UIBezierPath *path = [self startPoint:CGPointMake endPoint:CGPointMake controlPoint:CGPointMake]; UIBezierPath *path1 = [self startPoint:CGPointMake endPoint:CGPointMake controlPoint:CGPointMake]; CAShapeLayer *layer = [self createShapeLayer:[UIColor orangeColor]]; layer.path = path.CGPath; CAShapeLayer *layer1 = [self createShapeLayer:[UIColor greenColor]]; layer1.path = path1.CGPath;}/** 配置贝塞尔曲线 @param startPoint 起始点 @param endPoint 结束点 @param controlPoint 控制点 @return UIBezierPath对象 */- (UIBezierPath *)startPoint:startPoint endPoint:endPoint controlPoint:controlPoint{ UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:startPoint]; [path addQuadCurveToPoint:endPoint controlPoint:controlPoint]; return path;}- (CAShapeLayer *)createShapeLayer:(UIColor *)color{ CAShapeLayer *layer = [CAShapeLayer layer]; // layer.frame = CGRectMake(0, 0, 50, 50); //设置背景色 // layer.backgroundColor = [UIColor cyanColor].CGColor; //设置描边色 layer.strokeColor = [UIColor blackColor].CGColor; //设置填充色 layer.fillColor = color.CGColor; [self.view.layer addSublayer:layer]; return layer;}

运行出来之后,你会吓一跳。因为,全部都是黑得了。。。

创建一个slider,设置其初始值为0.7f(这就是为什么刚上来圆环并不完整),注意这里并没有在刚开始创建slider的时候设置其frame,center之类的属性,而是通过addConstraints:方法来限定slider的位置。

4、曲线动画

这是因为,mask属性需要用到的是颜色的透明部分。也就是说给定的变化的颜色需要出现一部分透明或者半透明的颜色,这样的gradient layer作为另外一个layer的mask才能发挥作用。另外需要千万注意的一个问题是,上面的gradientMask还需要有一个frame。一般这个值是mask属性所在view的bounds。不要错用了frame,那样gradientMask就mask到错误的地方了。那么补全代码之后,如下:

首先需要设置slider.translatesAutoresizingMaskIntoConstraints = NO,意思是说我们不使用autoresizingMask而是通过addConstraints。

1)、直线

图片 18直线动画

UIBezierPath *path = [UIBezierPath bezierPath];[path moveToPoint:CGPointMake(50, 667/2)];[path addLineToPoint:CGPointMake(375/2, 667/2)];[path addLineToPoint:CGPointMake(300, 667/2)]; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];//每次动画的持续时间animation.duration = 5;//动画起始位置animation.fromValue = @;//动画结束位置animation.toValue = @;//动画重复次数animation.repeatCount = 100; CAShapeLayer *layer = [self createShapeLayerNoFrame:[UIColor orangeColor]];layer.path = path.CGPath;layer.lineWidth = 2.0;//设置图形的弧度// layer.strokeStart = 0;// layer.strokeEnd = 0; [layer addAnimation:animation forKey:@"strokeEndAnimation"];//注:由于UIBezierPath已经设置路径,所以动画的路径就不需要再次设置,只需要设置起始0与结束1就行,有需要可以设置动画结束后是否需要返回原位置。
        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, 0.5)
        testGradient.endPoint = CGPointMake(1, 0.5)
        testGradient.locations = [0, 0.15, 0.3]
NSDictionary *views = NSDictionaryOfVariableBindings(slider, _circleView);[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_circleView]--[slider]" options:0 metrics:nil views:views]];[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[slider]-|" options:0 metrics:nil views:views]];
2)、曲线

图片 19曲线动画.gif

UIBezierPath *path = [UIBezierPath bezierPath];//起始点[path moveToPoint:CGPointMake(50, 667/2)];//结束点、两个控制点[path addCurveToPoint:CGPointMake(330, 667/2) controlPoint1:CGPointMake controlPoint2:CGPointMake]; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];animation.duration = 5;animation.fromValue = @;animation.toValue = @;animation.repeatCount = 100; CAShapeLayer *layer = [self createShapeLayerNoFrame:[UIColor clearColor]];layer.path = path.CGPath;layer.lineWidth = 2.0;[layer addAnimation:animation forKey:@"strokeEndAnimation"];

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的终点。

看代码可以知道我们加了两个constraints:一个是水平方向的,一个是竖直方向的

3)、圆形

笔者目前的一个项目中就用到了这个功能。直接贴一张效果图:

图片 20圆形动画.gif

其实实现效果很简单,只是把边线加粗了然后实现动画就可以了,还有一种思路就是画两个圆,截取中间的环,对大圆进行颜色渐变填充,小圆clear所有颜色再去实现动画也能达到这样的效果。

UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(375/2-100, 667/2-100, 200, 200)]; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];animation.duration = 3.0;animation.fromValue = @;animation.toValue = @;animation.repeatCount = 100; CAShapeLayer *layer = [self createShapeLayerNoFrame:[UIColor clearColor]];layer.path = path.CGPath;layer.lineWidth = 25.0;//圆的起始位置,默认为0layer.strokeStart = 0;//圆的结束位置,默认为1,如果值为0.75,则显示3/4的圆layer.strokeEnd = 1;[layer addAnimation:animation forKey:@"strokeEndAnimation"];

图片 21

在竖直方向上,我们竖直排列circleView和slider,其间距为40。在水平方向上,我们将slider放在一行上,两边距离边界的长度默认

四、小结

iOS中画图类还有CoreGraphics,但笔者比较喜欢使用CAShapeLayer,且CAShapeLayer一般是与UIBezierPath连用的,如果有动画功能的话,还可以加上CABasicAnimation。这篇文章只是对CAShapeLayerUIBezierPath类如何使用和主要功能实现做了简要的说明,还有一些项目中可能经常用到的圆形进度圈、下载圈,或者某些特效的实现,笔者可能在下一篇文章中简析封装步骤与代码实现,有需要的读者多多关注。

这里可以清楚的看到颜色的变化是从UIColor(white: 1.0, alpha: 0.3).CGColor一直到白色然后剩下的都是UIColor(white: 1.0, alpha: 0.3).CGColor

而NSDictionaryOfVariableBingdings()的作用在于,说明@"V:[_circleView]--[slider]"和@"H:|-[slider]-|"中,_circleView和slider所代表的对象是哪个。相当于:

五、笔者V587

笔者已入坑,文章有不妥之处请大家指出,谢谢各位。劳动不易,转载须注明出处。

到这里,就应该让我们的这个效果动起来了。不动的怎么能试动画呢?!

@"_circleView":_circleView (或者self.circleView)

先补上刚刚漏掉的一句:

@"slider":slider

self.textExampleLabel.layer.mask = testGradient

现在运行程序,发现圆环已经不完整,现在我们需要监听slider:改变slider的值,圆环也会跟着改变。

添加动画的方式就比较简单了。用得就是远古的core animation:CABasicAnimation。这个animation是作用在gradient layer上得locations属性的。所以这个animation的初始化应该是这样的:

在addSlider方法中,

var testAnimation = CABasicAnimation(keyPath: "locations")
[slider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];

然后,这个animation就是从一个locations到另外的一个locations。就像我们看到的iphone的解锁画面一样,高亮一部分的文字,从头到尾,一次又一次的重复。一直到屏幕变暗为止。

接着sliderChanged:方法:

        var testAnimation = CABasicAnimation(keyPath: "locations")
        testAnimation.fromValue = [0, 0.15, 0.3]
        testAnimation.toValue = [1 - 0.3, 1 - 0.15, 1.0];
        testAnimation.repeatCount = 10000
        testAnimation.duration = 0.3
        testAnimation.delegate = self
[self.circleView setStrokeEnd:slider.value animated:YES];

我们这里,设定的颜色渐变是从0到0.15,然后到0.3。这是开始,那么最后的应该是什么样呢,就是长度为0.3的长度上又三个颜色,所以是从1.0

回到CircleView.m中,找到setStrokeEnd:animated:方法。

  • 0.3,然后到1.0 - 0.15最后到1.0。中间所缺少的由动画自动补上。
- setStrokeEnd:strokeEnd animated:animated{ if  { [self animateToStrokeEnd:strokeEnd]; return; } self.circleLayer.strokeEnd = strokeEnd;}

好的,让动画起作用:

可以看到,如果animated的话,则设置spring动画,并返回;如果!animated的话,则直接设置shape layer的strokeEnd的值。这也就对应了两种不同的情况:应用刚进入的时候直接设置strokeEnd的值使圆环处于0.7处;拖动slider,每次变化的时候将根据当前的value变化圆环。

testGradient.addAnimation(testAnimation, forKey: "TEST")
- animateToStrokeEnd:strokeEnd{ POPSpringAnimation *strokeAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPShapeLayerStrokeEnd]; strokeAnimation.toValue = @(strokeEnd); strokeAnimation.springBounciness = 12.f; strokeAnimation.removedOnCompletion = NO; [self.circleLayer pop_addAnimation:strokeAnimation forKey:@"layerStrokeAnimation"];}

只需要给layer添加刚刚初始化并配置好的动画之后。layer的动画就会开始运作。至于forKey的值可以很随意,什么都不给也可以。运行代码你就会看到这个动画的效果了。

这个方法很简单,给shape layer的strokeEnd属性加上动画效果。(strokeEnd可以理解为所占的比例?)

但是,如果你仔细观察这个看似很完美的动画,就会发现。使用白色高亮的文字效果没有作用在最开始的几个字母上。而在最后,这个白色的高亮效果也没有出现在最后的几个文字上。所以,这个时候就会用到我们前面讲到的layer的坐标系了。start point和end

将strokeAnimation.removedOnCompletion = NO;这一行注释似乎也不影响运行结果,不知这一行的目的?

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(1   0.3, 0.5)
    

这时,在运行这个动画。嗯,一切都完美了。。。

但是。。。又是但是,如果在app中有很多的地方都出现这个效果呢?难道我们要用最简单,最直接的方法来复用这段代码么?这是非常初级和非常可耻的行为,也会给自己埋下定时炸弹。如果需要修改渐变颜色等情况出现,你又忘记修改某一处的代码的时候。。。

我们要重构这段代码,这样在任何的地方使用这个效果的时候可以直接使用我们重构出来的功能代码非常简单的实现这个效果。

添加一个新的swift文件。在这个文件中抽象出我们的“滑动解锁”动画功能:

图片 22

这里的新的类是继承自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 = 10000000   // 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> = [0, 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(1   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(10, 100, UIScreen.mainScreen().bounds.size.width - 20, 30))
        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(10, 100, UIScreen.mainScreen().bounds.size.width - 20, 30))
        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.
    }
}

全文完!

参考:

 

结束。

本文由星彩网app下载发布于计算机编程,转载请注明出处:iOS开荒之模仿Mac版QQ登入动画,构建滑动解锁文字

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。