325 lines
10 KiB
Mathematica
325 lines
10 KiB
Mathematica
|
|
//
|
|||
|
|
// CKShimmerLabel.m
|
|||
|
|
// CKShimmerLabel
|
|||
|
|
//
|
|||
|
|
// Created by caokun on 16/8/16.
|
|||
|
|
// Copyright © 2016年 caokun. All rights reserved.
|
|||
|
|
//
|
|||
|
|
|
|||
|
|
#import "CKShimmerLabel.h"
|
|||
|
|
|
|||
|
|
@interface CKShimmerLabel ()
|
|||
|
|
|
|||
|
|
|
|||
|
|
@property (strong, nonatomic) UILabel *maskLabel;
|
|||
|
|
@property (strong, nonatomic) CAGradientLayer *maskLayer;
|
|||
|
|
@property (assign, nonatomic) BOOL isPlaying; // 正在播放动画
|
|||
|
|
@property (assign, nonatomic) CGSize charSize; // 文字 size
|
|||
|
|
@property (assign, nonatomic) CATransform3D startT, endT; // 高亮移动范围 [startT, endT]
|
|||
|
|
@property (strong, nonatomic) CABasicAnimation *translate; // 位移动画
|
|||
|
|
@property (strong, nonatomic) CABasicAnimation *alphaAni; // alpha 动画
|
|||
|
|
|
|||
|
|
@end
|
|||
|
|
|
|||
|
|
@implementation CKShimmerLabel
|
|||
|
|
|
|||
|
|
- (instancetype)init {
|
|||
|
|
if (self = [super init]) {
|
|||
|
|
self.frame = CGRectMake(0, 0, 60, 30);
|
|||
|
|
[self myInit];
|
|||
|
|
}
|
|||
|
|
return self;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (instancetype)initWithFrame:(CGRect)frame {
|
|||
|
|
if (self = [super initWithFrame:frame]) {
|
|||
|
|
[self myInit];
|
|||
|
|
}
|
|||
|
|
return self;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
|||
|
|
if (self = [super initWithCoder:aDecoder]) {
|
|||
|
|
[self myInit];
|
|||
|
|
}
|
|||
|
|
return self;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)myInit {
|
|||
|
|
[self addSubview:self.contentLabel];
|
|||
|
|
[self addSubview:self.maskLabel];
|
|||
|
|
self.layer.masksToBounds = true;
|
|||
|
|
self.isPlaying = false;
|
|||
|
|
self.startT = CATransform3DIdentity;
|
|||
|
|
self.endT = CATransform3DIdentity;
|
|||
|
|
self.charSize = CGSizeMake(0, 0);
|
|||
|
|
self.shimmerType = ST_LeftToRight;
|
|||
|
|
self.repeat = true;
|
|||
|
|
self.shimmerWidth = 20;
|
|||
|
|
self.shimmerRadius = 20;
|
|||
|
|
self.shimmerColor = [UIColor whiteColor];
|
|||
|
|
self.durationTime = 2;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)dealloc {
|
|||
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)layoutSubviews {
|
|||
|
|
[super layoutSubviews];
|
|||
|
|
|
|||
|
|
// 刷新布局
|
|||
|
|
self.contentLabel.frame = self.bounds;
|
|||
|
|
self.maskLabel.frame = self.bounds;
|
|||
|
|
self.maskLayer.frame = CGRectMake(0, 0, self.charSize.width, self.charSize.height);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#pragma mark - 属性 set, get 方法
|
|||
|
|
|
|||
|
|
- (UILabel *)contentLabel {
|
|||
|
|
if (_contentLabel == nil) {
|
|||
|
|
_contentLabel = [[UILabel alloc] initWithFrame:self.bounds];
|
|||
|
|
_contentLabel.font = [UIFont systemFontOfSize:17];
|
|||
|
|
_contentLabel.textColor = [UIColor darkGrayColor];
|
|||
|
|
}
|
|||
|
|
return _contentLabel;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (UILabel *)maskLabel {
|
|||
|
|
if (_maskLabel == nil) {
|
|||
|
|
_maskLabel = [[UILabel alloc] initWithFrame:self.bounds];
|
|||
|
|
_maskLabel.font = [UIFont systemFontOfSize:17];
|
|||
|
|
_maskLabel.textColor = [UIColor darkGrayColor];
|
|||
|
|
_maskLabel.hidden = true;
|
|||
|
|
}
|
|||
|
|
return _maskLabel;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (CALayer *)maskLayer {
|
|||
|
|
if (_maskLayer == nil) {
|
|||
|
|
_maskLayer = [[CAGradientLayer alloc] init];
|
|||
|
|
_maskLayer.backgroundColor = [UIColor clearColor].CGColor;
|
|||
|
|
[self freshMaskLayer];
|
|||
|
|
}
|
|||
|
|
return _maskLayer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setText:(NSString *)text {
|
|||
|
|
if (_text == text) return ;
|
|||
|
|
|
|||
|
|
_text = text;
|
|||
|
|
self.contentLabel.text = text;
|
|||
|
|
self.charSize = [self.contentLabel.text boundingRectWithSize:self.contentLabel.frame.size options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : self.contentLabel.font} context:nil].size;
|
|||
|
|
[self update];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setFont:(UIFont *)font {
|
|||
|
|
if (_font == font) return ;
|
|||
|
|
|
|||
|
|
_font = font;
|
|||
|
|
self.contentLabel.font = font;
|
|||
|
|
self.charSize = [self.contentLabel.text boundingRectWithSize:self.contentLabel.frame.size options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : self.contentLabel.font} context:nil].size;
|
|||
|
|
[self update];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setTextColor:(UIColor *)textColor {
|
|||
|
|
if (_textColor == textColor) return ;
|
|||
|
|
|
|||
|
|
_textColor = textColor;
|
|||
|
|
self.contentLabel.textColor = textColor;
|
|||
|
|
[self update];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setAttributedText:(NSAttributedString *)attributedText {
|
|||
|
|
if (_attributedText == attributedText) return ;
|
|||
|
|
|
|||
|
|
_attributedText = attributedText;
|
|||
|
|
self.contentLabel.attributedText = attributedText;
|
|||
|
|
[self update];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setNumberOfLines:(NSInteger)numberOfLines {
|
|||
|
|
if (_numberOfLines == numberOfLines) return ;
|
|||
|
|
|
|||
|
|
_numberOfLines = numberOfLines;
|
|||
|
|
self.contentLabel.numberOfLines = numberOfLines;
|
|||
|
|
[self update];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setShimmerType:(ShimmerType)shimmerType {
|
|||
|
|
if (_shimmerType == shimmerType) return ;
|
|||
|
|
|
|||
|
|
_shimmerType = shimmerType;
|
|||
|
|
[self update];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setRepeat:(BOOL)repeat {
|
|||
|
|
if (_repeat == repeat) return ;
|
|||
|
|
|
|||
|
|
_repeat = repeat;
|
|||
|
|
[self update];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setShimmerWidth:(CGFloat)shimmerWidth {
|
|||
|
|
if (_shimmerWidth == shimmerWidth) return ;
|
|||
|
|
|
|||
|
|
_shimmerWidth = shimmerWidth;
|
|||
|
|
[self update];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setShimmerRadius:(CGFloat)shimmerRadius {
|
|||
|
|
if (_shimmerRadius == shimmerRadius) return ;
|
|||
|
|
|
|||
|
|
_shimmerRadius = shimmerRadius;
|
|||
|
|
[self update];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setShimmerColor:(UIColor *)shimmerColor {
|
|||
|
|
if (_shimmerColor == shimmerColor) return ;
|
|||
|
|
|
|||
|
|
_shimmerColor = shimmerColor;
|
|||
|
|
self.maskLabel.textColor = shimmerColor;
|
|||
|
|
[self update];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setDurationTime:(NSTimeInterval)durationTime {
|
|||
|
|
if (_durationTime == durationTime) return ;
|
|||
|
|
|
|||
|
|
_durationTime = durationTime;
|
|||
|
|
[self update];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)update {
|
|||
|
|
if (self.isPlaying) { // 如果在播放动画,更新动画
|
|||
|
|
[self stopShimmer];
|
|||
|
|
[self startShimmer];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 刷新 maskLayer 属性值, transform 值
|
|||
|
|
- (void)freshMaskLayer {
|
|||
|
|
if (self.shimmerType != ST_ShimmerAll) {
|
|||
|
|
_maskLayer.backgroundColor = [UIColor clearColor].CGColor;
|
|||
|
|
_maskLayer.startPoint = CGPointMake(0, 0.5);
|
|||
|
|
_maskLayer.endPoint = CGPointMake(1, 0.5);
|
|||
|
|
_maskLayer.colors = @[(id)[UIColor clearColor].CGColor, (id)[UIColor clearColor].CGColor, (id)[UIColor whiteColor].CGColor, (id)[UIColor whiteColor].CGColor, (id)[UIColor clearColor].CGColor, (id)[UIColor clearColor].CGColor];
|
|||
|
|
|
|||
|
|
CGFloat w = 1.0;
|
|||
|
|
CGFloat sw = 1.0;
|
|||
|
|
if (self.charSize.width >= 1) {
|
|||
|
|
w = self.shimmerWidth / self.charSize.width * 0.5;
|
|||
|
|
sw = self.shimmerRadius / self.charSize.width;
|
|||
|
|
}
|
|||
|
|
_maskLayer.locations = @[@(0.0), @(0.5 - w - sw), @(0.5 - w), @(0.5 + w), @(0.5 + w + sw), @(1)];
|
|||
|
|
CGFloat startX = self.charSize.width * (0.5 - w - sw);
|
|||
|
|
CGFloat endX = self.charSize.width * (0.5 + w + sw);
|
|||
|
|
self.startT = CATransform3DMakeTranslation(-endX, 0, 1);
|
|||
|
|
self.endT = CATransform3DMakeTranslation(self.charSize.width - startX, 0, 1);
|
|||
|
|
} else {
|
|||
|
|
_maskLayer.backgroundColor = self.shimmerColor.CGColor;
|
|||
|
|
_maskLayer.colors = nil;
|
|||
|
|
_maskLayer.locations = nil;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#pragma mark - 其他方法
|
|||
|
|
|
|||
|
|
- (void)copyLabel:(UILabel *)dLabel from:(UILabel *)sLabel {
|
|||
|
|
dLabel.attributedText = self.attributedText;
|
|||
|
|
dLabel.text = self.text;
|
|||
|
|
dLabel.font = self.font;
|
|||
|
|
dLabel.numberOfLines = self.numberOfLines;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (CABasicAnimation *)translate {
|
|||
|
|
if (_translate == nil) {
|
|||
|
|
_translate = [CABasicAnimation animationWithKeyPath:@"transform"];
|
|||
|
|
}
|
|||
|
|
_translate.removedOnCompletion = NO;
|
|||
|
|
_translate.duration = self.durationTime;
|
|||
|
|
_translate.repeatCount = self.repeat == true ? MAXFLOAT : 0;
|
|||
|
|
_translate.autoreverses = self.shimmerType == ST_AutoReverse ? true : false;
|
|||
|
|
|
|||
|
|
return _translate;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (CABasicAnimation *)alphaAni {
|
|||
|
|
if (_alphaAni == nil) {
|
|||
|
|
_alphaAni = [CABasicAnimation animationWithKeyPath:@"opacity"];
|
|||
|
|
_alphaAni.repeatCount = MAXFLOAT;
|
|||
|
|
_alphaAni.autoreverses = true;
|
|||
|
|
_alphaAni.removedOnCompletion = NO;
|
|||
|
|
_alphaAni.fromValue = @(0.0);
|
|||
|
|
_alphaAni.toValue = @(1.0);
|
|||
|
|
}
|
|||
|
|
_alphaAni.duration = self.durationTime;
|
|||
|
|
|
|||
|
|
return _alphaAni;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)startShimmer {
|
|||
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|||
|
|
// 切换到主线程串行队列,下面代码打包成一个事件(原子操作),加到runloop,就不用担心 isPlaying 被多个线程同时修改
|
|||
|
|
// dispatch_async() 不 strong 持有本 block,也不用担心循环引用
|
|||
|
|
if (self.isPlaying == true) return ;
|
|||
|
|
self.isPlaying = true;
|
|||
|
|
|
|||
|
|
[self copyLabel:self.maskLabel from:self.contentLabel];
|
|||
|
|
self.maskLabel.hidden = false;
|
|||
|
|
|
|||
|
|
// [self.layer addSublayer:self.maskLayer];
|
|||
|
|
[self.maskLayer removeFromSuperlayer];
|
|||
|
|
[self freshMaskLayer];
|
|||
|
|
[self.maskLabel.layer addSublayer:self.maskLayer];
|
|||
|
|
self.maskLabel.layer.mask = self.maskLayer;
|
|||
|
|
|
|||
|
|
switch (self.shimmerType) {
|
|||
|
|
case ST_LeftToRight: {
|
|||
|
|
self.maskLayer.transform = self.startT;
|
|||
|
|
self.translate.fromValue = [NSValue valueWithCATransform3D:self.startT];
|
|||
|
|
self.translate.toValue = [NSValue valueWithCATransform3D:self.endT];
|
|||
|
|
[self.maskLayer removeAllAnimations];
|
|||
|
|
[self.maskLayer addAnimation:self.translate forKey:@"start"];
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case ST_RightToLeft: {
|
|||
|
|
self.maskLayer.transform = self.endT;
|
|||
|
|
self.translate.fromValue = [NSValue valueWithCATransform3D:self.endT];
|
|||
|
|
self.translate.toValue = [NSValue valueWithCATransform3D:self.startT];
|
|||
|
|
[self.maskLayer removeAllAnimations];
|
|||
|
|
[self.maskLayer addAnimation:self.translate forKey:@"start"];
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case ST_AutoReverse : {
|
|||
|
|
self.maskLayer.transform = self.startT;
|
|||
|
|
self.translate.fromValue = [NSValue valueWithCATransform3D:self.startT];
|
|||
|
|
self.translate.toValue = [NSValue valueWithCATransform3D:self.endT];
|
|||
|
|
[self.maskLayer removeAllAnimations];
|
|||
|
|
[self.maskLayer addAnimation:self.translate forKey:@"start"];
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
case ST_ShimmerAll : {
|
|||
|
|
self.maskLayer.transform = CATransform3DIdentity;
|
|||
|
|
[self.maskLayer removeAllAnimations];
|
|||
|
|
[self.maskLayer addAnimation:self.alphaAni forKey:@"start"];
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
default: break;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)stopShimmer {
|
|||
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|||
|
|
if (self.isPlaying == false) return ;
|
|||
|
|
self.isPlaying = false;
|
|||
|
|
|
|||
|
|
[self.maskLayer removeAllAnimations];
|
|||
|
|
[self.maskLayer removeFromSuperlayer];
|
|||
|
|
self.maskLabel.hidden = true;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@end
|