如何使自动布局与 XIB 中的自定义 UIView 一起使用?
Posted
技术标签:
【中文标题】如何使自动布局与 XIB 中的自定义 UIView 一起使用?【英文标题】:How to make autolayout work with custom UIView on XIB? 【发布时间】:2018-12-19 21:23:46 【问题描述】:我在 XIB 上有一个 UIView
,其中包含一个 UIImageView
和一个 UILabel
,两者之间有一个小空格。水平布局是一个简单的链式|--image--label--|
,其中--
是一些固定空间。高度固定为 40,此视图在其视图控制器中水平居中,并且具有>=
100 宽度约束。
如果我更改标签文本,我的组合视图的宽度会按照预期宽度更新其标签的更改宽度,并且它很好地保持在视图控制器的中心。
因为我需要这个包含图像和标签的UIView
,在其他地方,我创建了一个由 XIB 和 Swift 文件组成的自定义类。我们就叫它ItemView
吧。
我遇到的问题是视图控制器 XIB 上的空 UIView
(我已将其更改为 ItemView
)不再接受 >=
40 宽度约束。这当然是因为视图控制器 XIB 不再看到变量宽度 UILabel
,而只是一个普通的 UIView
类 ItemView
。我收到“不等式约束歧义”IB 错误。
结果是我的自定义视图的宽度仍然是 40。如果我指定更大的 >= 标签宽度,它会起作用一点;文本仅在达到此宽度时才会被切断。但在第二种情况下,我的自定义视图不再水平居中,而是向左移动了一点。
我该如何解决这个问题?或者,我如何告诉 IB 以与 UILabel
类似的方式对待我的自定义 ItemView
?
在我的ItemView
中,我已尽我所能:
override class var requiresConstraintBasedLayout: Bool
return true
设置标签文字后调用setNeedsLayout()
。
致电myLabel.translatesAutoresizingMaskIntoConstraints = false
。
致电self.translatesAutoresizingMaskIntoConstraints = false
。
并在两个init()
s 中调用self.setNeedsUpdateConstraints()
。
【问题讨论】:
为什么不关闭这个视图的警告呢?您可以简单地告诉 IB 不要检查此视图的自动布局有效性。 您是否尝试将自定义 XIB 用作@IBDesignable
,而此时您会收到错误/警告?
@DonMag 这是一个 IB 布局错误。我没有指定@IBDesignable
。
【参考方案1】:
在视图的大小检查器中配置此弹出窗口:
现在 IB 不会担心您的尺寸规格。你比 IB 更清楚,这就是告诉 IB 这个事实的方法。
另一种方式:在视图的 Size 检查器中配置 this 弹出窗口:
这告诉 IB 视图将具有它不知道的固有内容大小。
其中任何一个都可以。正如您在此屏幕截图中看到的,我为自定义视图设置了大于或等于 40 的宽度约束,但 IB 没有抱怨任何错误:
【讨论】:
这确实消除了错误,但我的自定义UIView
根本没有更新它的宽度。
不,当然不是。怎么可能?直到运行时它才知道宽度。但是您仍然可以对其应用自动布局关系,如果您正确配置,一切都会在运行时运行良好。不过,那是另外一回事!您问在 IB 中使用占位符视图时如何消除警告,我回答了。
我已经用更多细节更新了这个问题。为发展中的“故事”道歉。然而,我的问题从一开始就是“如何使自动布局工作”。
很明显,您需要自定义视图来实现instrinsicContentSize
,使其根据标签自行调整大小。没有?
instrinsicContentSize
似乎是关键。但是为什么自动布局在这里不起作用,我需要手动计算?我至少可以要求我的自定义视图根据它的约束来计算它的宽度吗?我显然错过了一些东西。【参考方案2】:
我如何构建我的自定义 UIView
:我的 XIB 的文件所有者是 ItemView
。 ItemView
有一个 @IBOutlet var: UIView!
连接到 XIB 中的视图。然后我有:
init()
super.init(frame: .zero)
self.initView()
required init?(coder aDecoder: NSCoder)
super.init(coder: aDecoder)
self.initView()
private func initView()
Bundle.main.loadNibNamed("ItemView", owner: self, options: nil)
self.view.frame = CGRect(x: 0.0, y: 0.0, width: self.width, height: self.height)
self.addSubview(self.view!)
(在使用 XIB 创建自定义 UIView
时,我似乎需要这个额外的 UIView
。很想知道它是否根本不需要。)
为了让它工作,我需要在更新标签后致电self.invalidateIntrinsicContentSize()
。并覆盖intrinsicContentSize
:
override open var intrinsicContentSize: CGSize
return self.view.systemLayoutSizeFitting(self.bounds.size)
请注意,我是在 self.view
上调用这个,而不是在 self
上。
感谢@matt 为我指明了正确的方向。这是我第一次遇到这样的事情。
我尝试过的以下事情我们都没有必要:
override class var requiresConstraintBasedLayout: Bool
return true
设置标签文字后调用setNeedsLayout()
。
致电myLabel.translatesAutoresizingMaskIntoConstraints = false
。
致电self.translatesAutoresizingMaskIntoConstraints = false
。
并在两个init()
s 中调用self.setNeedsUpdateConstraints()
。
【讨论】:
以上是关于如何使自动布局与 XIB 中的自定义 UIView 一起使用?的主要内容,如果未能解决你的问题,请参考以下文章
在自包含的自定义 UIView 子类中使用 .xib 文件进行原型设计
如何使 UIScrollView 与自动布局和动态内容一起使用? [复制]