用組件式的思想實(shí)現(xiàn)跑馬燈的文字效果

?今天在項(xiàng)目中要做一個(gè)跑馬燈文字的效果郭毕。雖然網(wǎng)上有第三方的旺罢,但是本寶寶覺得這個(gè)效果實(shí)現(xiàn)起來并不是很難太抓,所以本寶寶決定 自己動(dòng)手淹仑,風(fēng)衣足食剿涮。而且還要做一個(gè)可以在IB上也能使用的控件


既然要在IB上使用言津,那么首先想到的是class

1.png

先將UILabel控件拖入到IB中,讓后把class改為ScrollLabel取试。不用寫其他的代碼悬槽,凡是只要是ScrollLabel的都應(yīng)該有這個(gè)效果。( 這讓我想到了HTML的各種組件庫瞬浓,在HTML中的標(biāo)簽都是用的class,比如<button class="btn btn-default" >按鈕</button>,而不是在標(biāo)簽里寫上style<button style="background-color : red;"></button>初婆,顯然前一種要比后面一種要更加解藕,更加適合復(fù)用猿棉。)


整體的設(shè)計(jì)思路已搭好磅叛,下面就開始進(jìn)入正題

第一步

先建立一個(gè)ScrollLabel的類
swift
import UIKit

@IBDesignable
class ScrollLabel: UILabel {

private var textLayer = CATextLayer()
var labelWidth : CGFloat {
    return self.frame.size.width
}

var labelHeight : CGFloat {
    return self.frame.size.height
}

override init(frame: CGRect) {
    super.init(frame: frame)
    initUI()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

override func awakeFromNib() {
    super.awakeFromNib()
    initUI()
}

// ?添加一個(gè)textLayer顯示在label上
func initUI(){
textLayer.string = self.text
textLayer.anchorPoint = CGPoint(x : 0,y: 0)
textLayer.bounds = CGRect(x: 0, y: 0, width: labelWidth , height: labelHeight)
textLayer.foregroundColor = self.textColor.CGColor
textLayer.backgroundColor = self.backgroundColor?.CGColor
textLayer.fontSize = self.font.pointSize
textLayer.font = self.font
self.layer.addSublayer(textLayer)
}

}

將CATextLayer添加在label上,( CATextLayer是一個(gè)可以顯示文字的圖層萨赁,CALayer要比UIView性能要好 )
***
運(yùn)行后的結(jié)果是這樣的

![2.png](http://upload-images.jianshu.io/upload_images/1215250-332f78af65b833b3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我們發(fā)現(xiàn)運(yùn)行后有一部分的被蓋住了弊琴,解決方案為一下三種:
```swift```
        self.textLayer.zPosition = 1      //第一種
        self.layer.masksToBounds = true   //第二種
        self.clipsToBounds = true         //第三種

這里我們采用的是第二種或第三種方式,因?yàn)槲覀円屗谝欢ǖ膮^(qū)域內(nèi)滾動(dòng)

第二步

添加一個(gè)動(dòng)畫,讓它開始滾動(dòng):
swift

import UIKit

class ScrollLabel: UILabel {

private var textLayer = CATextLayer()
var labelWidth : CGFloat {
    return self.frame.size.width
}

var labelHeight : CGFloat {
    return self.frame.size.height
}

override init(frame: CGRect) {
    super.init(frame: frame)
    initUI()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

override func awakeFromNib() {
    super.awakeFromNib()
    
    initUI()
    startScrollAnimation()
}

// ? 添加一個(gè)textLayer顯示在label上
func initUI(){

    if text == nil {
        text = ""
    }
    
    layer.masksToBounds = true
    
    textLayer.string = text
    textLayer.anchorPoint = CGPoint(x : 0,y: 0)
    textLayer.position = CGPoint(x: 0, y: 0)
    
    //計(jì)算text所需要的寬度
    let textWidth = text?.boundingRectWithSize(CGSize(width: 375,height: frame.size.height), options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName : font], context: nil).size.width

    textLayer.bounds = CGRect(x: 0, y: 0, width: textWidth! , height: labelHeight)
    
    textLayer.foregroundColor = textColor.CGColor
    textLayer.backgroundColor = backgroundColor?.CGColor
    textLayer.fontSize = font.pointSize
    textLayer.font = font
    textColor = UIColor.clearColor()

    self.layer.addSublayer(self.textLayer)
    
}

// ? 添加一個(gè)動(dòng)畫杖爽,讓它開始滾動(dòng)
func startScrollAnimation(){

    let animation = CABasicAnimation(keyPath: "position.x")
    animation.duration = 6
    animation.repeatCount = MAXFLOAT
    animation.fromValue = labelWidth
    animation.toValue = -textLayer.bounds.size.width
    
    textLayer.addAnimation(animation, forKey: "animation")
}

}

運(yùn)行后的結(jié)果為:

![3.gif](http://upload-images.jianshu.io/upload_images/1215250-e64c4f5d20974974.gif?imageMogr2/auto-orient/strip)

雖然文字可以滾動(dòng)敲董,但是當(dāng)label從界面上消失的時(shí)候,再次出現(xiàn)的時(shí)候就不能動(dòng)畫慰安,我猜測(cè)的原因是可能是當(dāng)控件從界面消失的時(shí)候就會(huì)刪除動(dòng)畫腋寨。
解決這個(gè)有兩個(gè)思路:
1.  **在界面消失的時(shí)候不要?jiǎng)h掉動(dòng)畫,動(dòng)畫繼續(xù)執(zhí)行**化焕。(但是萄窜,這種方法我作不出來。我把a(bǔ)nimation設(shè)置成全局變量也不行撒桨,我估計(jì)可能animation可能有個(gè)api是可以解決這個(gè)問題的查刻,但是我沒有找到,如果有知道的童鞋可以告訴我)
1.  **就是在界面出現(xiàn)的時(shí)候就添加動(dòng)畫**凤类,就是相當(dāng)于UIViewController的```viewDidAppear```穗泵。那么在UIView的子類的控件中,有沒有類似的方法了踱蠢?答案是有的火欧。
```didMoveToWindow()```:控件在出現(xiàn)的時(shí)候就調(diào)用這個(gè)方法,控件在消失的時(shí)候也會(huì)調(diào)用這個(gè)方法茎截。
接下來貼上解決后的代碼:

```swift```

import UIKit


class ScrollLabel: UILabel {
    
