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

CAShapeLayer内部添加图片失败问题咨询及实现方案求助

CAShapeLayer内部添加图片失败问题咨询及实现方案求助

哥们,我仔细看了你的代码,发现几个导致系统图标不显示的关键问题,给你梳理下并附上可行的修改方案:

问题分析

  • circularShapeLayer的bounds未正确初始化:你只给circularShapeLayer设置了path,但没设置它的bounds,默认情况下它的bounds是CGRectZero,所以你给systemImageView.frame赋值为circularShapeLayer.bounds时,图片视图的尺寸是0,自然看不到。
  • UIImageView的contentMode未配置:系统图标(SF Symbols)需要合适的contentMode来保证显示正常,默认的contentMode可能导致图标被压缩或不显示。
  • 图层层级的问题:直接把UIImageView.layer添加到CAShapeLayer里,不如将UIImageView作为当前View的子视图来管理,这样布局和更新会更直观稳定。

修改后的完整代码

import Foundation
import UIKit

@IBDesignable
class PlainHorizontalProgressBar: UIView {

    @IBInspectable var color: UIColor = .gray {
        didSet { setNeedsDisplay() }
    }

    var progress: CGFloat = 0 {
        didSet { setNeedsDisplay() }
    }

    var circularShapeColor: UIColor = .blue {
        didSet { setNeedsDisplay() }
    }

    private let progressLayer = CALayer()
    private let backgroundMask = CAShapeLayer()
    private let circularShapeLayer = CAShapeLayer()

    // Create an UIImageView for the system image
    private let systemImageView = UIImageView()

    override init(frame: CGRect) {
        super.init(frame: frame)
        circularShapeColor = UIColor.systemOrange
        setupLayers()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        circularShapeColor = UIColor.systemOrange
        setupLayers()
    }

    private func setupLayers() {
        layer.addSublayer(progressLayer)
        layer.addSublayer(circularShapeLayer)
        
        // 将UIImageView作为当前View的子视图添加,而不是加到CAShapeLayer里
        addSubview(systemImageView)
        // 配置图片视图的contentMode,保证图标正确显示
        systemImageView.contentMode = .scaleAspectFit
        systemImageView.tintColor = .white
        // 先设置图片,避免后续重复设置
        systemImageView.image = UIImage(systemName: "bolt.fill")
    }

    override func draw(_ rect: CGRect) {
        backgroundMask.path = UIBezierPath(roundedRect: rect, cornerRadius: rect.height * 0.75).cgPath
        layer.mask = backgroundMask

        let progressRect = CGRect(origin: .zero, size: CGSize(width: rect.width * progress, height: rect.height))
        progressLayer.frame = progressRect
        progressLayer.backgroundColor = color.cgColor

        // Calculate the position and size of the circular shape
        let circularSize = CGSize(width: rect.height, height: rect.height)
        let circularOrigin = CGPoint(x: progressRect.maxX - circularSize.width / 2.0, y: (rect.height - circularSize.height) / 2.0)
        let circularRect = CGRect(origin: circularOrigin, size: circularSize)
        let circularPath = UIBezierPath(ovalIn: circularRect)
        
        // 设置circularShapeLayer的bounds和position,保证它的坐标系正确
        circularShapeLayer.path = circularPath.cgPath
        circularShapeLayer.bounds = circularRect
        circularShapeLayer.position = CGPoint(x: circularRect.midX, y: circularRect.midY)
        circularShapeLayer.fillColor = circularShapeColor.cgColor

        // 更新图片视图的frame,让它正好覆盖圆形图层
        systemImageView.frame = circularRect
    }
}

额外优化建议

  • 如果你的进度条需要动画更新,建议把draw(_:)里的布局逻辑移到layoutSubviews()方法里,这样在视图尺寸变化时也能正确更新布局。
  • 可以给progress的设置加上范围限制(比如progress = max(0, min(1, newProgress))),避免进度值超出0-1的范围导致布局异常。

备注:内容来源于stack exchange,提问作者Saland

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 13:53:06