zl程序教程

您现在的位置是:首页 >  其它

当前栏目

带辉光效果的跑马灯

效果 跑马灯
2023-09-14 08:57:30 时间

带辉光效果的跑马灯

 

效果

 

说明

并没有对代码进行封装,以后会在项目 Animation(https://github.com/YouXianMing/Animations)里面进行集成,欢迎前去star。

 

源码

UIView+GlowView

//

// UIView+GlowView.h

// GlowView

// Created by YouXianMing on 15/7/4.

// Copyright (c) 2015年 YouXianMing. All rights reserved.

#import UIKit/UIKit.h 

@interface UIView (GlowView)

// == 动画时间解析 ==

// 0.0 ------------- 0.0 ------------ glowOpacity [-------------] glowOpacity ------------ 0.0

// T T T T

// | | | |

// | | | |

// . . . .

// hideDuration glowAnimationDuration glowDuration glowAnimationDuration

#pragma mark - 设置辉光效果

 * 辉光的颜色

@property (nonatomic, strong) UIColor *glowColor;

 * 辉光的透明度

@property (nonatomic, strong) NSNumber *glowOpacity;

 * 辉光的阴影半径

@property (nonatomic, strong) NSNumber *glowRadius;

#pragma mark - 设置辉光时间间隔

 * 一次完整的辉光周期(从显示到透明或者从透明到显示),默认1s

@property (nonatomic, strong) NSNumber *glowAnimationDuration;

 * 保持辉光时间(不设置,默认为0.5s)

@property (nonatomic, strong) NSNumber *glowDuration;

 * 不显示辉光的周期(不设置默认为0.5s)

@property (nonatomic, strong) NSNumber *hideDuration;

#pragma mark - 辉光相关操作

 * 创建出辉光layer

- (void)createGlowLayer;

 * 插入辉光的layer

- (void)insertGlowLayer;

 * 移除辉光的layer

- (void)removeGlowLayer;

 * 显示辉光

- (void)glowToshow;

 * 隐藏辉光

- (void)glowToHide;

 * 开始循环辉光

- (void)startGlowLoop;

UIView+GlowView.h


//

// UIView+GlowView.m

// GlowView

// Created by YouXianMing on 15/7/4.

// Copyright (c) 2015年 YouXianMing. All rights reserved.

#import "UIView+GlowView.h"

#import objc/runtime.h 

@interface UIView ()

@property (nonatomic, strong) CALayer *glowLayer;

@property (nonatomic, strong) dispatch_source_t dispatchSource;

@implementation UIView (GlowView)

- (void)createGlowLayer {

 UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);

 [self.layer renderInContext:UIGraphicsGetCurrentContext()];

 UIBezierPath* path = [UIBezierPath bezierPathWithRect:self.bounds];

 [[self accessGlowColor] setFill];

 [path fillWithBlendMode:kCGBlendModeSourceAtop alpha:1.0];

 self.glowLayer = [CALayer layer];

 self.glowLayer.frame = self.bounds;

 self.glowLayer.contents = (__bridge id)UIGraphicsGetImageFromCurrentImageContext().CGImage;

 self.glowLayer.opacity = 0.f;

 self.glowLayer.shadowOffset = CGSizeMake(0, 0);

 self.glowLayer.shadowOpacity = 1.f;

 UIGraphicsEndImageContext();

- (void)insertGlowLayer {

 if (self.glowLayer) {

 [self.layer addSublayer:self.glowLayer];

- (void)removeGlowLayer {

 if (self.glowLayer) {

 [self.glowLayer removeFromSuperlayer];

- (void)glowToshow {

 self.glowLayer.shadowColor = [self accessGlowColor].CGColor;

 self.glowLayer.shadowRadius = [self accessGlowRadius].floatValue;

 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];

 animation.fromValue = @(0.f);

 animation.toValue = [self accessGlowOpacity];

 self.glowLayer.opacity = [self accessGlowOpacity].floatValue;

 animation.duration = [self accessAnimationDuration].floatValue;

 [self.glowLayer addAnimation:animation forKey:nil];

- (void)glowToHide {

 self.glowLayer.shadowColor = [self accessGlowColor].CGColor;

 self.glowLayer.shadowRadius = [self accessGlowRadius].floatValue;

 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];

 animation.fromValue = [self accessGlowOpacity];

 animation.toValue = @(0.f);

 self.glowLayer.opacity = 0.f;

 animation.duration = [self accessAnimationDuration].floatValue;

 [self.glowLayer addAnimation:animation forKey:nil];

- (void)startGlowLoop {

 if (self.dispatchSource == nil) {

 CGFloat seconds = [self accessAnimationDuration].floatValue * 2 + [self accessGlowDuration].floatValue + [self accessHideDuration].floatValue;

 CGFloat delaySeconds = [self accessAnimationDuration].floatValue + [self accessGlowDuration].floatValue;

 self.dispatchSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());

 dispatch_source_set_timer(self.dispatchSource, dispatch_time(DISPATCH_TIME_NOW, 0), NSEC_PER_SEC * seconds, 0);

 dispatch_source_set_event_handler(self.dispatchSource, ^{

 [self glowToshow];

 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * delaySeconds), dispatch_get_main_queue(), ^{

 [self glowToHide];

 dispatch_resume(self.dispatchSource);

#pragma mark - 处理数据越界问题

- (NSNumber *)accessGlowOpacity {

 if (self.glowOpacity) {

 if (self.glowOpacity.floatValue = 0 || self.glowOpacity.floatValue 1) {

 return @(0.8);

 } else {

 return self.glowOpacity;

 } else {

 return @(0.8);

- (NSNumber *)accessGlowDuration {

 if (self.glowDuration) {

 if (self.glowDuration.floatValue = 0) {

 return @(0.5f);

 } else {

 return self.glowDuration;

 } else {

 return @(0.5f);

- (NSNumber *)accessHideDuration {

 if (self.hideDuration) {

 if (self.hideDuration.floatValue 0) {

 return @(0.5);

 } else {

 return self.hideDuration;

 } else {

 return @(0.5f);

- (NSNumber *)accessAnimationDuration {

 if (self.glowAnimationDuration) {

 if (self.glowAnimationDuration.floatValue = 0) {

 return @(1.f);

 } else {

 return self.glowAnimationDuration;

 } else {

 return @(1.f);

- (UIColor *)accessGlowColor {

 if (self.glowColor) {

 return self.glowColor;

 } else {

 return [UIColor redColor];

- (NSNumber *)accessGlowRadius {

 if (self.glowRadius) {

 if (self.glowRadius.floatValue = 0) {

 return @(2.f);

 } else {

 return self.glowRadius;

 } else {

 return @(2.f);

#pragma mark - runtime属性

NSString * const _recognizerDispatchSource = @"_recognizerDispatchSource";

- (void)setDispatchSource:(dispatch_source_t)dispatchSource {

 objc_setAssociatedObject(self, (__bridge const void *)(_recognizerDispatchSource), dispatchSource, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

- (dispatch_source_t)dispatchSource {

 return objc_getAssociatedObject(self, (__bridge const void *)(_recognizerDispatchSource));

NSString * const _recognizerGlowColor = @"_recognizerGlowColor";

- (void)setGlowColor:(UIColor *)glowColor {

 objc_setAssociatedObject(self, (__bridge const void *)(_recognizerGlowColor), glowColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

- (UIColor *)glowColor {

 return objc_getAssociatedObject(self, (__bridge const void *)(_recognizerGlowColor));

NSString * const _recognizerGlowOpacity = @"_recognizerGlowOpacity";

- (void)setGlowOpacity:(NSNumber *)glowOpacity {

 objc_setAssociatedObject(self, (__bridge const void *)(_recognizerGlowOpacity), glowOpacity, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

- (NSNumber *)glowOpacity {

 return objc_getAssociatedObject(self, (__bridge const void *)(_recognizerGlowOpacity));

NSString * const _recognizerGlowRadius = @"_recognizerGlowRadius";

- (void)setGlowRadius:(NSNumber *)glowRadius {

 objc_setAssociatedObject(self, (__bridge const void *)(_recognizerGlowRadius), glowRadius, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

- (NSNumber *)glowRadius {

 return objc_getAssociatedObject(self, (__bridge const void *)(_recognizerGlowRadius));

NSString * const _recognizerGlowAnimationDuration = @"_recognizerGlowAnimationDuration";

- (void)setGlowAnimationDuration:(NSNumber *)glowAnimationDuration {

 objc_setAssociatedObject(self, (__bridge const void *)(glowAnimationDuration), _recognizerGlowAnimationDuration, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

- (NSNumber *)glowAnimationDuration {

 return objc_getAssociatedObject(self, (__bridge const void *)(_recognizerGlowAnimationDuration));

NSString * const _recognizerGlowDuration = @"_recognizerGlowDuration";

- (void)setGlowDuration:(NSNumber *)glowDuration {

 objc_setAssociatedObject(self, (__bridge const void *)(_recognizerGlowDuration), glowDuration, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

- (NSNumber *)glowDuration {

 return objc_getAssociatedObject(self, (__bridge const void *)(_recognizerGlowDuration));

NSString * const _recognizerHideDuration = @"_recognizerHideDuration";

- (void)setHideDuration:(NSNumber *)hideDuration {

 objc_setAssociatedObject(self, (__bridge const void *)(_recognizerHideDuration), hideDuration, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

- (NSNumber *)hideDuration {

 return objc_getAssociatedObject(self, (__bridge const void *)(_recognizerHideDuration));

NSString * const _recognizerGlowLayer = @"_recognizerGlowLayer";

- (void)setGlowLayer:(CALayer *)glowLayer {

 objc_setAssociatedObject(self, (__bridge const void *)(_recognizerGlowLayer), glowLayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

- (CALayer *)glowLayer {

 return objc_getAssociatedObject(self, (__bridge const void *)(_recognizerGlowLayer));

UIView+GlowView.m

UIView+SetRect
//

// UIView+SetRect.h

// UIView

// Created by YouXianMing on 16/1/29.

// Copyright © 2016年 YouXianMing. All rights reserved.

#import UIKit/UIKit.h 

 * UIScreen width.

#define Width [UIScreen mainScreen].bounds.size.width

 * UIScreen height.

#define Height [UIScreen mainScreen].bounds.size.height

 * Status bar height.

#define StatusBarHeight 20.f

 * Navigation bar height.

#define NavigationBarHeight 44.f

 * Tabbar height.

#define TabbarHeight 49.f

 * Status bar navigation bar height.

#define StatusBarAndNavigationBarHeight (20.f + 44.f)

 * iPhone4 or iPhone4s

#define iPhone4_4s (Width == 320.f Height == 480.f ? YES : NO)

 * iPhone5 or iPhone5s

#define iPhone5_5s (Width == 320.f Height == 568.f ? YES : NO)

 * iPhone6 or iPhone6s

#define iPhone6_6s (Width == 375.f Height == 667.f ? YES : NO)

 * iPhone6Plus or iPhone6sPlus

#define iPhone6_6sPlus (Width == 414.f Height == 736.f ? YES : NO)

@interface UIView (SetRect)

/*----------------------

 * Absolute coordinate *

 ----------------------*/

@property (nonatomic) CGPoint viewOrigin;

@property (nonatomic) CGSize viewSize;

@property (nonatomic) CGFloat x;

@property (nonatomic) CGFloat y;

@property (nonatomic) CGFloat width;

@property (nonatomic) CGFloat height;

@property (nonatomic) CGFloat top;

@property (nonatomic) CGFloat bottom;

@property (nonatomic) CGFloat left;

@property (nonatomic) CGFloat right;

@property (nonatomic) CGFloat centerX;

@property (nonatomic) CGFloat centerY;

/*----------------------

 * Relative coordinate *

 ----------------------*/

@property (nonatomic, readonly) CGFloat middleX;

@property (nonatomic, readonly) CGFloat middleY;

@property (nonatomic, readonly) CGPoint middlePoint;

UIView+SetRect.h


//

// UIView+SetRect.m

// UIView

// Created by YouXianMing on 16/1/29.

// Copyright © 2016年 YouXianMing. All rights reserved.

#import "UIView+SetRect.h"

@implementation UIView (SetRect)

- (CGPoint)viewOrigin {

 return self.frame.origin;

- (void)setViewOrigin:(CGPoint)viewOrigin {

 CGRect newFrame = self.frame;

 newFrame.origin = viewOrigin;

 self.frame = newFrame;

- (CGSize)viewSize {

 return self.frame.size;

- (void)setViewSize:(CGSize)viewSize {

 CGRect newFrame = self.frame;

 newFrame.size = viewSize;

 self.frame = newFrame;

- (CGFloat)x {

 return self.frame.origin.x;

- (void)setX:(CGFloat)x {

 CGRect newFrame = self.frame;

 newFrame.origin.x = x;

 self.frame = newFrame;

- (CGFloat)y {

 return self.frame.origin.y;

- (void)setY:(CGFloat)y {

 CGRect newFrame = self.frame;

 newFrame.origin.y = y;

 self.frame = newFrame;

- (CGFloat)width {

 return CGRectGetWidth(self.bounds);

- (void)setWidth:(CGFloat)width {

 CGRect newFrame = self.frame;

 newFrame.size.width = width;

 self.frame = newFrame;

- (CGFloat)height {

 return CGRectGetHeight(self.bounds);

- (void)setHeight:(CGFloat)height {

 CGRect newFrame = self.frame;

 newFrame.size.height = height;

 self.frame = newFrame;

- (CGFloat)top {

 return self.frame.origin.y;

- (void)setTop:(CGFloat)top {

 CGRect newFrame = self.frame;

 newFrame.origin.y = top;

 self.frame = newFrame;

- (CGFloat)bottom {

 return self.frame.origin.y + self.frame.size.height;

- (void)setBottom:(CGFloat)bottom {

 CGRect newFrame = self.frame;

 newFrame.origin.y = bottom - self.frame.size.height;

 self.frame = newFrame;

- (CGFloat)left {

 return self.frame.origin.x;

- (void)setLeft:(CGFloat)left {

 CGRect newFrame = self.frame;

 newFrame.origin.x = left;

 self.frame = newFrame;

- (CGFloat)right {

 return self.frame.origin.x + self.frame.size.width;

- (void)setRight:(CGFloat)right {

 CGRect newFrame = self.frame;

 newFrame.origin.x = right - self.frame.size.width;

 self.frame = newFrame;

- (CGFloat)centerX {

 return self.center.x;

- (void)setCenterX:(CGFloat)centerX {

 CGPoint newCenter = self.center;

 newCenter.x = centerX;

 self.center = newCenter;

- (CGFloat)centerY {

 return self.center.y;

- (void)setCenterY:(CGFloat)centerY {

 CGPoint newCenter = self.center;

 newCenter.y = centerY;

 self.center = newCenter;

- (CGFloat)middleX {

 return CGRectGetWidth(self.bounds) / 2.f;

- (CGFloat)middleY {

 return CGRectGetHeight(self.bounds) / 2.f;

- (CGPoint)middlePoint {

 return CGPointMake(CGRectGetWidth(self.bounds) / 2.f, CGRectGetHeight(self.bounds) / 2.f);

UIView+SetRect.m

NSString+LabelWidthAndHeight
//

// NSString+LabelWidthAndHeight.h

// ZiPeiYi

// Created by YouXianMing on 15/12/9.

// Copyright © 2015年 YouXianMing. All rights reserved.

#import Foundation/Foundation.h 

#import UIKit/UIKit.h 

@interface NSString (LabelWidthAndHeight)

 * Get the strings height with the fixed width.

 * @param attribute Strings attribute, eg. attribute = @{NSFontAttributeName: [UIFont systemFontOfSize:18.f]}

 * @param width Fixed width.

 * @return Strings height.

- (CGFloat)heightWithStringAttribute:(NSDictionary NSString *, id *)attribute fixedWidth:(CGFloat)width;

 * Get the strings width.

 * @param attribute Strings attribute, eg. attribute = @{NSFontAttributeName: [UIFont systemFontOfSize:18.f]}

 * @return Strings width.

- (CGFloat)widthWithStringAttribute:(NSDictionary NSString *, id *)attribute;

 * Get a line of text height.

 * @param attribute Strings attribute, eg. attribute = @{NSFontAttributeName: [UIFont systemFontOfSize:18.f]}

 * @return Strings width.

+ (CGFloat)aLineOfTextHeightWithStringAttribute:(NSDictionary NSString *, id *)attribute;

NSString+LabelWidthAndHeight.h


//

// NSString+LabelWidthAndHeight.m

// ZiPeiYi

// Created by YouXianMing on 15/12/9.

// Copyright © 2015年 YouXianMing. All rights reserved.

#import "NSString+LabelWidthAndHeight.h"

@implementation NSString (LabelWidthAndHeight)

- (CGFloat)heightWithStringAttribute:(NSDictionary NSString *, id *)attribute fixedWidth:(CGFloat)width {

 NSParameterAssert(attribute);

 CGFloat height = 0;

 if (self.length) {

 CGRect rect = [self boundingRectWithSize:CGSizeMake(width, MAXFLOAT)

 options:NSStringDrawingTruncatesLastVisibleLine |NSStringDrawingUsesLineFragmentOrigin |

 NSStringDrawingUsesFontLeading

 attributes:attribute

 context:nil];

 height = rect.size.height;

 return height;

- (CGFloat)widthWithStringAttribute:(NSDictionary NSString *, id *)attribute {

 NSParameterAssert(attribute);

 CGFloat width = 0;

 if (self.length) {

 CGRect rect = [self boundingRectWithSize:CGSizeMake(MAXFLOAT, 0)

 options:NSStringDrawingTruncatesLastVisibleLine |NSStringDrawingUsesLineFragmentOrigin |

 NSStringDrawingUsesFontLeading

 attributes:attribute

 context:nil];

 width = rect.size.width;

 return width;

+ (CGFloat)aLineOfTextHeightWithStringAttribute:(NSDictionary NSString *, id *)attribute {

 CGFloat height = 0;

 CGRect rect = [@"One" boundingRectWithSize:CGSizeMake(200, MAXFLOAT)

 options:NSStringDrawingTruncatesLastVisibleLine |NSStringDrawingUsesLineFragmentOrigin |

 NSStringDrawingUsesFontLeading

 attributes:attribute

 context:nil];

 height = rect.size.height;

 return height;

NSString+LabelWidthAndHeight.m

ViewController.m
//

// ViewController.m

// UILabel

// Created by YouXianMing on 16/4/13.

// Copyright © 2016年 YouXianMing. All rights reserved.

#import "ViewController.h"

#import "UIView+SetRect.h"

#import "UIView+GlowView.h"

#import "NSString+LabelWidthAndHeight.h"

@interface ViewController () UIGestureRecognizerDelegate 

@property (nonatomic, strong) UIView *contentView;

@property (nonatomic, strong) UILabel *label;

@implementation ViewController

- (void)viewDidLoad {

 [super viewDidLoad];

 self.contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 250.f, 20)];

 self.contentView.layer.borderWidth = 0.5f;

 self.contentView.layer.masksToBounds = YES;

 self.contentView.layer.borderColor = [[UIColor grayColor] colorWithAlphaComponent:0.25f].CGColor;

 self.contentView.center = self.view.center;

 [self.view addSubview:self.contentView];

 NSString *string = @"Copyright © 2016 YouXianMing. All rights reserved.";

 CGFloat width = [string widthWithStringAttribute:@{NSFontAttributeName : [UIFont fontWithName:@"Heiti SC" size:14.f]}];

 self.label = [[UILabel alloc] initWithFrame:CGRectMake(self.contentView.width, 0, width, self.contentView.height)];

 self.label.font = [UIFont fontWithName:@"Heiti SC" size:14.f];

 self.label.text = string;

 [self.contentView addSubview:self.label];

 [self doAnimation];

 // Start glow.

 self.label.glowRadius = @(1.f);

 self.label.glowOpacity = @(1.f);

 self.label.glowColor = [[UIColor cyanColor] colorWithAlphaComponent:0.5f];

 self.label.glowDuration = @(1.f);

 self.label.hideDuration = @(3.f);

 self.label.glowAnimationDuration = @(2.f);

 [self.label createGlowLayer];

 [self.label insertGlowLayer];

 [self.label startGlowLoop];

 UIPanGestureRecognizer *tapGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureEvent:)];

 tapGesture.delegate = self;

 [self.contentView addGestureRecognizer:tapGesture];

- (void)doAnimation {

 CGPoint fromPoint = CGPointMake(self.contentView.width + self.label.width / 2.f, self.contentView.height / 2.f);

 UIBezierPath *movePath = [UIBezierPath bezierPath];

 [movePath moveToPoint:fromPoint];

 [movePath addLineToPoint:CGPointMake(-self.label.width / 2, self.contentView.height / 2.f)];

 CAKeyframeAnimation *moveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];

 moveAnimation.path = movePath.CGPath;

 moveAnimation.removedOnCompletion = YES;

 moveAnimation.duration = 8.f;

 moveAnimation.delegate = self;

 [self.label.layer addAnimation:moveAnimation forKey:nil];

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {

 if (flag) {

 [self doAnimation];

- (void)pauseLayer:(CALayer*)layer {

 CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];

 layer.speed = 0.0;

 layer.timeOffset = pausedTime;

- (void)resumeLayer:(CALayer*)layer {

 CFTimeInterval pausedTime = layer.timeOffset;

 layer.speed = 1.0;

 layer.timeOffset = 0.0;

 layer.beginTime = 0.0;

 CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;

 layer.beginTime = timeSincePause;

- (void)tapGestureEvent:(UIPanGestureRecognizer *)tapGesture {

 if (tapGesture.state == UIGestureRecognizerStateBegan) {

 NSLog(@"拖拽");

 [self pauseLayer:self.label.layer];

 } else if (tapGesture.state == UIGestureRecognizerStateEnded) {

 NSLog(@"释放");

 [self resumeLayer:self.label.layer];

@end

细节

暂停CALayer的动画以及恢复CALayer的动画

用了贝塞尔曲线的Path动画来实现重复移动的效果


![scroll-init.png](https://ata2-img.cn-hangzhou.oss-pub.aliyun-inc.com/c838b2d824bfb939e419ffe95ee03c94.png)