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

技术求助:如何用UIKit实现顺时针百分比填充色的ImageView

UIKit实现带双色顺时针百分比填充的ImageView方案

给你几个可行的实现思路,都是基于UIKit原生组件的方案:

方案一:双层CAShapeLayer叠加

这种方式适合圆形或规则形状的ImageView:

  • 先创建一个底层灰色Layer:用CAShapeLayer绘制完整的目标形状(比如圆形),设置填充/描边色为未完成状态的灰色,作为背景层。
  • 再创建一个上层彩色Layer:同样用CAShapeLayer绘制相同形状,设置填充/描边色为完成状态的彩色,通过strokeEnd(描边模式)或path(填充模式)控制显示的百分比。比如描边模式下,把strokeStart设为0,strokeEnd设为进度值(如0.7代表70%),就能实现顺时针的部分填充。
  • 将两个Layer添加到ImageView的layer上,确保尺寸和位置与ImageView匹配。

方案二:双ImageView叠加+Mask遮罩

这种方式适配任意形状的ImageView,灵活性更高:

  • 准备两个完全相同的ImageView:
    • 底层ImageView:设置tintColor为未填充状态的灰色,作为背景。
    • 上层ImageView:设置tintColor为填充状态的彩色,放在底层视图上方。
  • 给上层ImageView添加CAShapeLayer作为遮罩:根据进度值绘制顺时针的扇形(或对应形状的路径),只让上层ImageView的对应区域显示,剩余区域透出底层的灰色。
  • 进度更新时,动态修改遮罩Layer的路径即可。

简单代码示例(圆形场景)

class ProgressImageView: UIImageView {
    var progress: CGFloat = 0 {
        didSet {
            updateMaskLayer()
        }
    }
    
    private let topImageView = UIImageView()
    
    override init(image: UIImage?) {
        super.init(image: image?.withRenderingMode(.alwaysTemplate))
        setupSubviews()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupSubviews()
    }
    
    private func setupSubviews() {
        // 底层灰色背景
        self.tintColor = UIColor.lightGray
        
        // 上层彩色视图
        topImageView.image = self.image?.withRenderingMode(.alwaysTemplate)
        topImageView.tintColor = UIColor.systemBlue
        topImageView.frame = self.bounds
        topImageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.addSubview(topImageView)
        
        // 初始化遮罩Layer
        let maskLayer = CAShapeLayer()
        maskLayer.fillColor = UIColor.black.cgColor
        topImageView.layer.mask = maskLayer
    }
    
    private func updateMaskLayer() {
        guard let maskLayer = topImageView.layer.mask as? CAShapeLayer else { return }
        let center = CGPoint(x: bounds.midX, y: bounds.midY)
        let radius = min(bounds.width, bounds.height) / 2
        // 从12点钟方向开始顺时针绘制扇形路径
        let startAngle = -CGFloat.pi / 2
        let endAngle = startAngle + 2 * CGFloat.pi * progress
        let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
        path.addLine(to: center)
        path.close()
        maskLayer.path = path.cgPath
    }
}

方案三:重写draw(_ rect:)自定义绘制

如果需要更复杂的形状适配,可以自定义ImageView并重写绘制方法:

  • 在draw(_ rect:)中,先绘制未填充的灰色完整形状。
  • 再根据进度值,用贝塞尔路径绘制顺时针的填充区域,填充彩色。
  • 若要适配不规则图片,可以通过图片的alpha通道提取轮廓路径,再基于该路径生成填充区域。

注意事项

  • 所有UI操作确保在主线程执行,避免卡顿。
  • 若使用描边模式,可通过lineCap属性设置进度端点的样式(如圆角)。
  • 不规则形状的场景,可利用UIImage的CGImage提取alpha通道生成路径。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 21:35:02