如何使自动布局与 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,而只是一个普通的 UIViewItemView。我收到“不等式约束歧义”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 的文件所有者是 ItemViewItemView 有一个 @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 一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

如何将值从 XIB 传递到 UIView 初始化程序?

在自包含的自定义 UIView 子类中使用 .xib 文件进行原型设计

如何使 UIScrollView 与自动布局和动态内容一起使用? [复制]

如何将额外参数传递给链接到 XIB 文件的自定义 UIView?

故事板中的自定义 UIView

使用 xib 和自动布局的自定义 UITableViewCell 无法在顶部的多个单元格中正确显示