美文网首页
iOS 跑马灯效果

iOS 跑马灯效果

作者: Topredator | 来源:发表于2017-12-01 11:54 被阅读2798次

    介绍

    因业务需求,需要做一个跑马灯效果的公告栏,查了网上大部分制作原理,都不能满足也无需求,决定自己造轮子

    效果

    MarqueeGif.gif

    需求

    • 如果文字太多,跑马灯效果;否则,无动画中间位置显示
    • 默认跑马灯10秒结束,后自动隐藏;短文一样10秒显隐
    • 随时更换内容,更换内容是时间重新计算
    • 点击关闭按钮,消失

    原理

    CoreAnimation动画实现,通过检测动画的开始与结束,进行业务处理

    代码部分

    .h文件

    @class MarqueeView;
    typedef NS_ENUM(NSInteger, MarqueeViewDirection) {
        MarqueeDirectionLeft,// 从右向左
        MarqueeDirectionRight // 从左向右
    };
    
    @protocol MarqueeViewDelegate <NSObject>
    @optional
    //  动画结束回调
    - (void)marqueeView:(MarqueeView *)marqueeView animationDidStopFinished:(BOOL)fnished;
    // 关闭
    - (void)closeMarqueeView:(MarqueeView *)marqueeView;
    @end
    
    @interface MarqueeView : UIView
    
    /// 代理
    @property (nonatomic, weak) id <MarqueeViewDelegate> delegate;
    /// 速度 default 1.0
    @property (nonatomic, assign) CGFloat speed;
    /// 动画时间     设置完后speed失去作用
    @property (nonatomic, assign) CGFloat duration;
    /// 方向  default MarqueeDirectionLeft
    @property (nonatomic, assign) MarqueeViewDirection marqueeDirection;
    /// 是否需要标题, 需要在添加视图之前设置  default NO
    @property (nonatomic, assign) BOOL isNeedTitle;
    
    - (instancetype)init NS_UNAVAILABLE;
    /**
     添加内容视图
    
     @param view 内容视图
     */
    - (void)addContentView:(UIView *)view;
    
    /**
     开始动画
     */
    - (void)startAnimation;
    
    /**
     结束动画
     */
    - (void)stopAnimation;
    
    /**
     暂停动画
     */
    - (void)pauseAnimation;
    
    /**
     重新开始动画
     */
    - (void)resumeAnimation;
    

    .m文件

    static CGFloat const kTitleWidth = 80;
    static CGFloat const kCancleWidth = 30;
    
    static CGFloat const kDefaultDuration = 10;
    
    @interface MarqueeView ()<CAAnimationDelegate> {
        /// 整体控件宽度
        CGFloat _width;
        /// 整体控件高度
        CGFloat _height;
        
        /// 动画视图的宽度
        CGFloat _animationViewWidth;
        /// 动画视图的高度
        CGFloat _animationViewHeight;
        
        /// 内容视图
        UIView *_contentView;
        UIView *_bgView;
    }
    /// 背景视图
    @property (nonatomic, strong) UIView *bgView;
    /// 动画视图
    @property (nonatomic, strong) UIView *animationView;
    /// title视图
    @property (nonatomic, strong) UIButton *titleBtn;
    /// 关闭按钮
    @property (nonatomic, strong) UIButton *closeBtn;
    /// 是否重新赋值开始动画
    @property (nonatomic, assign) BOOL isRestart;
    @end
    
    @implementation MarqueeView
    #pragma mark ==================  init method  ==================
    - (instancetype)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            _width = frame.size.width;
            _height = frame.size.height;
            
            self.layer.cornerRadius = _height / 2;
            self.layer.masksToBounds = YES;
            
            _isRestart = YES;
            _speed = 1.f;
            _marqueeDirection = MarqueeDirectionLeft;
            
            [self addSubview:self.bgView];
            [_bgView addSubview:self.animationView];
            [self addSubview:self.titleBtn];
            [self addSubview:self.closeBtn];
            
        }
        return self;
    }
    #pragma mark ==================  public method  ==================
    - (void)addContentView:(UIView *)view {
        [_contentView removeFromSuperview];
        view.frame = view.bounds;
        _contentView = view;
        
        if (!_isNeedTitle) {
            self.animationView.frame = view.bounds;
            self.layer.cornerRadius = 0;
        } else {
            _titleBtn.frame = CGRectMake(0, 0, kTitleWidth, _height);
            _bgView.frame = CGRectMake(kTitleWidth + 10, 0, _width - kTitleWidth - kCancleWidth - 20, _height);
            _closeBtn.frame = CGRectMake(_width - kCancleWidth, 0, kCancleWidth, _height);
            self.animationView.frame = CGRectMake(0, 0, view.bounds.size.width, view.bounds.size.height);
        }
        if (_bgView.width >= view.width) {
            self.animationView.frame = CGRectMake((_bgView.width - view.width) / 2, 0, view.width, view.height);
        }
        [self.animationView addSubview:_contentView];
        _animationViewWidth = self.animationView.width;
        _animationViewHeight = self.animationView.height;
    }
    #pragma mark ==================  CAAnimationDelegate   ==================
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
        if (_isRestart) {
            if (self.delegate && [self.delegate respondsToSelector:@selector(marqueeView:animationDidStopFinished:)]) {
                [self.delegate marqueeView:self animationDidStopFinished:flag];
            }
        }
        _isRestart = YES;
    }
    - (void)startAnimation {
        [self stopAnimation];
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(dismiss) object:nil];
        CGFloat bgWidth = CGRectGetWidth(_bgView.frame);
        if (bgWidth >= _animationViewWidth) {
            [self performSelector:@selector(dismiss) withObject:nil afterDelay:_duration ? : kDefaultDuration];
            return;
        }
        
        dispatch_block_t block = ^ {
            CGPoint pointRightCenter = CGPointMake(_animationViewWidth / 2, _animationViewHeight / 2);
            CGPoint pointLeftCenter = CGPointMake(- _animationViewWidth / 2, _animationViewHeight / 2);
            CGPoint fromPoint = self.marqueeDirection == MarqueeDirectionLeft ? pointRightCenter : pointLeftCenter;
            CGPoint toPoint = self.marqueeDirection == MarqueeDirectionLeft ? pointLeftCenter : pointRightCenter;
            
            self.animationView.center = fromPoint;
            UIBezierPath *movePath    = [UIBezierPath bezierPath];
            [movePath moveToPoint:fromPoint];
            [movePath addLineToPoint:toPoint];
            
            CAKeyframeAnimation *moveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
            moveAnimation.path                 = movePath.CGPath;
            moveAnimation.removedOnCompletion  = YES;
            moveAnimation.duration             = _duration ? : _animationViewWidth / 30.f * (1 / self.speed);
            moveAnimation.delegate             = self;
            [self.animationView.layer addAnimation:moveAnimation forKey:@"animationViewPosition"];
        };
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), block);
    }
    - (void)stopAnimation {
        if (self.animationView.layer.animationKeys.count) {
            [self.animationView.layer removeAllAnimations];
            _isRestart = NO;
        }
    }
    - (void)pauseAnimation {
        [self pauseLayer:self.animationView.layer];
    }
    - (void)resumeAnimation {
        [self resumeLayer:self.animationView.layer];
    }
    
    - (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)dismiss {
        [self.animationView.layer removeAllAnimations];
        _isRestart = NO;
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(dismiss) object:nil];
        if (self.delegate && [self.delegate respondsToSelector:@selector(closeMarqueeView:)]) {
            [self.delegate closeMarqueeView:self];
        }
    }
    #pragma mark ==================  setter and getter method ==================
    - (UIView *)bgView {
        if (!_bgView) {
            _bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _width, _height)];
            _bgView.layer.masksToBounds = YES;
        }
        return _bgView;
    }
    - (UIView *)animationView {
        if (!_animationView) {
            _animationView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _width, _height)];
        }
        return _animationView;
    }
    - (UIButton *)titleBtn {
        if (!_titleBtn) {
            UIButton *titleBtn = [[UIButton alloc] initWithFrame:CGRectMake(- kTitleWidth, 0, kTitleWidth, _height)];
            titleBtn.layer.cornerRadius = _height / 2;
            titleBtn.layer.masksToBounds = YES;
            [titleBtn setBackgroundColor:kDefTintColor];
            [titleBtn setTitle:@"公告:" forState:UIControlStateNormal];
            [titleBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
            titleBtn.titleLabel.font = Font(14);
            [titleBtn setImage:Image(@"announcementSound") forState:UIControlStateNormal];
            _titleBtn = titleBtn;
        }
        return _titleBtn;
    }
    - (UIButton *)closeBtn {
        if (!_closeBtn) {
            UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(_width, 0, kCancleWidth, _height)];
            [btn setImage:Image(@"announcementClose") forState:UIControlStateNormal];
            [btn addTarget:self action:@selector(dismiss) forControlEvents:UIControlEventTouchUpInside];
            _closeBtn = btn;
        }
        return _closeBtn;
    }
    @end
    

    使用

    self.marqueeView = [[MarqueeView alloc] initWithFrame:CGRectMake(0, 0, 300, 30)];
            _marqueeView.backgroundColor = RGBACOLOR(0, 0, 0, 0.7);
            _marqueeView.duration = 10;
            _marqueeView.delegate = self;
            _marqueeView.center = self.view.center;
            [self.view addSubview:_marqueeView];
            _marqueeView.isNeedTitle = YES;
            [_marqueeView addContentView:[self createLabelWithText:@"XXXXXXXXXXXXXXXXXXXXX"]];
            [_marqueeView startAnimation];
    
    /// 更换
    - (void)clickAction:(UIButton *)btn {
        [_marqueeView stopAnimation];
        [_marqueeView addContentView:[self createLabelWithText:@"haha"]];
        [_marqueeView startAnimation];
    }
    

    相关文章

      网友评论

          本文标题:iOS 跑马灯效果

          本文链接:https://www.haomeiwen.com/subject/lurnyxtx.html