UIBezierPath在Storyboard与模拟器中绘制正方形显示为矩形问题
问题分析与解决方案
这个问题我之前做自定义IBDesignable控件时也踩过坑,核心原因大概率是控件布局变化时的内容缩放策略,或者没有基于当前控件尺寸动态计算单元格大小,咱们逐个排查:
1. 检查控件的contentMode设置
UIKit控件默认的contentMode是.scaleToFill,当控件的frame在运行时因AutoLayout调整发生变化时,系统会直接拉伸/缩放之前绘制好的内容,而不会重新调用draw(_ rect:)方法。这就导致IB里尺寸固定时绘制的正方形,在运行时被拉伸成矩形。
解决办法:
在控件的初始化方法里(比如init(frame:)或init(coder:))添加:
self.contentMode = .redraw
这样每次控件尺寸变化时,系统都会重新调用draw(_ rect:),保证绘制的内容适配新的尺寸。
2. 避免硬编码cellSize,基于控件动态尺寸计算
如果你的cellSize是固定的硬编码值(比如let cellSize = 20.0),那当控件在运行时的宽高和IB里的设置不一致时,整个网格的总尺寸和控件尺寸不匹配,就会被拉伸变形。
正确的做法是基于控件的bounds动态计算cellSize:
override func draw(_ rect: CGRect) { let columnCount = 7 let rowCount = 7 let space: Double = 2.0 // 替换成你的间隔值 // 计算总间隔宽度/高度 let totalHorizontalSpace = space * Double(columnCount - 1) let totalVerticalSpace = space * Double(rowCount - 1) // 取控件宽高中的较小值,保证网格整体为正方形 let maxAvailableSize = min(Double(bounds.width), Double(bounds.height)) // 计算单个单元格的尺寸 let cellSize = (maxAvailableSize - max(totalHorizontalSpace, totalVerticalSpace)) / Double(max(columnCount, rowCount)) // 执行绘制逻辑 for i in 0 ..< columnCount { for j in 0 ..< rowCount { let x = Double(i) * (cellSize + space) let y = Double(j) * (cellSize + space) let rectangle = CGRect(x: x, y: y, width: cellSize, height: cellSize) let face = UIBezierPath(rect: rectangle) face.fill() } } }
这样不管控件最终的尺寸是多少,网格都会适配控件大小,每个单元格保持正方形。
3. 确认控件的AutoLayout约束是否正确
如果你的控件在运行时的宽高比不是1:1,即使绘制的是正方形,也会因为控件本身被拉伸而显示成矩形。检查一下控件的约束:
- 是否设置了宽高比为1:1的约束?
- 是否有约束强制控件拉伸成非正方形?
如果希望控件本身保持正方形,一定要给它添加宽高比1:1的约束,或者在intrinsicContentSize里返回正方形的默认尺寸:
override var intrinsicContentSize: CGSize { return CGSize(width: 170, height: 170) // 替换成你期望的默认正方形尺寸 }
内容的提问来源于stack exchange,提问作者Alex Barinov
相关产品推荐
相关产品推荐

