如何为 swift 中的 marquee/scrolling 文本效果创建一个简单的 UILabel 子类?

How to make a simple UILabel subclass for marquee/scrolling text effect in swift?

正如您在上面看到的,我尝试编写一个简单的 (!) UILabel subclass 代码,以便在标签文本太长时制作选取框或滚动文本效果。我知道那里已经有不错的 classes(例如 https://cocoapods.org/pods/MarqueeLabel),但我想自己做 :)

在下方您可以看到我当前的 class。 我也无法解决新标签向右滚动的问题,但还有第三个标签不应该存在。我认为这是标签本身。但是,当我尝试用该标签替换第一个附加标签时,我将无法工作。我希望它不会太混乱:/

对我来说很重要的是,我只需将故事板中的 class 分配给标签。这样就不需要去添加代码,例如在视图控制器中(在插座旁边)。我希望我想要的很清楚 :D

再说一遍:

(这是我的第一个自己的子class,请随时教我正确的做法:))

非常感谢!

目前还不完美,但这是我目前的 class:

import UIKit

class LoopLabel: UILabel {

    var labelText : String?
    var rect0: CGRect!
    var rect1: CGRect!
    var labelArray = [UILabel]()
    var isStop = false
    var timeInterval: TimeInterval!
    let leadingBuffer = CGFloat(25.0)
    let loopStartDelay = 2.0

   required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.lineBreakMode = .byClipping
   }

    override var text: String? {
        didSet {
            labelText = text
            setup()
        }
    }

    func setup() {
        let label = UILabel()
        label.frame = CGRect.zero
        label.text = labelText

        timeInterval = TimeInterval((labelText?.characters.count)! / 5)
        let sizeOfText = label.sizeThatFits(CGSize.zero)
        let textIsTooLong = sizeOfText.width > frame.size.width ? true : false

        rect0 = CGRect(x: leadingBuffer, y: 0, width: sizeOfText.width, height: self.bounds.size.height)
        rect1 = CGRect(x: rect0.origin.x + rect0.size.width, y: 0, width: sizeOfText.width, height: self.bounds.size.height)
        label.frame = rect0

        super.clipsToBounds = true
        labelArray.append(label)
        self.addSubview(label)

        self.frame = CGRect(origin: self.frame.origin, size: CGSize(width: 0, height: 0))

        if textIsTooLong {
            let additionalLabel = UILabel(frame: rect1)
            additionalLabel.text = labelText
            self.addSubview(additionalLabel)

            labelArray.append(additionalLabel)

            animateLabelText()
        }
    }

    func animateLabelText() {
        if(!isStop) {
            let labelAtIndex0 = labelArray[0]
            let labelAtIndex1 = labelArray[1]

            UIView.animate(withDuration: timeInterval, delay: loopStartDelay, options: [.curveLinear], animations: {
                labelAtIndex0.frame = CGRect(x: -self.rect0.size.width,y: 0,width: self.rect0.size.width,height: self.rect0.size.height)
                labelAtIndex1.frame = CGRect(x: labelAtIndex0.frame.origin.x + labelAtIndex0.frame.size.width,y: 0,width: labelAtIndex1.frame.size.width,height: labelAtIndex1.frame.size.height)
            }, completion: { finishied in
                labelAtIndex0.frame = self.rect1
                labelAtIndex1.frame = self.rect0

                self.labelArray[0] = labelAtIndex1
                self.labelArray[1] = labelAtIndex0
                self.animateLabelText()
            })
        } else {
            self.layer.removeAllAnimations()
        }
    }
}

首先,如果您不需要从外部访问变量,我会将变量设为私有,尤其是 labelText(因为您使用的是要设置的计算 属性 文本)。

其次,由于您将标签添加为子视图,我宁愿使用 UIView 作为容器而不是 UILabel。情节提要中的唯一区别是添加视图而不是标签。

第三,如果您使用这种方法,则不应将(视图的)框架设置为零。

类似的东西会做:

import UIKit

class LoopLabelView: UIView {

    private var labelText : String?
    private var rect0: CGRect!
    private var rect1: CGRect!
    private var labelArray = [UILabel]()
    private var isStop = false
    private var timeInterval: TimeInterval!
    private let leadingBuffer = CGFloat(25.0)
    private let loopStartDelay = 2.0

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

    var text: String? {
        didSet {
            labelText = text
            setup()
        }
    }

    func setup() {
        self.backgroundColor = UIColor.yellow
        let label = UILabel()
        label.text = labelText
        label.frame = CGRect.zero

        timeInterval = TimeInterval((labelText?.characters.count)! / 5)
        let sizeOfText = label.sizeThatFits(CGSize.zero)
        let textIsTooLong = sizeOfText.width > frame.size.width ? true : false

        rect0 = CGRect(x: leadingBuffer, y: 0, width: sizeOfText.width, height: self.bounds.size.height)
        rect1 = CGRect(x: rect0.origin.x + rect0.size.width, y: 0, width: sizeOfText.width, height: self.bounds.size.height)
        label.frame = rect0

        super.clipsToBounds = true
        labelArray.append(label)
        self.addSubview(label)

        //self.frame = CGRect(origin: self.frame.origin, size: CGSize(width: 0, height: 0))

        if textIsTooLong {
            let additionalLabel = UILabel(frame: rect1)
            additionalLabel.text = labelText
            self.addSubview(additionalLabel)

            labelArray.append(additionalLabel)

            animateLabelText()
        }
    }

    func animateLabelText() {
        if(!isStop) {
            let labelAtIndex0 = labelArray[0]
            let labelAtIndex1 = labelArray[1]

            UIView.animate(withDuration: timeInterval, delay: loopStartDelay, options: [.curveLinear], animations: {
                labelAtIndex0.frame = CGRect(x: -self.rect0.size.width,y: 0,width: self.rect0.size.width,height: self.rect0.size.height)
                labelAtIndex1.frame = CGRect(x: labelAtIndex0.frame.origin.x + labelAtIndex0.frame.size.width,y: 0,width: labelAtIndex1.frame.size.width,height: labelAtIndex1.frame.size.height)
            }, completion: { finishied in
                labelAtIndex0.frame = self.rect1
                labelAtIndex1.frame = self.rect0

                self.labelArray[0] = labelAtIndex1
                self.labelArray[1] = labelAtIndex0
                self.animateLabelText()
            })
        } else {
            self.layer.removeAllAnimations()
        }
    }
}