是否可以设置仅在iOS中部分文本动画的文本颜色变化?

问题描述 投票:1回答:1

[我想知道是否甚至可以在iOS中为文本的一部分动画化颜色,最好不是逐个字符,而是逐个像素地动画化,就像这张图片一样?

enter image description here

我知道如何使用NSAttributedString静态更改文本颜色,并且我知道如何使用CADisplayLink为整个文本设置动画,但这使我感到担忧。

也许我可以深入研究CoreText,但是我仍然不确定是否有可能。有什么想法吗?

UPD我决定添加一个包含我的第一个结果的视频,以使问题更加清楚:

my efforts for now (the label is overlapping)

ios uilabel nsattributedstring core-text
1个回答
1
投票

您可以很容易地使用CoreAnimation实现这一目标。我添加了一个简单的演示,您可以使用它here播放(只需构建项目,然后点击任意位置以查看动画)。

逻辑如下:

  1. 创建UIView的自定义子类。
  2. [设置了某些文本后,创建两个相似的CATextLayers,每个文本和框架都相同。
  3. 为这些图层设置不同的foregroundColormask。左层的mask将是视图的左部分,右层的mask将是视图的右部分。
  4. 同时为这些图层设置动画foregroundColor

自定义视图的代码:

class CustomTextLabel: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .green
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private var textLayer1: CATextLayer?
    private var textLayer2: CATextLayer?

    func setText(_ text: String, fontSize: CGFloat) {
        // create 2 layers with the same text and size, we'll set the colors for them later
        textLayer1 = createTextLayer(text, fontSize: fontSize)
        textLayer2 = createTextLayer(text, fontSize: fontSize)

        // estimate the frame size needed for the text layer with such text and font size
        let textSize = textLayer1!.preferredFrameSize()
        let w = frame.width, h = frame.height

        // calculate the frame such that both layers will be in center of view
        let centeredTextFrame = CGRect(x: (w-textSize.width)/2, y: (h-textSize.height)/2, width: textSize.width, height: textSize.height)
        textLayer1!.frame = centeredTextFrame
        textLayer2!.frame = centeredTextFrame

        // set up default color for the text
        textLayer1!.foregroundColor = UIColor.yellow.cgColor
        textLayer2!.foregroundColor = UIColor.yellow.cgColor

        // set background transparent, that's very important
        textLayer1!.backgroundColor = UIColor.clear.cgColor
        textLayer2!.backgroundColor = UIColor.clear.cgColor

        // set up masks, such that each layer's text is visible only in its part
        textLayer1!.mask = createMaskLayer(CGRect(x: 0, y: 0, width: textSize.width/2, height: textSize.height))
        textLayer2!.mask = createMaskLayer(CGRect(x: textSize.width/2, y: 0, width: textSize.width/2, height: textSize.height))

        layer.addSublayer(textLayer1!)
        layer.addSublayer(textLayer2!)
    }

    private var finishColor1: UIColor = .black, finishColor2: UIColor = .black
    func animateText(leftPartColor1: UIColor, leftPartColor2: UIColor, rightPartColor1: UIColor, rightPartColor2: UIColor) {
        finishColor1 = leftPartColor2
        finishColor2 = rightPartColor2

        if let layer1 = textLayer1, let layer2 = textLayer2 {
            CATransaction.begin()
            let animation1 = CABasicAnimation(keyPath: "foregroundColor")
            animation1.fromValue = leftPartColor1.cgColor
            animation1.toValue = leftPartColor2.cgColor
            animation1.duration = 3.0
            layer1.add(animation1, forKey: "animation1")


            let animation2 = CABasicAnimation(keyPath: "foregroundColor")
            animation2.fromValue = rightPartColor1.cgColor
            animation2.toValue = rightPartColor2.cgColor
            animation2.duration = 3.0
            layer2.add(animation2, forKey: "animation2")

            CATransaction.setCompletionBlock {
                self.textLayer1?.foregroundColor = self.finishColor1.cgColor
                self.textLayer2?.foregroundColor = self.finishColor2.cgColor
            }

            CATransaction.commit()
        }
    }

    private func createTextLayer(_ text: String, fontSize: CGFloat) -> CATextLayer {
        let textLayer = CATextLayer()
        textLayer.string = text
        textLayer.fontSize = fontSize // TODO: also set font name
        textLayer.contentsScale = UIScreen.main.scale

        return textLayer
    }

    private func createMaskLayer(_ holeRect: CGRect) -> CAShapeLayer {
        let layer = CAShapeLayer()

        let path = CGMutablePath()

        path.addRect(holeRect)
        path.addRect(bounds)

        layer.path = path
        layer.fillRule = CAShapeLayerFillRule.evenOdd
        layer.opacity = 1

        return layer
    }
}

自定义视图的调用:

class ViewController: UIViewController {

    var customLabel: CustomTextLabel!
    override func viewDidLoad() {
        super.viewDidLoad()

        let viewW = view.frame.width, viewH = view.frame.height
        let labelW: CGFloat = 200, labelH: CGFloat = 50

        customLabel = CustomTextLabel(frame: CGRect(x: (viewW-labelW)/2, y: (viewH-labelH)/2, width: labelW, height: labelH))
        customLabel.setText("Optimizing...", fontSize: 20)
        view.addSubview(customLabel)

        let tapRecogniner = UITapGestureRecognizer(target: self, action: #selector(onTap))
        view.addGestureRecognizer(tapRecogniner)
    }

    @objc func onTap() {
        customLabel.animateText(leftPartColor1: UIColor.blue,
                                leftPartColor2: UIColor.red,
                                rightPartColor1: UIColor.white,
                                rightPartColor2: UIColor.black)
    }

}
© www.soinside.com 2019 - 2024. All rights reserved.