一.简介
跑马灯其实就是一串文字的轮播,实现的方式有很多种。这里我只介绍一种,通过CADisplayLink与UIScrollView来实现轮播。
二.OC版本
.h
/**
显示的文字
*/
@property (nonatomic , strong )NSString *text;
/**
字体
*/
@property (nonatomic , strong , readonly)UIFont *font;
/**
背景
*/
@property (nonatomic , strong)UIColor *bgColor;
/**
字体颜色
*/
@property (nonatomic , strong)UIColor *textColor;
/**
每帧移动距离
*/
@property (nonatomic , assign)CGFloat slidingSpeed;
/**
设置控件值
@param text 显示文字
@param font 字体
*/
- (void)ay_setText:(NSString*)text Font:(UIFont *)font;
/**
开启暂停滚动
@param paused 滚动状态
*/
- (void)ay_setPaused:(BOOL)paused;
.m
初始化方法
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
self.showsVerticalScrollIndicator = NO;
self.showsHorizontalScrollIndicator = NO;
}
return self;
}
设置文本内容的方法
- (void)setText:(NSString *)text{
[self ay_setText:text Font:nil];
}
对整个UIScrollView的布局
- (void)ay_setText:(NSString *)text Font:(UIFont *)font{
// self_w是UIScrollview的真实宽度
self_w = (UIScreen.mainScreen.bounds.size.width - 24 - 24)/4 - 8;
_text = text;
_font = font ? font : kDefaultFont;
labelSize = [_text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, 100) options:NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:_font} context:nil].size;
CGSize labelSizeFrame = labelSize;
labelSizeFrame.width = labelSizeFrame.width + 6;
labelSize = labelSizeFrame;
CGRect selfFrame = self.frame;
selfFrame.size.height = _scrollerLabelSize.height > labelSize.height ? _scrollerLabelSize.height : labelSize.height;
self.frame = selfFrame;
if (!_textLabel) {[self addTxtLabel:text];}
self.contentSize = CGSizeMake(self_w > labelSize.width? self_w : labelSize.width*2 + self_w, selfFrame.size.height);
if (!_displayLink) {
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(ay_scrollingLabelAction)];
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
//自动判断 是否循环播放 当然也可以外部手动控制
if (self_w >= labelSize.width) {
_displayLink.paused = true;
} else {
_displayLink.paused = false;
}
}
这里是label的布局和配置
-(void)addTxtLabel:(NSString*)title {
for (UIView *view in [self subviews]) {
[view removeFromSuperview];
}
for (int i = 0; i<3; i++) {
UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(self_w > labelSize.width ? 0 : i == 2 ? (labelSize.width + self_w): (i*labelSize.width + self_w/2), 0, self_w > labelSize.width ? self_w : i == 1 ? self_w/2 :labelSize.width, self.frame.size.height)];
textLabel.textAlignment = NSTextAlignmentCenter;
textLabel.font = _font;
if (i == 1) {
textLabel.textColor = [UIColor clearColor];
} else {
textLabel.textColor = _textColor;
}
textLabel.text = _text;
[self addSubview:textLabel];
}
_scrollerLabelSize = self.frame.size;
}
循环方法
- (void)ay_scrollingLabelAction{
CGPoint point = self.contentOffset;
if (point.x < labelSize.width + self_w/2) {
point.x += _slidingSpeed ? _slidingSpeed : 1;
}else{
point.x = 1;
}
self.contentOffset = point;
}
其他的一些暂定、开始、以及CADisplayLink释放方法
- (void)ay_setPaused:(BOOL)paused{
_displayLink.paused = paused;
}
- (void)setBgColor:(UIColor *)bgColor{
_bgColor = bgColor;
self.backgroundColor = bgColor;
}
- (void)setTextColor:(UIColor *)textColor{
_textColor = textColor;
_textLabel.textColor = textColor;
}
- (void)dealloc{
[_displayLink invalidate];
[_displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
此方法来自百度搜索,我做了简单的优化解决了轮播卡顿问题,下面我自己手写了swift的初稿 逻辑更完整
三.Swift
直接上代码 可运行,不懂得地方欢迎交流
import Foundation
class ZHJRunningLight: UIScrollView {
var text: String?
var font: UIFont?
var bgColor: UIColor?
var textColor: UIColor?
var slidingSpeed: CGFloat?
var displayLink: CADisplayLink?
var zhjSize: CGSize?
var labelSize: CGSize?
var isPaused: Bool = true
init(frame: CGRect,color: UIColor,bgColor: UIColor) {
super.init(frame: frame)
zhjSize = frame.size
self.textColor = color
self.bgColor = bgColor
showsVerticalScrollIndicator = false
showsHorizontalScrollIndicator = false
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
showsVerticalScrollIndicator = false
showsHorizontalScrollIndicator = false
}
/**
控件布局
*/
func addTxtLabel() {
guard let zhjSize = self.zhjSize,let labelSize = self.labelSize else {return}
removeAllSubViews()
for i in 0..<3 {
let x = isPaused ? 0 : i == 2 ? (labelSize.width + zhjSize.width) : CGFloat(CGFloat(i) * labelSize.width + zhjSize.width/2)
let y = 0
let w = isPaused ? zhjSize.width : i == 1 ? zhjSize.width/2 : labelSize.width
let h = zhjSize.height
let label = UILabel(frame: CGRect(x: x, y: CGFloat(y), width: w, height: h))
label.text = self.text
label.textAlignment = .center
if i == 1{
label.textColor = UIColor.clear
} else {
label.textColor = self.textColor
}
label.font = self.font
label.backgroundColor = UIColor.clear
addSubview(label)
}
}
/**
设置控件值
@param text 显示文字
@param font 字体
*/
func zhj_setTextAndFont(text: String,font: UIFont = UIFont.systemFont(ofSize: 15.0)) {
// self.slidingSpeed = 1 ?? 1
self.text = text
self.font = font
self.labelSize = text.WithStrigFontSize(font, sizeFont: nil, width: nil)
var labelSizeFrame: CGSize = self.labelSize!
labelSizeFrame.width = labelSizeFrame.width + 6;
self.labelSize = labelSizeFrame
guard let zhjSize = self.zhjSize,let labelSize = self.labelSize else {alert("请给控件大小赋值");return}
var selfFrame: CGRect = frame
selfFrame.size.height = zhjSize.height > labelSize.height ? zhjSize.height : labelSize.height
self.frame = selfFrame;
(zhjSize.width >= labelSize.width) ? (isPaused = true) : (isPaused = false)
addTxtLabel()
self.contentSize = CGSize(width: isPaused ? zhjSize.width : labelSize.width*2 + zhjSize.width, height: selfFrame.size.height)
if displayLink == nil {
displayLink = CADisplayLink(target: self, selector: #selector(zhj_scrollingLabelAction))
displayLink?.add(to: RunLoop.current, forMode: .defaultRunLoopMode)
}
if (isPaused) {
displayLink?.isPaused = true
} else {
displayLink?.isPaused = false
}
}
func zhj_scrollingLabelAction() {
var point: CGPoint = self.contentOffset
if (point.x < labelSize!.width + zhjSize!.width/2) {
point.x += slidingSpeed ?? 1
}else{
point.x = 1;
}
self.contentOffset = point;
}
/**
开启暂停滚动
@param paused 滚动状态
*/
func zhj_setPaused(ispaus: Bool) {
displayLink?.isPaused = ispaus
}
deinit {
displayLink?.invalidate()
displayLink?.remove(from: .current, forMode: .defaultRunLoopMode)
}
}