跑馬燈Label

跑馬燈Label網上有不少個作法,其實基本原理很簡單,作法也不少樣,能夠是一個scrollView上面放兩個label,而後用定時器定時執行動畫;也有的是用定時器,在短暫的間隔時間內執行把文字drawInRect方法。性能

最開始我是用上面說的第一種方法,可是發現每次定時器執行完一次動畫,下一次動畫執行之間,總會有個很短暫但很明顯感受到的停頓,這和我想要的效果不一樣;而執行文字drawInRect方法是比較耗cpu的,第二個方法對性能消耗太大了;既然最終都是執行動畫,那麼爲何不讓動畫本身來負責本身的開始和結束和循環,而讓定時器都不用參與?字體


個人作法裏主要用了CATextLayer和CABasicAnimation,CATextLayer是用來繪製文本,CABasicAnimation用來執行動畫,CATextLayer和UILabel很類似,在iOS6以前,UILabel是經過Webkit來實現繪製的,因此當不少文字的時候性能方面就有壓力,而CATextLayer是使用了CoreText,渲染很是快。動畫

CATextLayer有個string的屬性,這個屬性能夠是NSString也能夠是NSAttributedString,但當是NSAttributedString的時候,相應的像font屬性之類的屬性就沒有效果。atom

我在使用CATextLayer的時候,碰上了一點麻煩,就是字體,CATextLayer使用的不是UIFont,而是CTFontRef和CGFontRef或者給字體的名稱,這三個方法我都試過,但在最終效果上對比一樣文本和字體的UILabel,計算出來的寬度老是有小誤差,因此最終使用了string屬性是NSAttributedString對象(這裏我的猜想,可能即便string屬性是NSString對象,最終也是轉換爲NSAttributedString來使用)。spa


下面是個人作法code

@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屬性,那麼其餘屬性設置就無需了。animation

相關文章
相關標籤/搜索