You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CALayer从下到上动画需求:UIImageView上接口请求进度组动画异常

解决UIImageView上WebService进度动画从下到上的问题

嘿,我之前刚好碰到过一模一样的场景!你现在的动画从顶部启动,核心原因是CALayer默认的锚点在图层中心,咱们只需要调整锚点或者用mask图层,就能轻松实现从下往上的进度效果,还能同步文本变更的动画。

方案一:使用Mask图层(推荐,灵活适配各种场景)

这种方式是给UIImageView的图层加一个遮罩,通过动画遮罩的高度来实现从下往上“揭开”原图的效果,完全贴合进度逻辑,还不会影响原图的样式(比如圆角、阴影)。

代码示例:

// 假设你的UIImageView实例叫progressImageView
let maskLayer = CALayer()
maskLayer.backgroundColor = UIColor.black.cgColor
// 初始状态:遮罩高度为0,位置在ImageView底部,完全隐藏原图
maskLayer.frame = CGRect(
    x: 0,
    y: progressImageView.bounds.height,
    width: progressImageView.bounds.width,
    height: 0
)
progressImageView.layer.mask = maskLayer

// 1. 进度动画:让遮罩高度从0增加到ImageView的高度
let progressAnim = CABasicAnimation(keyPath: "bounds.size.height")
progressAnim.fromValue = 0
progressAnim.toValue = progressImageView.bounds.height
progressAnim.duration = 2.0 // 替换成你的实际请求时长
progressAnim.fillMode = .forwards
progressAnim.isRemovedOnCompletion = false

// 2. 文本变更动画:这里以CATextLayer的淡入+文本更新为例
let textLayer = CATextLayer()
textLayer.string = "加载中..."
textLayer.frame = progressImageView.bounds
textLayer.alignmentMode = .center
textLayer.foregroundColor = UIColor.white.cgColor
textLayer.fontSize = 16
progressImageView.layer.addSublayer(textLayer)

let textAnim = CABasicAnimation(keyPath: "opacity")
textAnim.fromValue = 0
textAnim.toValue = 1
textAnim.duration = 2.0

// 3. 组合动画:同步执行进度和文本动画
let animGroup = CAAnimationGroup()
animGroup.animations = [progressAnim, textAnim]
animGroup.duration = 2.0
animGroup.fillMode = .forwards
animGroup.isRemovedOnCompletion = false

maskLayer.add(animGroup, forKey: "progressWithTextGroup")

方案二:调整图层锚点+缩放动画

如果不想用遮罩,也可以直接修改UIImageView图层的锚点,让缩放动画从底部开始。不过要注意,锚点变更会影响图层位置,需要重置frame避免偏移。

代码示例:

// 保存原始frame,因为锚点变更会改变图层位置
let originalFrame = progressImageView.frame
// 将锚点设为底部中心,这样缩放动画会从底部向上展开
progressImageView.layer.anchorPoint = CGPoint(x: 0.5, y: 1.0)
// 重置frame,修正锚点变更带来的偏移
progressImageView.frame = originalFrame

// 进度动画:Y轴从0缩放到1,实现从下往上的填充效果
let scaleAnim = CABasicAnimation(keyPath: "transform.scale.y")
scaleAnim.fromValue = 0
scaleAnim.toValue = 1
scaleAnim.duration = 2.0
scaleAnim.fillMode = .forwards
scaleAnim.isRemovedOnCompletion = false

// 同步文本动画(和方案一类似)
let textLayer = CATextLayer()
textLayer.string = "加载中..."
textLayer.frame = progressImageView.bounds
textLayer.alignmentMode = .center
textLayer.foregroundColor = UIColor.white.cgColor
progressImageView.layer.addSublayer(textLayer)

let textAnim = CABasicAnimation(keyPath: "opacity")
textAnim.fromValue = 0
textAnim.toValue = 1
textAnim.duration = 2.0

// 组合动画
let animGroup = CAAnimationGroup()
animGroup.animations = [scaleAnim, textAnim]
animGroup.duration = 2.0
progressImageView.layer.add(animGroup, forKey: "scaleProgressGroup")

额外技巧:绑定真实WebService进度

如果要和网络请求的实时进度同步,不需要用基础动画,直接在进度回调里更新遮罩的frame即可:

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceiveData data: Data) {
    guard let totalBytes = dataTask.response?.expectedContentLength else { return }
    let receivedBytes = dataTask.countOfBytesReceived
    let progressRatio = CGFloat(receivedBytes) / CGFloat(totalBytes)
    
    DispatchQueue.main.async {
        // 计算遮罩的位置和高度:进度越高,遮罩从底部向上延伸的高度越大
        self.maskLayer.frame = CGRect(
            x: 0,
            y: self.progressImageView.bounds.height * (1 - progressRatio),
            width: self.progressImageView.bounds.width,
            height: self.progressImageView.bounds.height * progressRatio
        )
        // 同步更新文本
        self.textLayer.string = String(format: "已加载%.0f%%", progressRatio * 100)
    }
}

这样就能完美实现和网络请求进度同步的从下往上动画啦!

内容的提问来源于stack exchange,提问作者ibiren

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:05:25