以编程方式添加带有约束的多个子视图会引发异常

时间:2016-10-16 06:34:18

标签: ios swift swift3 ios10

环境

  • 的Xcode-8
  • 夫特-3
  • IOS的​​10

意图

    germain意图是将三个子视图添加到主视图中,使得它们从屏幕顶部开始依次相邻。
    还有更多的最终意图,但这就是我目前陷入困境的地方。

工作代码

    如果我只是添加一个子视图,它可以使用以下代码(略有删节,添加行号以供参考):

01: class ViewController: UIViewController, UITextFieldDelegate {
02:    var textMatte: UIView!

03:    override func viewDidLoad() {
04:        super.viewDidLoad()
05:        setupTextfieldMatte(inView: view)
06:    }

07:    func setupTextfieldMatte(inView: UIView) {
08:        textMatte = UIView()
09:        textMatte.backgroundColor = UIColor.lightGray
10:        textMatte.translatesAutoresizingMaskIntoConstraints = false
11:        inView.addSubview(textMatte)
12:        let tmGuide = textMatte.layoutMarginsGuide
13:        let inGuide = inView.layoutMarginsGuide
14:        tmGuide.topAnchor.constraint(equalTo: inGuide.topAnchor, constant: 40).isActive = true
15:        tmGuide.leadingAnchor.constraint(equalTo: inGuide.leadingAnchor, constant: 20).isActive = true
16:        tmGuide.trailingAnchor.constraint(equalTo: inGuide.trailingAnchor, constant: -20).isActive = true
17:        tmGuide.heightAnchor.constraint(equalToConstant: 40).isActive = true
18:    }
19:    //... other code ...
20: }

代码破碎

    我想从约束步骤中分解创建步骤,以便做“批处理”处理(因为还有更多需要发生的事情,因为最终这些子视图也会有自己的子视图......)。添加了相对行号,以便于进一步描述和(希望)您的回答:

01: class ViewController: UIViewController, UITextFieldDelegate {
02:     var mattes = [UIView()]

03:     override func viewDidLoad() {
04:         super.viewDidLoad()
05:         for (index, title) in ["A", "B", "C"].enumerated() { // genericized for posting
06:            let v = createView(idx: index)
07:             mattes.append(v)
08:             view.addSubview(v)
09:         }
10:         _ = mattes.popLast() //from testing/debugging found that there was an extraneous entry in the array
11:         addViewAnnotations(views: mattes, inView: self.view)
12:     }

13:     func createView(idx: Int) -> UIView {
14:         let v = UIView()
15:         v.backgroundColor = UIColor.lightGray
16:         v.translatesAutoresizingMaskIntoConstraints = false
17:         v.tag = idx
18:         return v
19:     }

20:     func addViewAnnotations(views: [UIView], inView: UIView) {
21:         let inGuide = inView.layoutMarginsGuide
22:         for (index, v) in views.enumerated() {
23:             let myGuide = v.layoutMarginsGuide
24:             if index == 0 {
25:                 myGuide.topAnchor.constraint(equalTo: inGuide.topAnchor, constant: 40).isActive = true
***                 //^^^ libc++abi.dylib: terminating with uncaught exception of type NSException
26:             }
27:             else {
28:                 myGuide.topAnchor.constraint(equalTo: views[index-1].layoutMarginsGuide.bottomAnchor, constant: 10).isActive = true
29:             }
30:             myGuide.leadingAnchor.constraint(equalTo: inGuide.leadingAnchor, constant: 20).isActive = true
31:             myGuide.trailingAnchor.constraint(equalTo: inGuide.trailingAnchor, constant: -20).isActive = true
32:             myGuide.heightAnchor.constraint(equalToConstant: 40).isActive = true
33:         }
34:     }
35:     //... other code ...
36: }
    错误发生在第25行(该行下面的注释中的错误消息),使用行上方和下方的print语句进行验证。
    没有其他错误/调试信息我可以看到
    我原以为可能在第28行中断,但25(在“破码”中)与第14行相同(在工作代码中)
    注意: 如果我从行中放入try,Xcode告诉我:“在'try'表达式中没有调用抛出函数”

