如何:在iOS中使用StackView View从XIB自定义自定义视图的大小

马丁:

我最近开始再次使用Swift进行iOS开发,现在正在研究自定义UIControl。为了布局和灵活性,此自定义视图使用StackView构建。

我想实现的目标...

...基本上是将StackView的功能与我通常直接将其拖到情节提要中时所具有的功能相同-在这里它的大小可以毫无问题地调整。这本质上与自定尺寸的Tableview单元相同。

展示柜

我可以将CustomView简化为一个简单的示例:这是一个StackView,其中带有三个标签,分别Alignment设置为CenterDistribution Equal CenteringStackView将固定在左,右和顶部布局指南(或尾随,前导和顶部)。我可以轻松地使用CustomView进行重新创建,从XIB加载后,将其转换为具有相同参数的CustomView-我将两者都作为屏幕快照附加。

Simple StackView的屏幕截图

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将抱怨未知的高度。

我阅读了《自动布局指南》,并观看了WWDC视频“自动布局的奥秘”,但是对于我来说,这个话题仍然很新,我认为我没有找到正确的方法。所以这是我需要帮助的地方

(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))
}

基本上,我确实将视图拉伸到适合故事板中UIView的整个宽度,并限制了它在StackView的顶部和底部至顶部和底部。

使用XCode 8时,我遇到了很多有关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继承尺寸/高度。另外,我不能真正将StackView固定到XIB文件的IB中的容器视图中。

希望有人可以帮助我。干杯!

丹尼尔·霍尔(Daniel Hall):

以下是您问题的一些答案,尽管顺序不同:

(5)为什么我必须在代码中添加约束?

假设我禁用了translatesAutoresizingMaskIntoConstraints,那么我无法像在IB中为表格视图单元格那样设置约束吗?它将始终转换为label.top = top之类的内容,因此标签将被拉伸,而不是StackView继承尺寸/高度。另外,我不能真正将StackView固定到XIB文件的IB中的容器视图中。

在此示例中,您必须在代码中添加约束,因为要从笔尖加载任意视图,然后将其添加到已编码的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的子类,因此它将自动创建任何子视图所需的约束。在这种情况下,因为distributionalignment都设置为.fill,它将根据需要将子视图的边缘固定到其自己的边缘。

(4)为什么我必须首先这样做?

真的吗 我只将StackView移到了CustomView中。那么为什么StackView现在停止自适应,为什么我必须设置顶部和底部约束?

实际上,这里的关键问题是您StackView的alignment属性.center也就是说,StackView中的标签将垂直居中,但不会将它们限制在StackView的顶部或底部就像在视图内的子视图中添加centerY约束一样。您可以将外部视图设置为所需的任何高度,子视图将居中,但它不会“推出”外部视图的顶部和底部,因为中心约束仅约束中心,而不约束顶部和底部边缘。

在您的StackView故事板版本中,StackView之上的视图层次结构是已知的,并且还有一些选项可通过调整蒙版的大小自动生成约束。无论哪种方式,UIKit都知道最终的StackView高度,并且标签将在该高度内垂直居中,但实际上并不会影响它。您需要将StackView的对齐方式设置为.fill,或从标签的顶部和底部向StackView的顶部和底部添加其他约束,以便自动布局确定StackView的高度。

(2)还是应该重写internalContentSize?

这无济于事,因为固有内容大小本身不会推出父视图的边缘,除非子视图的边缘也被约束到父视图的边缘。无论如何,只要您解决上述.center对齐问题,您就会自动获得在这种情况下所需的所有正确的内在内容大小行为

(1)我应该设置约束并禁用translatesAutoresizingMaskIntoConstraints吗?

这通常是有效的方法。但是,如果您像我上面描述的那样继承了UIStackView,则无需执行任何操作。

综上所述

如果你:

  1. 如上例代码所示,将CustomView创建为UIStackView的子类。

  2. 在设置为自定义类的情节提要板上创建视图CustomView如果希望CustomView正确自调整大小,则需要为其赋予约束,而不要使用从调整大小掩码中自动生成的约束。

  3. 更改笔尖中的StackView,以在至少一个标签(最可能是大的中心标签)的顶部和底部与堆栈视图的顶部和底部之间进行alignment设置.fill设置其他约束

然后,自动布局应该正常工作,并且具有StackView的自定义视图应该按预期布局。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章