Files
fanyin-ios/QXLive/Tools/CKShimmerLabel.m
2025-08-12 14:27:12 +08:00

325 lines
10 KiB
Objective-C
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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