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

UIProgressView调整高度并设置圆角后进度动画异常问题求助

UIProgressView 自定义高度+圆角的问题解决方案

我之前也踩过UIProgressView自定义样式的坑,来给你拆解一下问题根源和可行的解决办法:


问题1:进度跳停的原因及解决方法

为什么会出现跳停?

UIProgressView内部是由轨道图层和进度图层两个独立部分组成的。当你直接通过Interface Builder给它加高度约束强制修改高度时,系统的内部布局逻辑并没有适配这个自定义高度——它依然按照默认的intrinsicContentSize来计算进度值对应的绘制位置,导致进度的数值映射和视觉显示区域错位,就出现了你看到的“直接跳至1/3位置”的异常。

简单说:你改了外层View的高度,但内部进度的绘制区域没跟着同步调整,进度值的计算逻辑乱了。

解决方法

方案1:用可拉伸图片适配自定义高度(推荐)

不要直接改高度约束,而是通过设置可拉伸的轨道图和进度图来实现自定义高度,同时配合圆角设置:

// 准备1px高的纯色图片(可根据需求调整样式),设置为可垂直拉伸
let trackImage = UIImage(named: "track_bg")?.resizableImage(withCapInsets: .zero, resizingMode: .stretch)
let progressImage = UIImage(named: "progress_fill")?.resizableImage(withCapInsets: .zero, resizingMode: .stretch)

// 给进度条设置图片
audioProgress.trackImage = trackImage
audioProgress.progressImage = progressImage

// 最后设置圆角和裁剪
audioProgress.layer.cornerRadius = audioProgress.frame.height / 3
audioProgress.clipsToBounds = true

这种方式下,系统会根据你设置的高度约束正确拉伸图片,内部进度的计算逻辑也会基于正确的绘制区域,不会再出现跳停问题。

方案2:自定义UIProgressView子类,手动调整内部图层

如果不想用图片,可以自定义子类,在layoutSubviews里手动修正内部图层的frame:

class CustomProgressView: UIProgressView {
    override func layoutSubviews() {
        super.layoutSubviews()
        
        // 调整轨道图层的frame和圆角
        subviews.first?.frame = bounds
        subviews.first?.layer.cornerRadius = bounds.height / 3
        subviews.first?.clipsToBounds = true
        
        // 调整进度图层的frame和圆角
        subviews.last?.frame = bounds
        subviews.last?.layer.cornerRadius = bounds.height / 3
        subviews.last?.clipsToBounds = true
    }
}

使用这个子类后,你可以安全地给它加高度约束,进度显示会完全正常。


问题2:CGAffineTransform缩放时如何实现正常圆角

为什么圆角会变形?

当你先设置cornerRadius再做缩放时,圆角半径是基于原始未缩放的frame计算的,缩放后圆角的视觉大小会跟着被拉伸(比如y轴缩放3倍,圆角半径也会被拉长3倍),所以看起来边角变形。反过来先缩放再设置圆角也不行——因为UIProgressView的内部图层已经被缩放,系统的绘制逻辑依然会出现偏差。

解决方法

方案1:缩放后重新计算圆角半径

在设置transform之后,根据缩放后的实际高度重新设置cornerRadius:

let transformFactor: CGFloat = 3.0 // 你的缩放倍数
audioProgress.transform = CGAffineTransform(scaleX: 1.0, y: transformFactor)

// 基于缩放后的实际高度重新计算圆角
let scaledHeight = audioProgress.bounds.height * transformFactor
audioProgress.layer.cornerRadius = scaledHeight / 3
audioProgress.clipsToBounds = true

这个方法能缓解变形,但因为整个视图layer被缩放,可能还是会有细微的视觉偏差。

方案2:用CAShapeLayer自定义进度条(最可靠)

直接放弃系统的UIProgressView,用两个CAShapeLayer实现完全可控的进度条,彻底避免系统布局的坑:

class CustomProgressBar: UIView {
    private let trackLayer = CAShapeLayer()
    private let progressLayer = CAShapeLayer()
    
    var progress: Float = 0.0 {
        didSet { updateProgress() }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupLayers()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupLayers()
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        updateLayerFrames()
    }
    
    private func setupLayers() {
        // 配置轨道图层
        trackLayer.fillColor = UIColor.lightGray.cgColor
        layer.addSublayer(trackLayer)
        
        // 配置进度图层
        progressLayer.fillColor = UIColor.blue.cgColor
        layer.addSublayer(progressLayer)
    }
    
    private func updateLayerFrames() {
        trackLayer.frame = bounds
        trackLayer.cornerRadius = bounds.height / 3
        
        // 更新进度图层的宽度
        let progressWidth = bounds.width * CGFloat(progress)
        progressLayer.frame = CGRect(x: 0, y: 0, width: progressWidth, height: bounds.height)
        progressLayer.cornerRadius = bounds.height / 3
        
        // 进度未满时隐藏右侧圆角(可选优化)
        progressLayer.maskedCorners = progress < 1.0 
            ? [.layerMinXMinYCorner, .layerMinXMaxYCorner]
            : [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
    }
    
    private func updateProgress() {
        updateLayerFrames()
    }
}

使用这个自定义View,你可以直接设置高度约束,圆角完全符合预期,进度更新也会无比平滑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 20:12:38