UIView.convert(_ point:to:)方法异常行为的排查求助
问题:UIView convert方法转换中心点坐标不符合预期
我尝试转换若干视图的中心点CGPoint以便绘制连线,但原本垂直对齐的点,convert返回的值不符合预期。复现代码如下:
class ViewController: UIViewController { private lazy var backgroundView: UIView = { let view = UIView() view.translatesAutoresizingMaskIntoConstraints = false view.backgroundColor = .systemBackground return view }() private lazy var firstView: UIView = { let view = UIView() view.translatesAutoresizingMaskIntoConstraints = false view.backgroundColor = .systemRed return view }() private lazy var secondView: UIView = { let view = UIView() view.translatesAutoresizingMaskIntoConstraints = false view.backgroundColor = .systemBlue return view }() private lazy var firstSubview: UIImageView = { let view = UIImageView() view.translatesAutoresizingMaskIntoConstraints = false view.backgroundColor = .systemMint return view }() private lazy var secondSubview: UIImageView = { let view = UIImageView() view.translatesAutoresizingMaskIntoConstraints = false view.backgroundColor = .systemGreen return view }() override func viewDidLoad() { super.viewDidLoad() view.addSubview(backgroundView) backgroundView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true backgroundView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20).isActive = true backgroundView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20).isActive = true backgroundView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true backgroundView.addSubview(firstView) backgroundView.addSubview(secondView) firstView.topAnchor.constraint(equalTo: backgroundView.topAnchor).isActive = true firstView.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor).isActive = true firstView.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor).isActive = true secondView.topAnchor.constraint(equalTo: firstView.bottomAnchor, constant: 10).isActive = true secondView.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor).isActive = true secondView.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor).isActive = true firstView.addSubview(firstSubview) firstSubview.topAnchor.constraint(equalTo: firstView.topAnchor).isActive = true firstSubview.bottomAnchor.constraint(equalTo: firstView.bottomAnchor).isActive = true firstSubview.leadingAnchor.constraint(equalTo: firstView.leadingAnchor).isActive = true firstSubview.heightAnchor.constraint(equalToConstant: 50).isActive = true firstSubview.widthAnchor.constraint(equalToConstant: 50).isActive = true secondView.addSubview(secondSubview) secondSubview.topAnchor.constraint(equalTo: secondView.topAnchor).isActive = true secondSubview.bottomAnchor.constraint(equalTo: secondView.bottomAnchor).isActive = true secondSubview.leadingAnchor.constraint(equalTo: secondView.leadingAnchor, constant: 10).isActive = true secondSubview.heightAnchor.constraint(equalToConstant: 30).isActive = true secondSubview.widthAnchor.constraint(equalToConstant: 30).isActive = true } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let firstCenter1 = firstSubview.convert(firstSubview.center, to: view) let secondCenter1 = secondSubview.convert(secondSubview.center, to: view) print(firstCenter1, secondCenter1) } }
打印结果为(45.0, 84.0) (55.0, 134.0),预期应输出(45.0, 84.0) (45.0, 134.0),因为secondSubview的相对center.x理应与firstSubview一致为45.0。
错误原因
你混淆了UIView.center的坐标系和convert(_:to:)方法的参数要求:
UIView.center属性表示的是该视图在父视图坐标系中的中心点,而非自身坐标系的点。convert(_ point: CGPoint, to view: UIView?)方法要求传入的point是调用该方法的视图自身坐标系中的点。
当你执行secondSubview.convert(secondSubview.center, to: view)时,相当于把「secondSubview在父视图中的中心点坐标」当成「secondSubview自身坐标系中的点」来转换,这自然会得到错误的结果。
另外你的代码中存在约束冲突:secondSubview同时设置了top/bottom与父视图对齐和固定height,AutoLayout会自动调整约束优先级,这也可能间接影响坐标计算。
解决方案
有两种正确的方式获取目标坐标系下的中心点:
方式1:通过父视图转换子视图的center
利用子视图的center(父坐标系中的点),让父视图执行转换:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let firstCenter1 = firstView.convert(firstSubview.center, to: view) let secondCenter1 = secondView.convert(secondSubview.center, to: view) print(firstCenter1, secondCenter1) // 输出预期的(45.0, 84.0) (45.0, 134.0) }
方式2:转换子视图自身bounds的中心点
子视图自身坐标系的中心点是bounds.center,直接用子视图转换这个点:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let firstCenter1 = firstSubview.convert(firstSubview.bounds.center, to: view) let secondCenter1 = secondSubview.convert(secondSubview.bounds.center, to: view) print(firstCenter1, secondCenter1) // 输出预期结果 }
额外修复:解决约束冲突
移除secondSubview的top/bottom约束,保留height和垂直居中约束,避免AutoLayout自动调整布局:
// 替换原secondSubview的约束代码 secondSubview.centerYAnchor.constraint(equalTo: secondView.centerYAnchor).isActive = true secondSubview.leadingAnchor.constraint(equalTo: secondView.leadingAnchor, constant: 10).isActive = true secondSubview.heightAnchor.constraint(equalToConstant: 30).isActive = true secondSubview.widthAnchor.constraint(equalToConstant: 30).isActive = true
内容的提问来源于stack exchange,提问作者Faruk
相关产品推荐
相关产品推荐

