地址放在最前面:
写在前面
最近在使用一些app时 , 发现他们点赞功能后面的数字增加或者减少会有一个动画改变的效果 , 很喜欢这样的小细节 , 于是自己动手做了一个类似demo ~~
效果图
结构<small><small><small><small> 话说图好丑啊!!!!😝(看不到,看不到..)</small></small></small></small>
代码分析
- 因为在创建scrollLayer时, 需要对新旧两个数值进行比较 , 所以setter方法中应该先设置scrollLayer , 然后再进行赋值.创建的scrollLayer会保存在数组中
//设置显示的数字
-(void)setNumberValue:(NSNumber *)numberValue {
[self configScrollLayers:numberValue];
_numberValue = numberValue;
}
- 在configScrollLayer方法中,因为我们有时候要复用这些scrollLayer , 而数字位数的复用是从低位到高位.(比如第一次数字是12 , 第二次数字是123 , 那么我们能复用的是第一次的个位和十位对应的layer).所以我这里保存layer的数组 , 存放顺序是从右到左 . 即array[0]代表的是最低位的数字(最右边的数字). 计算layer位置也是从右到左计算 , 简化逻辑
CGFloat lastX = 0;
//先根据对齐方式 , 计算最低位数字的x值
if (_alignment == NSTextAlignmentRight) {
lastX = self.frame.size.width - kAnimationNumberLabel_eachWidth - _contentEdgeInsets.right;
} else if (_alignment == NSTextAlignmentLeft) {
lastX = (kAnimationNumberLabel_eachWidth + _textMargin) * (numberValue.description.length - 1) + _contentEdgeInsets.left;
} else if (_alignment == NSTextAlignmentCenter) {
lastX = self.frame.size.width / 2.0 + (kAnimationNumberLabel_eachWidth * numberValue.description.length + _textMargin * (numberValue.description.length - 1)) / 2.0 - kAnimationNumberLabel_eachWidth;
}
- 复用layer的逻辑是先比较新旧两个数组的个数 , 如果新数组位数小于scrollLayers.count , 则需要将多余的layer从数组和页面中移除 , 保存到temLayers中 . 如果新数组位数大于scrollLayers.count , 则需要判断temLayers中是否有layer , 有的话拿来复用 , 没有的话创建新的layer
//如果之前数字的位数大于新数字的位数 , 将多余的layer移除存放如temLayers中 , 复用剩下的layer
for (NSInteger i = numberValue.description.length; i<_scrollLayers.count; i++) {
[_temLayers addObject:_scrollLayers[i]];
[_scrollLayers[i] removeFromSuperlayer];
[_scrollLayers removeObjectAtIndex:i];
i--;
}
//如果之前的数字的位数小于新数字的位数 , 添加新的layer .
for (NSInteger i = _scrollLayers.count; i<numberValue.description.length; i++) {
CGRect frame = CGRectMake(lastX - i * kAnimationNumberLabel_eachWidth, 0, kAnimationNumberLabel_eachWidth, self.bounds.size.height);
if (_temLayers.count) {
_temLayers.lastObject.frame = frame;
[_scrollLayers addObject:_temLayers.lastObject];
[self.layer addSublayer:_temLayers.lastObject];
[_temLayers removeObjectAtIndex:_temLayers.count - 1];
} else {
TextTransformationLayer *textLayer = [TextTransformationLayer layer];
textLayer.frame = frame;
[textLayer setTextArray:@[@".",@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9"] font:_font textColor:_textColor selectText:nil];
[_scrollLayers addObject:textLayer];
[self.layer addSublayer:textLayer];
}
}
- 最后对每个位数上的layer进行动画处理 , 滚动到正确的位置
//遍历layer 进行赋值和计算frame
for (NSInteger i = 0; i<_scrollLayers.count; i++) {
TextTransformationLayer *layer = _scrollLayers[i];
layer.frame = CGRectMake(lastX - i * kAnimationNumberLabel_eachWidth - _textMargin * i, 0, kAnimationNumberLabel_eachWidth, self.bounds.size.height);
//如果是0到9或者9到0 , 不进行动画展示
BOOL animated;
NSString *newStr = [numberValue.description substringWithRange:NSMakeRange(_scrollLayers.count - i - 1, 1)];
if (i<_numberValue.description.length) {
NSString *curStr = [_numberValue.description substringWithRange:NSMakeRange(_numberValue.description.length - i - 1, 1)];
animated = (abs(newStr.intValue - curStr.intValue) < 9);
} else {
animated = NO;
}
animated = (animated && layer.selectText);
[layer setSelectText:newStr animated:animated];;
}
使用方法
使用的话就很简单了 , 先创建对象 , 然后进行赋值就好了
NumberTransformationView *view = [[NumberTransformationView alloc] initWithFrame:frame font:[UIFont systemFontOfSize:18]];
view.numberValue = @200;
总结
这个功能总体来说是比较简单的 , 我在动手coding之前大致思路是这样子的
- 首先要注意每个位数上的数字都应该有动画效果 , 所以不应该对整个数字做动画 , 而应该切分成若干个模块单独进行处理
- 切分后的模块应该注意复用 , 避免每次设值都要重新创建.
- 单独来看, 每个位数上的逻辑处理是一样的 , 所以应该单独拿出来作为一个功能.我这里对应的是TextTransformationLayer . 所有逻辑和动画效果都在由这个类完成 , NumberTransformationView只是对前者功能的一个组合.
写在最后
<big>**demo在最上 , 下载时欢迎顺手赏个star. (¬_¬)
以上.
**</big>
网友评论