跑马灯Label网上有很多个做法,其实基本原理很简单,做法也很多样,可以是一个scrollView上面放两个label,然后用定时器定时执行动画;也有的是用定时器,在短暂的间隔时间内执行把文字drawInRect方法。
最开始我是用上面说的第一种方法,但是发现每次定时器执行完一次动画,下一次动画执行之间,总会有个很短暂但很明显感觉到的停顿,这和我想要的效果不同;而执行文字drawInRect方法是比较耗cpu的,第二个方法对性能消耗太大了;既然最终都是执行动画,那么为什么不让动画自己来负责自己的开始和结束和循环,而让定时器都不用参与?
我的做法里主要用了CATextLayer和CABasicAnimation,CATextLayer是用来绘制文本,CABasicAnimation用来执行动画,CATextLayer和UILabel很相似,在iOS6之前,UILabel是通过Webkit来实现绘制的,所以当很多文字的时候性能方面就有压力,而CATextLayer是使用了CoreText,渲染非常快。
CATextLayer有个string的属性,这个属性可以是NSString也可以是NSAttributedString,但当是NSAttributedString的时候,相应的像font属性之类的属性就没有效果。
我在使用CATextLayer的时候,碰上了一点麻烦,就是字体,CATextLayer使用的不是UIFont,而是CTFontRef和CGFontRef或者给字体的名称,这三个方法我都试过,但在最终效果上对比同样文本和字体的UILabel,计算出来的宽度总是有小偏差,所以最终使用了string属性是NSAttributedString对象(这里个人猜测,可能即使string属性是NSString对象,最终也是转换为NSAttributedString来使用)。
下面是我的做法
@interface AutoScrollLabel : UIView
@property (nonatomic, copy) NSString* text;
@property (nonatomic, assign) CGFloat fontSize;
@property (nonatomic, strong) UIFont* font;
@property (nonatomic, strong) UIColor* textColor;
@property (nonatomic, assign) CGFloat speed;
@property (nonatomic, copy) NSAttributedString* attributedText;
@end
#import <CoreText/CoreText.h>
@interface AutoScrollLabel() {
CATextLayer* _firstLayer;
CATextLayer* _secondLayer;
}
@end
@implementation AutoScrollLabel
- (instancetype)init {
self = [super init];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit {
_firstLayer = [[CATextLayer alloc] init];
_firstLayer.bounds = CGRectZero;
_firstLayer.position = CGPointZero;
_firstLayer.alignmentMode = kCAAlignmentCenter;
_firstLayer.foregroundColor = [UIColor blackColor].CGColor;
_firstLayer.contentsScale = [UIScreen mainScreen].scale;
//contentsScale的指定为了适配retian屏
[self.layer addSublayer:_firstLayer];
_secondLayer = [[CATextLayer alloc] init];
_secondLayer.bounds = CGRectZero;
_secondLayer.position = CGPointZero;
_secondLayer.alignmentMode = kCAAlignmentCenter;
_secondLayer.foregroundColor = [UIColor blackColor].CGColor;
_secondLayer.contentsScale = [UIScreen mainScreen].scale;
[self.layer addSublayer:_secondLayer];
_fontSize = 15.0f;
_font = [UIFont systemFontOfSize:_fontSize];
_textColor = [UIColor blackColor];
self.layer.masksToBounds = YES;
}
- (void)setAttributedText:(NSAttributedString *)attributedText {
_attributedText = attributedText;
_firstLayer.string = attributedText;
_secondLayer.string = attributedText;
}
- (void)drawRect:(CGRect)rect {
if (_attributedText == nil) {
_font = [UIFont systemFontOfSize:_fontSize];
_attributedText = [[NSAttributedString alloc] initWithString:_text attributes:@{NSFontAttributeName:_font, NSForegroundColorAttributeName:_textColor}];
_firstLayer.string = _attributedText;
_secondLayer.string = _attributedText;
}
NSRange range = NSMakeRange(0, _attributedText.length);
CGSize textSize = [_attributedText.string boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:[_attributedText attributesAtIndex:0 effectiveRange:&range]
context:nil].size;
if (textSize.width < rect.size.width) {
textSize.width = rect.size.width;
}
// 在计算出的文本宽度后加入10的宽度作为两个文本之间的间隔
_firstLayer.bounds = CGRectMake(0, 0, textSize.width + 10, textSize.height);
_firstLayer.position = CGPointMake(textSize.width / 2 + 5, rect.size.height / 2);
_secondLayer.bounds = CGRectMake(0, 0, textSize.width + 10, textSize.height);
_secondLayer.position = CGPointMake(textSize.width + 10 + textSize.width / 2 + 5, rect.size.height / 2);
// 文本宽度超过区域宽度才有滚动动画
if (textSize.width > rect.size.width) {
if (_speed <= 0) {
_speed = textSize.width / 30;
}
CABasicAnimation* firstAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
firstAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(_firstLayer.frame.origin.x - textSize.width / 2 - 5, rect.size.height / 2)];
firstAnimation.duration = _speed;
firstAnimation.repeatCount = HUGE_VALF;
[_firstLayer addAnimation:firstAnimation forKey:@"FirstAnimation"];
CABasicAnimation* secondAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
secondAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(textSize.width / 2 + 5, rect.size.height / 2)];
secondAnimation.duration = _speed;
secondAnimation.repeatCount = HUGE_VALF;
[_secondLayer addAnimation:secondAnimation forKey:@"SecondAnimation"];
}
}
@end
用xib生成的实例也是有效
@property (weak, nonatomic) IBOutlet AutoScrollLabel *autoScrollLabel;
/只是作为分割线
_autoScrollLabel.text = @"jojo_text店铺";
_autoScrollLabel.fontSize = 17;
但跟CATextLayer一样,如果指定了attributedText属性,那么其他属性设置就无需了。