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

图像绘制与CGRect的AspectFill实现及屏幕裁剪问题排查

解决iOS设备上圆形绘制被屏幕边缘裁剪的问题

我看了你的代码和问题描述,这个裁剪问题的根源其实很明确——你在绘制画布的时候混用了全局屏幕尺寸和目标视图的尺寸,导致绘制的内容和实际显示的容器不匹配,自然就会被裁剪。下面给你一步步分析和修复方案:

问题核心分析

  1. 在getImageWithColor函数中,你用全局的screenWidth和screenHeight创建了绘制矩形,但实际上应该使用传入的size参数(也就是imgOverlay.bounds.size)。这会导致绘制的画布尺寸和目标视图的尺寸不一致,超出部分就会被裁剪。
  2. 文本绘制部分用了硬编码的CGRect(x: 12, y: 38, width: 448, height: 448),这种固定值在不同尺寸的设备上肯定会出问题,要么被裁剪要么位置不对。

修复后的代码

1. 修正getImageWithColor函数

func getImageWithColor(color: UIColor, size: CGSize) -> UIImage {
    // 改用传入的size创建矩形,而不是全局screenWidth/screenHeight
    let rect = CGRect(origin: .zero, size: size)
    UIGraphicsBeginImageContextWithOptions(size, false, 0)
    color.setFill()
    UIRectFill(rect)
    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    return image
}

2. 修正drawCirclesOnImage函数

主要是确保所有尺寸计算基于目标视图的尺寸,同时修复文本绘制的矩形:

func drawCirclesOnImage(fromImage: UIImage? = nil, targetSize: CGSize) -> UIImage? {
    if fromImage == nil && targetSize == CGSize.zero {
        return nil
    }
    
    var tmpimg: UIImage?
    if targetSize == CGSize.zero {
        tmpimg = fromImage
    } else {
        tmpimg = getImageWithColor(color: UIColor.clear, size: targetSize)
    }
    
    guard let img = tmpimg else { return nil }
    
    let imageSize = img.size
    let scale: CGFloat = 0
    UIGraphicsBeginImageContextWithOptions(imageSize, false, scale)
    img.draw(at: CGPoint.zero)
    
    let w = imageSize.width
    let h = imageSize.height
    print("Width 1: \(w) Height 1: \(h)")
    
    let midX = w / 2
    let midY = h / 2
    
    // red circles - radius in %
    let circleRads = [0.07, 0.13, 0.17, 0.22, 0.29, 0.36, 0.40, 0.48, 0.60, 0.75]
    
    // center "dot" - radius is 1.5%
    var circlePath = UIBezierPath(arcCenter: CGPoint(x: midX,y: midY),
                                  radius: CGFloat(w * 0.010),
                                  startAngle: 0,
                                  endAngle: CGFloat(Double.pi * 2),
                                  clockwise: true)
    UIColor.white.setFill()
    circlePath.stroke()
    circlePath.fill()
    
    lineColor = getLineColor(color: color)
    lineColor?.setStroke()
    
    for pct in circleRads {
        let rad = w * CGFloat(pct)
        circlePath = UIBezierPath(arcCenter: CGPoint(x: midX, y: midY),
                                  radius: rad,
                                  startAngle: 0,
                                  endAngle: CGFloat(Double.pi * 2),
                                  clockwise: true)
        circlePath.lineWidth = 2.5
        circlePath.stroke()
    }
    
    // 修复文本绘制:基于目标尺寸计算位置,避免硬编码
    let now = Date()
    let formatter = DateFormatter()
    formatter.timeZone = TimeZone.current
    formatter.dateFormat = "yyyy-MM-dd HH:mm"
    let dateString = formatter.string(from: now)
    
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.alignment = .center
    
    let attrs = [
        NSAttributedString.Key.font: UIFont(name: "HelveticaNeue-Thin", size: 26)!,
        NSAttributedString.Key.paragraphStyle: paragraphStyle
    ]
    
    // 让文本在底部居中显示,基于imageSize计算位置
    let textSize = dateString.size(withAttributes: attrs)
    let textRect = CGRect(
        x: (w - textSize.width) / 2,
        y: h - textSize.height - 20, // 底部留20pt边距
        width: textSize.width,
        height: textSize.height
    )
    dateString.draw(with: textRect, options: .usesLineFragmentOrigin, attributes: attrs, context: nil)
    
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage
}

额外说明

  • 确保你调用drawCirclesOnImage时传入的targetSize是imgOverlay.bounds.size,这个值已经是适配当前设备的视图尺寸,不需要再用全局屏幕尺寸。
  • 如果你之前定义了screenWidth/screenHeight这类全局变量,建议尽量避免在视图绘制中使用,而是直接使用当前视图的bounds.size来保证适配性。

附上你提供的iPad模拟器截图:
iPad模拟器中圆形叠加画面

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:57:28