我最近再次开始使用 Swift 进行 iOS 开发,现在正在研究自定义 UIControl。为了布局和灵活性,这个自定义视图是使用 StackView 构建的。
我想要达到的...
... 基本上是简单地与 StackView 具有相同的功能,我通常将它直接拖到情节提要中时 - 它调整大小以适应没有问题。这与自调整大小的 tableview 单元格基本相同。
展示柜
我可以将我的 CustomView 简化为一个简单的示例:它是一个 StackView,其中包含三个标签,Alignment
设置为Center
, Distribution
Equal Centering
。StackView 将固定到左、右和顶部布局指南(或尾随、前导和顶部)。我可以使用从 XIB 加载的 CustomView 轻松地重新创建它,并将其转换为具有相同参数的 CustomView - 我将两者都附加为屏幕截图。
加载 XIB 文件并进行设置。
import UIKit
@IBDesignable
class CustomView: UIView {
// loaded from NIB
private weak var view: UIView!
convenience init() {
self.init(frame: CGRect())
}
override init(frame: CGRect) {
super.init(frame: frame)
self.loadNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.loadNib()
}
override func layoutSubviews() {
super.layoutSubviews()
// we need to adjust the frame of the subview to no longer match the size used
// in the XIB file BUT the actual frame we got assinged from the superview
self.view.frame = bounds
}
private func loadNib () {
self.view = Bundle (for: type(of: self)).loadNibNamed(
"CustomView", owner: self, options: nil)! [0] as! UIView
self.addSubview(self.view)
}
}
您可以在这里看到的是,我只是加载了 XIB 文件,覆盖布局子视图以使框架适应实际视图,仅此而已。
问题
所以我的问题与通常如何处理有关。我的 StackView 中的标签具有内在的内容大小,即通常 StackView 只是适应标签的高度而不会抱怨 - 如果您在情节提要中设计它。但是,如果您将所有这些都放在 XIB 中并保持布局不变,则不会 - 如果视图以三种方式固定在屏幕截图中(顶部、前导、尾随),则 IB 会抱怨未知高度。
我阅读了 Auto-Layout Guide 并观看了 WWDC 视频“AutoLayout 的奥秘”,但这个话题对我来说还是很新的,我认为我还没有找到正确的方法。所以这是我需要帮助的地方
(1) 我应该设置约束并禁用translatesAutoresizingMaskIntoConstraints
吗?
我首先可以并且应该做的是设置 translatesAutoresizingMaskIntoConstraints
false
这样我不会得到自动生成的约束并定义我自己的约束。所以我可以loadNib
变成这样:
private func loadNib () {
self.view = Bundle (for: type(of: self)).loadNibNamed(
"CustomView", owner: self, options: nil)! [0] as! UIView
self.view.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.view)
self.addConstraint(NSLayoutConstraint (
item: self
, attribute: .bottom
, relatedBy: .equal
, toItem: self.view
, attribute: .bottom
, multiplier: 1.0
, constant: 0))
self.addConstraint(NSLayoutConstraint (
item: self
, attribute: .top
, relatedBy: .equal
, toItem: self.view
, attribute: .top
, multiplier: 1.0
, constant: 0))
self.addConstraint(NSLayoutConstraint (
item: self.view
, attribute: .leading
, relatedBy: .equal
, toItem: self
, attribute: .leading
, multiplier: 1.0
, constant: 0))
self.addConstraint(NSLayoutConstraint (
item: self.view
, attribute: .trailing
, relatedBy: .equal
, toItem: self
, attribute: .trailing
, multiplier: 1.0
, constant: 0))
}
基本上,我确实拉伸了视图以适应 Storyboard 中 UIView 的整个宽度,并限制了它的顶部和底部到 StackView 的顶部和底部。
我在使用这个时遇到了很多 XCode 8 崩溃的问题,所以我不太确定该怎么做。
(2) 还是我应该覆盖intrinsicContentSize
?
我也可以intrinsicContentSize
通过检查所有我的高度来简单地覆盖arrangedSubviews
并取最高的那个,将我的视图高度设置为它:
override var intrinsicContentSize: CGSize {
var size = CGSize.zero
for (_ , aSubView) in (self.view as! UIStackView).arrangedSubviews.enumerated() {
let subViewHeight = aSubView.intrinsicContentSize.height
if subViewHeight > size.height {
size.height = aSubView.intrinsicContentSize.height
}
}
// add some padding
size.height += 0
size.width = UIViewNoIntrinsicMetric
return size
}
虽然这肯定给了我更大的灵活性,但我还必须使内在内容大小无效,检查动态类型和许多其他我宁愿避免的东西。
(3)我什至以“建议”的方式加载 XIB 吗?
还是有更好的方法,例如使用UINib.instantiate
? 我真的找不到最佳实践方法来做到这一点。
(4) 为什么我必须首先这样做?
真的,为什么?我只是将 StackView 移到了 CustomView 中。那么为什么StackView现在就停止自适应了,为什么还要设置上下约束呢?
到目前为止,帮助最大的是https://stackoverflow.com/a/24441368中的关键短语:
“一般来说,自动布局是以自上而下的方式执行的。换句话说,首先执行父视图布局,然后执行任何子视图布局。”
..但我真的不太确定。
(5) 为什么我必须在代码中添加约束?
假设我禁用translatesAutoresizingMaskIntoConstraints
了,那么我不能为 IB 中的 tableview 单元格设置约束?它总是会转换为 label.top = top 之类的东西,因此标签会被拉伸,而不是 StackView 继承大小/高度。此外,对于 XIB 文件,我也无法真正将 StackView 固定到 IB 中的容器视图。
希望有人可以帮助我。干杯!