图像绘制与CGRect的AspectFill实现及屏幕裁剪问题排查
解决iOS设备上圆形绘制被屏幕边缘裁剪的问题
我看了你的代码和问题描述,这个裁剪问题的根源其实很明确——你在绘制画布的时候混用了全局屏幕尺寸和目标视图的尺寸,导致绘制的内容和实际显示的容器不匹配,自然就会被裁剪。下面给你一步步分析和修复方案:
问题核心分析
- 在
getImageWithColor函数中,你用全局的screenWidth和screenHeight创建了绘制矩形,但实际上应该使用传入的size参数(也就是imgOverlay.bounds.size)。这会导致绘制的画布尺寸和目标视图的尺寸不一致,超出部分就会被裁剪。 - 文本绘制部分用了硬编码的
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模拟器截图:
内容的提问来源于stack exchange,提问作者Harry McGovern
相关产品推荐
相关产品推荐