我有一种感觉,这是在修改代码时经常出现的那种愚蠢的疏忽之一,但我似乎无法发现问题的根源 - 所以我希望你们其中一人可以。

更新2016-10-16

    根据我添加的第一条评论中描述的修改,我更多地使用了代码并发现了问题仅在尝试使[现在]最后一个约束活动时发生:

001:    override func viewDidLoad() {
002:        super.viewDidLoad()

003:        for (index, title) in ["Small Blind", "Big Blind", "Ante"].enumerated() {
004:            let v = createView(idx: index)
005:            mattes.append(v)
006:            view.addSubview(v)
007:            let myGuide = v.layoutMarginsGuide
008:            let inGuide = view.layoutMarginsGuide
009:            myGuide.leadingAnchor.constraint(equalTo: inGuide.leadingAnchor, constant: 20 ).isActive = true
010:            myGuide.trailingAnchor.constraint(equalTo: inGuide.trailingAnchor, constant: -20).isActive = true
011:            myGuide.heightAnchor.constraint(equalToConstant: 40).isActive = true
012:            if index == 0 {
013:                myGuide.topAnchor.constraint(equalTo: inGuide.topAnchor, constant: 40).isActive = true
014:            }
015:            else {
016:                let x = myGuide.topAnchor.constraint(equalTo: mattes[index-1].bottomAnchor, constant: 10)
017:                x.isActive = true
                    //^^^ libc++abi.dylib: terminating with uncaught exception of type NSException
018:            }
019:        }
020:    }
    所有其他约束都没有问题。如果我使用它似乎没关系:
    • mattes[index-1].bottomAnchor
    • mattes[index-1].layoutMarginsGuide.bottomAnchor
    第一个评估为{{1第二个到<NSLayoutYAxisAnchor:0x174076540 "UIView:0x100b0d380.bottom">如果重要?
    如果我注释掉第017行,代码会运行,但是当第一个矩形位于正确的位置时,剩下的两个不是 - 它们与屏幕顶部齐平而不是在顶部指示器之下。
    所以 - 也许有更好的方法来设置它们?

2 个答案:

答案 0 :(得分:0)

例外的描述是什么?在添加约束之前,请务必将视图添加到其超级视图(在同一视图层次结构中)。您不能在不属于同一层次结构的视图之间创建约束。

在您的更新中,您使用index-1作为mattes数组的索引,这可能是第一次出现异常的原因。

我不知道这是否会导致问题,但我不会从一个视图的layoutMarginsGuide创建约束到superview的layoutMarginsGuide。将该指南视为视图及其子视图。所以约束条件是从子视图本身(不是它的边距)到superview的layoutMarginsGuide。

答案 1 :(得分:0)

我认为这是造成问题的一系列因素,但首先让我展示一下工作代码和一些屏幕截图

importStatus.setCellFactory(tc -> new TableCell<Contact, Boolean>() {
    @Override
    protected void updateItem(Boolean item, boolean empty) {
        super.updateItem(item, empty);
        setText(empty ? null :
            item.booleanValue() ? "imported" : "new");
    }
});

有趣的是,虽然来自模拟器的图像正确处理横向模式,但当我在iPhone 6s上运行它时 - 当我以横向模式握住手机时它不会旋转 - 但我并不太担心关于此,因为我正在处理的应用程序仅用于纵向模式。

现在,我认为这些作品与原始代码有很大不同:
注意: 我不知道为什么,但由于某种原因,我最初将该函数命名为处理约束&#34; Annotations&#34;,这已在我当前的工作代码中修复

<小时/> BROKEN: 001: class ViewController: UIViewController, UITextFieldDelegate { 002: var textfields: [UITextField] = [] 003: var labels: [UILabel] = [] 004: var mattes: [UIView] = [] 005: override func viewDidLoad() { 006: super.viewDidLoad() 007: view.backgroundColor = UIColor.blue 008: for title in ["A", "B", "C"] { 009: let matte = setupMatte(inView: view) 010: let textfield = setupTextfield(inView: view, title: title) 011: let label = setupLabel(inView: view, title: title) 012: mattes.append(matte) 013: textfields.append(textfield) 014: labels.append(label) 015: } 016: addConstraints(inView: view) // could probably leave off parameter, but for now... 017: } 018: func setupMatte(inView: UIView) -> UIView { 019: let matte = UIView() //...various view attribute configurations... 020: matte.translatesAutoresizingMaskIntoConstraints = false 021: inView.addSubview(matte) 022: return matte 023: } 024: func setupTextfield(inView: UIView, title: String) -> UITextField { 025: let textfield = UITextField() //...various textfield attribute configurations... 026: textfield.translatesAutoresizingMaskIntoConstraints = false 027: inView.addSubview(textfield) 028: return textfield 029: } 030: func setupLabel(inView: UIView, title: String) -> UILabel { 031: let label = UILabel() //...various label attribute configurations... 032: label.translatesAutoresizingMaskIntoConstraints = false 033: inView.addSubview(label) 034: return label 035: } 036: func addConstraints(inView: UIView) { 037: for (index, matte) in mattes.enumerated() { 038: let textfield = textfields[index] 039: let label = labels[index] 040: matte.leadingAnchor.constraint(equalTo: inView.leadingAnchor).isActive = true 041: matte.trailingAnchor.constraint(equalTo: inView.trailingAnchor).isActive = true 042: matte.heightAnchor.constraint(equalToConstant: 50).isActive = true 043: label.leadingAnchor.constraint(equalTo: matte.leadingAnchor, constant: 20).isActive = true 044: label.widthAnchor.constraint(equalToConstant: 150).isActive = true 045: textfield.leadingAnchor.constraint(equalTo: label.trailingAnchor, constant: 5).isActive = true 046: textfield.trailingAnchor.constraint(equalTo: matte.trailingAnchor, constant: -20).isActive = true 047: if index == 0 { 048: matte.topAnchor.constraint(equalTo: inView.topAnchor, constant: 20).isActive = true 049: } 050: else { 051: matte.topAnchor.constraint(equalTo: mattes[index - 1].bottomAnchor, constant: 5).isActive = true 052: } 053: textfield.centerYAnchor.constraint(equalTo: matte.centerYAnchor).isActive = true 054: label.centerYAnchor.constraint(equalTo: textfield.centerYAnchor).isActive = true 055: } 056: } //... other code ... 057: }
工作: var mattes = [UIView()]

我认为以上可能存在显着差异

<小时/> BROKEN: var mattes: [UIView] = []功能循环:
  • 创建子视图
  • 附加到viewDidLoad数组
  • 的子视图
  • 被叫mattes
  • 循环后:
  • 弹出view.addSubview(v)数组
  • 的最后一个元素
  • 添加了注释

工作: mattes功能循环:

  • 创建子视图(viewDidLoad
    • 名为matte的创建过程(其中inView.addSubview(matte)inViewview相同)
  • 附加到self.view数组
  • 的子视图
  • 循环后:
  • 添加了注释

我认为定义mattes的方式的不同消除了在添加约束之前弹出数组中的最后一个(或任何)元素的需要

<小时/> {strong}中的 BROKEN:我使用子视图 [父]视图的mattes制作了约束

addViewAnnotations
layoutMarginGuide中的

工作:我直接从子视图 [父]视图

制作了约束
v.layoutMarginGuide.topAnchor.constraint(equalTo: view.layoutMarginGuide.topAnchor, constant: 40).isActive = true

感谢 Jerry 指出这一点,我没有理解这种区别,而且当我做完&#时,我不认为我完全修复了我的代码34; UPDATE&#34;

以上可能是显着差异