你的位置:首页 > 操作系统

[操作系统]带辉光效果的跑马灯


带辉光效果的跑马灯

 

效果

 

说明

并没有对代码进行封装,以后会在项目 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;@end

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;@end@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));}@end

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;@end

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);}@end

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 string's height with the fixed width. * * @param attribute String's attribute, eg. attribute = @{NSFontAttributeName: [UIFont systemFontOfSize:18.f]} * @param width   Fixed width. * * @return String's height. */- (CGFloat)heightWithStringAttribute:(NSDictionary <NSString *, id> *)attribute fixedWidth:(CGFloat)width;/** * Get the string's width. * * @param attribute String's attribute, eg. attribute = @{NSFontAttributeName: [UIFont systemFontOfSize:18.f]} * * @return String's width. */- (CGFloat)widthWithStringAttribute:(NSDictionary <NSString *, id> *)attribute;/** * Get a line of text height. * * @param attribute String's attribute, eg. attribute = @{NSFontAttributeName: [UIFont systemFontOfSize:18.f]} * * @return String's width. */+ (CGFloat)aLineOfTextHeightWithStringAttribute:(NSDictionary <NSString *, id> *)attribute;@end

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;}@end

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;@end@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动画来实现重复移动的效果