最近我重新开始学习使用Swift进行iOS开发,并且现在正在研究自定义UIControl。为了布局和灵活性,此自定义视图是使用StackView构建的。
我想要实现的目标是...
...基本上只是希望使用StackView时与将其直接拖入Storyboard中相同的功能-可以轻松调整大小。这本质上与自适应大小的表格视图单元格相同。
演示
我可以将我的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文件,覆盖了layoutSubviews以适应实际视图的框架,就是这样。
问题:
我的问题与通常的方法有关。我的StackView中的标签具有固有内容大小,即通常情况下,如果您在Storyboard中设计它,则StackView会根据标签的高度自适应而不会抱怨。但是,如果您将所有内容放入XIB并将布局保留为原样,则IB将抱怨未知的高度,如屏幕截图所示(顶部,前导,尾随)。我阅读了"Auto-Layout Guide"并观看了WWDC视频"Mysteries of 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
?
我也可以通过检查所有arrangedSubviews
的高度并取最高的一个来简单地重写intrinsicContentSize
,并将我的视图高度设置为它:
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://dev59.com/8WAf5IYBdhLWcg3woj7N#24441368中的关键短语:
“一般来说,自动布局是从上到下执行的。换句话说,首先执行父视图布局,然后执行任何子视图布局。”
..但我真的不太确定。
(5) 为什么我必须在代码中添加约束?
假设我禁用了translatesAutoresizingMaskIntoConstraints
,那么我不能像在IB中的tableview cells那样设置约束吗?它将总是转换为类似于label.top = top这样的东西,从而标签会被拉伸,而不是StackView继承大小/高度。此外,我无法真正将StackView固定到其XIB文件中的容器视图。
希望有人能帮助我。干杯!