    private var textLayer = CATextLayer()
    
    var labelWidth : CGFloat {
        return self.frame.size.width
    }
    
    var labelHeight : CGFloat {
        return self.frame.size.height
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        initUI()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
        initUI()
        
    }
    
//  ? 將動(dòng)畫添加在這個(gè)里面 
    override func didMoveToWindow() {
        super.didMoveToWindow()
        startScrollAnimation()
    }
    
//  ? 添加一個(gè)textLayer顯示在label上
    func initUI(){
        
        if text == nil {
            text = ""
        }
        
        layer.masksToBounds = true
        
        textLayer.string = text
        textLayer.anchorPoint = CGPoint(x : 0,y: 0)
        textLayer.position = CGPoint(x: 0, y: 0)
        
        //計(jì)算text所需要的寬度
        let textWidth = text?.boundingRectWithSize(CGSize(width: 375,height: frame.size.height), options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName : font], context: nil).size.width

        textLayer.bounds = CGRect(x: 0, y: 0, width: textWidth! , height: labelHeight)
        
        textLayer.foregroundColor = textColor.CGColor
        textLayer.backgroundColor = backgroundColor?.CGColor
        textLayer.fontSize = font.pointSize
        textLayer.font = font
        textColor = UIColor.clearColor()
    
        self.layer.addSublayer(self.textLayer)
        
    }
    
//  ? 添加一個(gè)動(dòng)畫苇侵,讓它開始滾動(dòng)
    func startScrollAnimation(){
        
        let anim = textLayer.animationForKey("animation")
        
        if anim != nil  {
            print("表示animation存在,return這個(gè)函數(shù)")
            return
        }else{
            print("表示animation不存在企锌,繼續(xù)執(zhí)行下面的函數(shù)")
        }
        
        let animation = CABasicAnimation(keyPath: "position.x")
        animation.duration = 6
        animation.repeatCount = MAXFLOAT
        animation.fromValue = labelWidth
        animation.toValue = -textLayer.bounds.size.width
        
        textLayer.addAnimation(animation, forKey: "animation")
    }

}

跑馬燈的效果貌似已經(jīng)完成榆浓,But!!! 萬萬沒想到,當(dāng)我添加約束的時(shí)候出現(xiàn)了BUG:

4.gif

"敵人..." 那些字并不是從控件的尾部出現(xiàn)的撕攒,而是從中間出現(xiàn)的陡鹃。所以我們這里的解決方就是:添加一個(gè)layoutIfNeeded()

5.png

添加后就解決了這個(gè)BUG烘浦。
關(guān)于這個(gè)BUG的原因,我們來打印下控件的frame:

6.png

打印出來的結(jié)果為:

7.png

這個(gè)BUG的原因萍鲸,你們自己體會(huì)就好了


最后貼上我的完整的源代碼(直接復(fù)制粘貼就可以了):

swift
//
// ScrollLabel.swift
// ScrollLabel
//
// Created by 李修冶 on 16/8/31.
// Copyright ? 2016年 李修冶. All rights reserved.
//

import UIKit

