Swift UIKit中使用相同frame绘制线条显示不一致问题求解
问题核心原因
- 生命周期时机错误:
viewDidLoad方法调用时,Storyboard中通过AutoLayout约束配置的视图还没有完成最终的布局适配,此时读取的TopView.frame是Storyboard设计时的预设尺寸,不是运行时适配当前设备的真实尺寸,后续AutoLayout更新TopView的frame后,你提前创建的贝塞尔路径不会自动同步更新。 - 坐标系使用错误:你在
Vvp函数中初始化子视图时用了inView.frame,frame是视图相对于其父视图坐标系的数值,而子视图的frame需要相对于它的直接父视图(也就是你传入的inView)的自身坐标系,应该使用inView.bounds来初始化子视图的frame,否则子视图的位置会出现偏移。
修复方案
- 将图层绘制、子视图尺寸调整的逻辑移到
viewDidLayoutSubviews中执行,这个方法会在AutoLayout完成所有视图布局后触发,此时读取的frame/bounds是运行时的正确值。注意要避免重复添加子视图和图层,建议把需要复用的图层、子视图声明为控制器的属性。 - 子视图初始化、内部坐标计算统一使用父视图的
bounds属性,不要使用frame。
修正后代码示例
import UIKit class ViewController: UIViewController { @IBOutlet weak var TopView: UIView! @IBOutlet weak var MiddleView: UIView! @IBOutlet weak var BottomView: UIView! // 声明为属性避免重复添加 private var redShapeLayer: CAShapeLayer? private var customSubview: UIView? override func viewDidLoad() { super.viewDidLoad() // 只做初始化添加,不做尺寸相关计算 let t = Vvp(inView: TopView) TopView.addSubview(t) customSubview = t let shapeLayer = CAShapeLayer() shapeLayer.strokeColor = UIColor.red.cgColor shapeLayer.fillColor = UIColor.red.cgColor shapeLayer.lineWidth = 1.0 TopView.layer.addSublayer(shapeLayer) redShapeLayer = shapeLayer } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 布局完成后再更新尺寸相关的路径、frame guard let redShapeLayer, let customSubview else { return } // 更新红色图层路径 let redBezierPath = UIBezierPath() redBezierPath.move(to: CGPoint(x: 0, y: 0)) redBezierPath.addLine(to: CGPoint(x: TopView.bounds.maxX, y: 0)) redBezierPath.close() redShapeLayer.path = redBezierPath.cgPath // 更新自定义子视图的frame和内部路径 customSubview.frame = TopView.bounds if let blueLayer = customSubview.layer.sublayers?.first as? CAShapeLayer { let blueBezierPath = UIBezierPath() blueBezierPath.move(to: CGPoint(x: 0, y: 0)) blueBezierPath.addLine(to: CGPoint(x: TopView.bounds.maxX, y: 0)) blueBezierPath.close() blueLayer.path = blueBezierPath.cgPath } } }
func Vvp(inView: UIView)-> UIView { // 用bounds初始化子视图frame let viewWithBeizer = UIView(frame: inView.bounds) let bezierPath = UIBezierPath() bezierPath.move(to: CGPoint(x: 0, y: 0)) bezierPath.addLine(to: CGPoint(x: inView.bounds.maxX, y: 0)) bezierPath.close() let shapeLayer = CAShapeLayer() shapeLayer.path = bezierPath.cgPath shapeLayer.strokeColor = UIColor.blue.cgColor shapeLayer.fillColor = UIColor.blue.cgColor shapeLayer.lineWidth = 1.0 viewWithBeizer.layer.addSublayer(shapeLayer) return viewWithBeizer }
内容的提问来源于stack exchange,提问作者Alek Szczęsnowicz
相关产品推荐
相关产品推荐

