?今天在項(xiàng)目中要做一個(gè)跑馬燈文字的效果郭毕。雖然網(wǎng)上有第三方的旺罢,但是本寶寶覺得這個(gè)效果實(shí)現(xiàn)起來并不是很難太抓,所以本寶寶決定 自己動(dòng)手淹仑,風(fēng)衣足食剿涮。而且還要做一個(gè)可以在IB上也能使用的控件
既然要在IB上使用言津,那么首先想到的是class
先將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:
"敵人..." 那些字并不是從控件的尾部出現(xiàn)的撕攒,而是從中間出現(xiàn)的陡鹃。所以我們這里的解決方就是:添加一個(gè)layoutIfNeeded()
添加后就解決了這個(gè)BUG烘浦。
關(guān)于這個(gè)BUG的原因,我們來打印下控件的frame:
打印出來的結(jié)果為:
這個(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)