我最近又开始使用Swift进入iOS开发阶段,现在正在研究定制的UIControl。对于布局和灵活性,此自定义视图使用StackView构建。
...基本上只是具有与StackView相同的功能,当我将它直接拖入故事板时我通常拥有它 - 在那里它调整大小以适应没有问题。这与自我调整tableview单元基本相同。
我可以将我的CustomView简化为一个简单示例:它是一个包含三个标签的StackView,Alignment
设置为Center
,Distribution
Equal Centering
。 StackView将固定到左,右和顶部布局指南(或尾随,前导和顶部)。我可以轻松地使用CustomView重新创建,从XIB加载转换为具有相同参数的CustomView - 我将它们作为屏幕截图附加。
Screenshot of Simple StackView
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将会抱怨未知高度。
我阅读了自动布局指南,并观看了WWDC视频“AutoLayout之谜”,但这个话题对我来说还是一个新鲜事,我认为我还没有找到正确的方法。所以这就是我需要帮助的地方
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))
}
基本上我确实拉伸视图以适应故事板中我的UIView的整个宽度,并限制它的顶部和底部到StackView的顶部和底部。
在使用XCode 8时,我遇到了很多问题,所以我不太清楚如何制作它。
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
}
虽然这确实给了我更大的灵活性,但我还必须使内在内容大小无效,检查动态类型以及我宁愿避免的许多其他内容。
或者有更好的方法,例如使用UINib.instantiate
?我真的找不到最好的方法来做到这一点。
真的,为什么?我只将StackView移动到CustomView中。那么为什么StackView现在不再适应自己,为什么我必须设置顶部和底部约束?
迄今为止最有帮助的是https://stackoverflow.com/a/24441368中的关键词:
“一般来说,自动布局是以自上而下的方式进行的 单词,首先执行父视图布局,然后执行任何子视图 视图布局已执行。“
..但我真的不太确定。
假设我禁用了translatesAutoresizingMaskIntoConstraints
,那么我不能像IB中的tableview单元那样设置约束?它总是会转换成类似label.top = top的东西,因此标签会被拉伸,而不是继承大小/高度的StackView。另外,我无法将StackView固定到IB的XIB文件的容器视图中。
希望有人可以帮助我。干杯!
答案 0 :(得分:10)
以下是您的问题的一些答案,但顺序不同:
(5)为什么我必须在代码中添加约束?
假设我禁用了translatesAutoresizingMaskIntoConstraints,那么我 不能像IB中的tableview单元那样设置约束?它永远都是 转换成类似label.top = top的东西,因此标签会 被拉伸而不是继承大小/高度的StackView。也 我无法将StackView固定到IB的容器视图中 XIB文件。
在此示例中,您必须在代码中添加约束,因为您从nib加载任意视图,然后将其添加到编码的CustomView
中。笔尖中的视图没有预先知道它将被置于什么上下文或层次结构中,因此它可以提前定义如何将自己约束到未知的未来超视图。对于故事板上的StackView或原型单元内的情况,该上下文及其父视图都是已知的。
对于这种情况,您可以考虑的一个不错的选择,如果您不想手动编写约束,则使您的CustomView子类UIStackView而不是UIView 。由于UIStackView会自动生成适当的约束,因此它将为您节省手动编码。有关示例代码,请参阅下一个答案。
(3)我是否将XIB加载到"建议"方式?
或者有更好的方法,例如通过使用UINib.instantiate?我真的 无法找到最佳实践方法。
我不知道"标准"或"对"处理将笔尖加载到自定义视图中的方法。我已经看到各种方法都有效。但是,我会注意到您在示例代码中所做的工作超出了所需的工作量。这是实现相同结果的更简单方法,但也使用UIStackView子类而不是UIView子类(如上所述):
class CustomView: UIStackView {
required init(coder: NSCoder) {
super.init(coder: coder)
distribution = .fill
alignment = .fill
if let nibView = Bundle.main.loadNibNamed("CustomView", owner: self, options: nil)?.first as? UIView {
addArrangedSubview(nibView)
}
}
}
请注意,因为CustomView现在是UIStackView的子类,所以它将自动创建任何子视图所需的约束。在这种情况下,由于distribution
和alignment
都设置为.fill
,因此会根据需要将子视图的边缘固定到自己的边缘。
(4)为什么我必须首先这样做?
真的,为什么?我只将StackView移动到CustomView中。那么为什么呢 StackView现在停止适应自己,为什么我必须设置顶部 和底部约束?
实际上,这里的关键问题是您的StackView alignment
.center
属性.center
。这就是说StackView中的标签将垂直居中,但它不会将它们限制在StackView的顶部或底部。这就像在视图内的子视图中添加centerY约束一样。您可以将外部视图设置为您想要的任何高度,子视图将居中,但它不会#34;推出"外部视图的顶部和底部,因为中心约束仅限制中心,而不是顶部和底部边缘。
在StackView的storyboard版本中,StackView上方的视图层次结构是已知的,并且还有一些选项可以通过调整大小掩码自动生成约束。无论哪种方式,UIKit都知道最终的StackView高度,并且标签将在该高度内垂直居中,但实际上不会影响它。 您需要将StackView的对齐方式设置为.fill,或者将标签顶部和底部的其他约束添加到StackView的顶部和底部,以便自动布局以确定StackView应该有多高是强>
(2)或者我应该覆盖intrinsicContentSize?
这不会有太大帮助,因为内在内容大小本身不会推出父视图的边缘,除非子视图的边缘也被约束到父视图的边缘。在任何情况下,只要您解决上述CustomView
对齐问题,您就会自动获得所需的所有正确的内在内容大小行为。
(1)我应该设置约束并禁用translatesAutoresizingMaskIntoConstraints吗?
这通常是一种有效的方法。如果您按照上面的描述继承UIStackView,那么您也不需要这样做。
如果你:
创建您的CustomView作为UIStackView的子类,如上面的示例代码所示
在自定义课程alignment
的故事板上创建视图。如果希望CustomView正确地自我调整大小,则需要为其设置约束,而不是使用调整大小掩码中自动生成的约束。
更改笔尖中的StackView,将.fill
设置为{{1}},或者在至少一个标签的顶部和底部之间设置其他约束(很可能是大的)中心一)和堆栈视图的顶部和底部
然后自动布局应该正常工作,使用StackView的自定义视图应该按预期布局。