class ScrollLabel: UILabel {

private var textLayer = CATextLayer()

var labelWidth : CGFloat {
    return self.frame.size.width
}

var labelHeight : CGFloat {
    return self.frame.size.height
}

override init(frame: CGRect) {
    super.init(frame: frame)
    initUI()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

override func awakeFromNib() {
    super.awakeFromNib()
    print("更新約束前",frame)

// ? 更新約束
layoutIfNeeded()
print("更新約束后",frame)
initUI()

}

// ? 將動(dòng)畫添加在這個(gè)里面
override func didMoveToWindow() {
super.didMoveToWindow()
startScrollAnimation()
}

// ? 添加一個(gè)textLayer顯示在label上
func initUI(){

    if text == nil {
        text = ""
    }
    
    layer.masksToBounds = true
    
    textLayer.string = text
    textLayer.anchorPoint = CGPoint(x : 0,y: 0)
    textLayer.position = CGPoint(x: 0, y: 0)
    
    //計(jì)算text所需要的寬度
    let textWidth = text?.boundingRectWithSize(CGSize(width: 375,height: frame.size.height), options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName : font], context: nil).size.width

    textLayer.bounds = CGRect(x: 0, y: 0, width: textWidth! , height: labelHeight)
    
    textLayer.foregroundColor = textColor.CGColor
    textLayer.backgroundColor = backgroundColor?.CGColor
    textLayer.fontSize = font.pointSize
    textLayer.font = font
    textColor = UIColor.clearColor()

    self.layer.addSublayer(self.textLayer)
    
}

// ? 添加一個(gè)動(dòng)畫闷叉,讓它開始滾動(dòng)
func startScrollAnimation(){

    let anim = textLayer.animationForKey("animation")
    
    if anim != nil  {
        print("表示animation存在,return這個(gè)函數(shù)")
        return
    }else{
        print("表示animation不存在脊阴,繼續(xù)執(zhí)行下面的函數(shù)")
    }
    
    let animation = CABasicAnimation(keyPath: "position.x")
    animation.duration = 6
    animation.repeatCount = MAXFLOAT
    animation.fromValue = labelWidth
    animation.toValue = -textLayer.bounds.size.width
    
    textLayer.addAnimation(animation, forKey: "animation")
}

}

最后希望你們?cè)诳赐暾燮恼潞?如果覺得我哪里寫得不好握侧,可以評(píng)論提出來,文章文字功底不行嘿期,寫得不清楚也可以提出來品擎。
如果你覺得我寫得還不錯(cuò)的話就請(qǐng)**雙擊666**
![8.jpg](http://upload-images.jianshu.io/upload_images/1215250-467d4b98a09e652a.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市备徐,隨后出現(xiàn)的幾起案子萄传,更是在濱河造成了極大的恐慌,老刑警劉巖蜜猾,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秀菱,死亡現(xiàn)場離奇詭異,居然都是意外死亡瓣铣,警方通過查閱死者的電腦和手機(jī)答朋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門贷揽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棠笑,“玉大人,你說我怎么就攤上這事禽绪”途龋” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵印屁,是天一觀的道長循捺。 經(jīng)常有香客問我,道長雄人,這世上最難降的妖魔是什么从橘? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮础钠,結(jié)果婚禮上恰力,老公的妹妹穿的比我還像新娘。我一直安慰自己旗吁,他們只是感情好踩萎,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著很钓,像睡著了一般香府。 火紅的嫁衣襯著肌膚如雪董栽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天企孩,我揣著相機(jī)與錄音锭碳,去河邊找鬼。 笑死勿璃,一個(gè)胖子當(dāng)著我的面吹牛工禾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蝗柔,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼闻葵,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了癣丧?” 一聲冷哼從身側(cè)響起槽畔,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胁编,沒想到半個(gè)月后厢钧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嬉橙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年早直,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片市框。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡霞扬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出枫振,到底是詐尸還是另有隱情喻圃,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布粪滤,位于F島的核電站斧拍,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏杖小。R本人自食惡果不足惜肆汹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望予权。 院中可真熱鬧昂勉,春花似錦、人聲如沸伟件。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谴返,卻和暖如春煞肾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嗓袱。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工籍救, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人渠抹。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓蝙昙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親梧却。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奇颠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件放航、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,024評(píng)論 4 62
  • 291976-陳國艷《2017-06-04》 【連續(xù)第113天總結(jié)】 A烈拒、目標(biāo)完成情況 1、抄寫概念一遍完成100...
    國艷更文的365天閱讀 323評(píng)論 0 1
  • 文|陌上花 最近被兒子折磨得快要瘋掉广鳍,每晚臨睡前非得纏著我講故事荆几。 你會(huì)不會(huì)翻我個(gè)白眼:買幾本童話書照著念不...
    陌上花km閱讀 429評(píng)論 11 11
  • 金指尖的花園閱讀 128評(píng)論 0 2