@IBDesignable自定义视图在Storyboard中渲染位置错误问题排查
问题原因
- 核心问题:
layoutAttachAll方法的约束逻辑完全写反,参数命名错误进一步引发调用逻辑混乱
你调用contentView.layoutAttachAll(to: self)的预期是将nib加载得到的子视图contentView贴满父视图self,但实际方法实现存在三处错误:- 参数名语义错误:你传入的
self是父视图,却被命名为childView,进一步加剧逻辑混淆 - 约束对象写反:你将父视图的四边对齐子视图的四边,而不是子视图的四边对齐父视图的四边,约束逻辑完全倒置。IB预览时nib默认大小的子视图会强制约束父视图的位置,最终表现为子视图错位到左上角
- 约束添加位置不规范:跨视图的约束应该添加到二者的公共父视图(也就是当前的父视图
self),你的逻辑因为约束倒置,给父视图添加了错误的约束。
- 参数名语义错误:你传入的
- 次要问题:
prepareForInterfaceBuilder方法为空,没有触发IB预览时的布局更新
IB预览自定义视图时会调用prepareForInterfaceBuilder,你当前的空实现没有主动触发布局,即使约束修正后也可能出现预览不更新的问题。
运行时看起来正常是因为运行时视图生命周期中,父视图的大小会先被UIKit确定,错误的约束在运行时因为布局刷新的时序被暂时覆盖,本质上约束逻辑本身的问题一直存在,IB预览时时序不同就暴露了问题。
修复方案
- 修正
layoutAttachAll方法的逻辑和参数命名:
extension UIView { func layoutAttachAll(to parentView: UIView) { var constraints = [NSLayoutConstraint]() // 调用方法的对象是子视图,对齐父视图parentView的四边 constraints.append(NSLayoutConstraint(item: self, attribute: .left, relatedBy: .equal, toItem: parentView, attribute: .left, multiplier: 1.0, constant: 0)) constraints.append(NSLayoutConstraint(item: self, attribute: .right, relatedBy: .equal, toItem: parentView, attribute: .right, multiplier: 1.0, constant: 0)) constraints.append(NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: parentView, attribute: .top, multiplier: 1.0, constant: 0)) constraints.append(NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: parentView, attribute: .bottom, multiplier: 1.0, constant: 0)) // 约束添加到公共父视图 parentView.addConstraints(constraints) } }
- 给
AuthInputView的prepareForInterfaceBuilder添加触发布局的代码:
override func prepareForInterfaceBuilder() { super.prepareForInterfaceBuilder() layoutIfNeeded() }
内容的提问来源于stack exchange,提问作者Whirlwind
相关产品推荐
相关产品推荐

