当一个文本比其他文本长时,UILabel 文本更改动画会闪烁

UILabel text change animation flickers when one text is longer than other

我正在使用理想的解决方案来为我的 UILabel 设置动画文本更改。类似于这个答案
问题是当文本长度不同时,动画不再流畅,文本在动画化之前会闪烁。
视频:https://drive.google.com/file/d/1I89NnzjQp7TbemO-dmcbKzYUr7pM7mGk/view?usp=sharing

我的代码如下所示:

@IBOutlet weak var label: UILabel!

var titleLabelAnimation: CATransition = {
        let animation = CATransition()
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.default)
        animation.type = .push
        animation.subtype = .fromTop
        animation.duration = 0.5
        return animation
    }()

    @IBAction func didTap() {
        self.label.layer.add(self.titleLabelAnimation, forKey: nil)
        self.label.text = "Can you see a flicker?"
        self.label.sizeToFit()
    }
    
    @IBAction func didTapRev() {
        self.label.layer.add(self.titleLabelAnimation, forKey: nil)
        self.label.text = "Hello this is animation !!!"
        self.label.sizeToFit()
    }

我已经尝试过 layoutIfNeeded()、sizeToFit()、更改动画前的文本以及其他几个解决方法。似乎没有任何效果!

不确定您的示例中到底发生了什么,因为我无法生成此结果。但是动画在很多情况下是一种痛苦,很多东西可能会产生跳跃视图。

通过对动画有更多的控制,您可能会更好地找到问题或修复它。对于您的特定情况,使用快照制作动画可能已经足够了。查看以下内容:

@IBOutlet weak var label: UILabel!

private func animateAsCustom(applyChanges: @escaping (() -> Void)) {
    guard let viewToMove = label else { return }
    guard let panel = viewToMove.superview else { return }
    guard let snapshotView = viewToMove.snapshotView(afterScreenUpdates: true) else { return }
    
    applyChanges()
    
    UIView.performWithoutAnimation {
        panel.addSubview(snapshotView)
        snapshotView.center = viewToMove.center
        viewToMove.transform = CGAffineTransform(translationX: 0.0, y: 50.0) // TODO: compute values for translation
        viewToMove.alpha = 0.0
    }
    UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveEaseIn) {
        viewToMove.transform = .identity
        snapshotView.transform = CGAffineTransform(translationX: 0.0, y: -50.0)
        snapshotView.alpha = 0.0
        viewToMove.alpha = 1.0
    } completion: { _ in
        snapshotView.removeFromSuperview()
    }
}

@IBAction func didTap() {
    animateAsCustom {
        self.label.numberOfLines = 1
        self.label.text = "Can you see a flicker?"
        self.label.textColor = .red
        self.label.font = UIFont.systemFont(ofSize: 20)
    }
}

@IBAction func didTapRev() {
    animateAsCustom {
        self.label.numberOfLines = 0
        self.label.text = "Hello this\nis animation !!!"
        self.label.textColor = .black
        self.label.font = UIFont.systemFont(ofSize: 30)
    }
}

如果您在上一个动画完成之前按下其中一个按钮,这仍然无法解决问题。要解决这个问题,可能还需要一些额外的努力。但目前这个解决方案可能就足够了。