今天在项目中要做一个跑马灯文字的效果。虽然网上有第三方的,但是本宝宝觉得这个效果实现起来并不是很难,所以本宝宝决定 自己动手,风衣足食。而且还要做一个可以在IB上也能使用的控件
既然要在IB上使用,那么首先想到的是class
先将UILabel控件拖入到IB中,让后把class改为ScrollLabel。不用写其他的代码,凡是只要是ScrollLabel的都应该有这个效果。( 这让我想到了HTML的各种组件库,在HTML中的标签都是用的class,比如
<button class="btn btn-default" >按钮</button>
,而不是在标签里写上style<button style="background-color : red;"></button>
,显然前一种要比后面一种要更加解藕,更加适合复用。)
整体的设计思路已搭好,下面就开始进入正题
第一步
先建立一个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()
}
// ️添加一个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是一个可以显示文字的图层,CALayer要比UIView性能要好 )
***
运行后的结果是这样的
![2.png](https://img.haomeiwen.com/i1215250/332f78af65b833b3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我们发现运行后有一部分的被盖住了,解决方案为一下三种:
```swift```
self.textLayer.zPosition = 1 //第一种
self.layer.masksToBounds = true //第二种
self.clipsToBounds = true //第三种
这里我们采用的是第二种或第三种方式,因为我们要让它在一定的区域内滚动
第二步
添加一个动画,让它开始滚动:
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()
}
// ️ 添加一个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)
//计算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)
}
// ️ 添加一个动画,让它开始滚动
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")
}
}
运行后的结果为:
![3.gif](https://img.haomeiwen.com/i1215250/e64c4f5d20974974.gif?imageMogr2/auto-orient/strip)
虽然文字可以滚动,但是当label从界面上消失的时候,再次出现的时候就不能动画,我猜测的原因是可能是当控件从界面消失的时候就会删除动画。
解决这个有两个思路:
1. **在界面消失的时候不要删掉动画,动画继续执行**。(但是,这种方法我作不出来。我把animation设置成全局变量也不行,我估计可能animation可能有个api是可以解决这个问题的,但是我没有找到,如果有知道的童鞋可以告诉我)
1. **就是在界面出现的时候就添加动画**,就是相当于UIViewController的```viewDidAppear```。那么在UIView的子类的控件中,有没有类似的方法了?答案是有的。
```didMoveToWindow()```:控件在出现的时候就调用这个方法,控件在消失的时候也会调用这个方法。
接下来贴上解决后的代码:
```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()
}
// ️ 将动画添加在这个里面
override func didMoveToWindow() {
super.didMoveToWindow()
startScrollAnimation()
}
// ️ 添加一个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)
//计算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)
}
// ️ 添加一个动画,让它开始滚动
func startScrollAnimation(){
let anim = textLayer.animationForKey("animation")
if anim != nil {
print("表示animation存在,return这个函数")
return
}else{
print("表示animation不存在,继续执行下面的函数")
}
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")
}
}
跑马灯的效果貌似已经完成,But!!! 万万没想到,当我添加约束的时候出现了BUG:
4.gif"敌人..." 那些字并不是从控件的尾部出现的,而是从中间出现的。所以我们这里的解决方就是:添加一个layoutIfNeeded()
添加后就解决了这个BUG。
关于这个BUG的原因,我们来打印下控件的frame:
打印出来的结果为:
7.png这个BUG的原因,你们自己体会就好了
最后贴上我的完整的源代码(直接复制粘贴就可以了):
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()
}
// ️ 将动画添加在这个里面
override func didMoveToWindow() {
super.didMoveToWindow()
startScrollAnimation()
}
// ️ 添加一个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)
//计算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)
}
// ️ 添加一个动画,让它开始滚动
func startScrollAnimation(){
let anim = textLayer.animationForKey("animation")
if anim != nil {
print("表示animation存在,return这个函数")
return
}else{
print("表示animation不存在,继续执行下面的函数")
}
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")
}
}
最后希望你们在看完折篇文章后,如果觉得我哪里写得不好,可以评论提出来,文章文字功底不行,写得不清楚也可以提出来。
如果你觉得我写得还不错的话就请**双击666**
![8.jpg](https://img.haomeiwen.com/i1215250/467d4b98a09e652a.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
网友评论