【问题标题】:Swift addConstraint leads to EXC_BAD_INSTRUCTIONSwift addConstraint 导致 EXC_BAD_INSTRUCTION
【发布时间】:2017-08-17 11:14:12
【问题描述】:

我一直在 macOS 应用程序中使用 NSTouchBar 并想出了以下代码:

func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
    switch identifier {
    case NSTouchBarItemIdentifier.testItemIdentifier:
        let viewObject = NSView()

        for i in 1...10 {
            let imageWidth: CGFloat = 20;
            let imageHeight: CGFloat = 20;
            let imagePositionTop: CGFloat = 0
            let imagePositionLeft: CGFloat = imageWidth * (CGFloat(i) - 1)

            let imageObject = NSImage(named: "test_image")!
            imageObject.size = NSSize(width: 20, height: 20)
            let imageView = NSImageView(image: imageObject)
            let constraintObject1 = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: viewObject, attribute: NSLayoutAttribute.top, multiplier: 1, constant: imagePositionTop)
            let constraintObject2 = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.left, relatedBy: NSLayoutRelation.equal, toItem: viewObject, attribute: NSLayoutAttribute.left, multiplier: 1, constant: imagePositionLeft)
            let constraintObject3 = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: imageView, attribute: NSLayoutAttribute.width, multiplier: 0, constant: imageWidth)
            let constraintObject4 = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: imageView, attribute: NSLayoutAttribute.height, multiplier: 0, constant: imageHeight)

            imageView.addConstraint(constraintObject1)
            imageView.addConstraint(constraintObject2)
            imageView.addConstraint(constraintObject3)
            imageView.addConstraint(constraintObject4)

            viewObject.addSubview(imageView)
        }

        let customViewItem = NSCustomTouchBarItem(identifier: identifier)
        customViewItem.view = viewObject
        return customViewItem
    default:
        return nil
    }
}

运行应用程序时,我得到一个EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)。造成这种情况的原因似乎是对addConstraint 的调用。我以前从未手动创建过约束,并且对 swift 语言相对较新。

非常感谢任何帮助。

【问题讨论】:

    标签: swift macos swift3 autolayout nslayoutconstraint


    【解决方案1】:

    @toddg 是正确的:您必须将imageView 添加为viewObject 的子视图您可以激活两个视图之间的约束。如果要激活涉及两个视图的约束,则两个视图必须位于同一视图层次结构中。

    但你还有其他问题。

    两个视图 A 和 B 之间的约束必须添加到 A 和 B 的共同祖先(可能是 A 或 B 本身)。在您的情况下,由于imageViewviewObject 的子视图,这意味着必须将imageViewviewObject 之间的任何约束添加到viewObject(或viewObject 的超级视图)。您不能将约束添加到imageView。您可以通过将约束的the isActive property 设置为true 而不是使用addConstraint 来解决此问题。当您将 isActive 属性设置为 true 时,约束将自己添加到相应的视图中。

    此外,您在 imageView.widthimageView.width 之间创建了一个约束,这没有任何意义。如果要将imageView.width约束为常量,则约束的另一项应为nil,属性为.notAnAttribute

    此外,您没有将 translatesAutoresizingMaskIntoConstraints 设置为 false,如果您想使用约束来设置 imageView 的大小和位置,则需要这样做。

    无论如何,您使用的是一堆旧的 API,这让事情变得比必要的更难。通过使用NSStackView,您可以使您的代码正确、更短、更简单。 NSStackView 是在 macOS 10.9 中添加的,NSTouchBar 是在 macOS 10.12.2 中添加的,因此没有理由为此使用堆栈视图。

    func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
        switch identifier {
        case NSTouchBarItemIdentifier.testItemIdentifier:
            let stack = NSStackView()
            stack.translatesAutoresizingMaskIntoConstraints = false
            stack.spacing = 0
            stack.distribution = .fillEqually
    
            let length = CGFloat(20)
    
            for _ in 1...10 {
                let imageObject = NSImage(named: "test_image")!
                imageObject.size = NSSize(width: length, height: length)
                let imageView = NSImageView(image: imageObject)
                imageView.translatesAutoresizingMaskIntoConstraints = false
                stack.addArrangedSubview(imageView)
            }
    
            let customViewItem = NSCustomTouchBarItem(identifier: identifier)
            customViewItem.view = stack
            return customViewItem
        default:
            return nil
        }
    }
    

    结果:

    【讨论】:

    • "你不能将约束添加到 imageView"
    • 正如我所解释的,必须将约束添加到约束的受约束视图的共同祖先。如果viewObjectimageView 的父视图,并且约束在viewObjectimageView 之间,则必须将约束添加到viewObject(或viewObject 的父视图)。如果您尝试将其添加到 imageView,则会出现错误。
    • 这在-[NSView addConstraint:] 文档中有详细说明:“约束必须只涉及接收视图范围内的视图。具体来说,所涉及的任何视图都必须是接收视图本身,或者是接收视图的子视图。”
    • 感谢您的详细解释!现在一切正常。
    【解决方案2】:

    您需要在添加约束之前添加视图。尝试将约束添加到 viewObject

    viewObject.addSubview(imageView)
    viewObject.addConstraint(constraintObject1)
    viewObject.addConstraint(constraintObject2)
    viewObject.addConstraint(constraintObject3)
    viewObject.addConstraint(constraintObject4)
    

    此外,为了将来参考,您应该包含所有文本错误消息。

    【讨论】:

    • 感谢您的回答。不幸的是,这会导致相同的错误。完整的错误信息是:EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    • 我已经更新了我的答案。控制台中通常有更多关于错误的信息。当您的应用程序正在运行时按 command-shift-c(或从菜单中打开)以显示控制台。
    【解决方案3】:

    除了@toddg 的回答之外,您还应该考虑将NSLayoutAnchor 用于像这样的简单布局。它简化了代码。

    imageView.topAnchor.constraint(equalTo: viewObject.topAnchor).isActive = true
    imageView.leadingAnchor.constraint(equalTo: viewObject.leadingAnchor).isActive = true
    imageView.trailingAnchor.constraint(equalTo: viewObject.trailingAnchor).isActive = true
    imageView.bottomAnchor.constraint(equalTo: viewObject.bottomAnchor).isActive = true
    

    这会初始化约束 AND 在一行代码中激活它。请务必先将imageView 添加到视图堆栈中。

    另外,您可能想要设置imageView.translatesAutoresizingMaskIntoConstraints = false

    您也不需要设置 imageObject.size = NSSize(width: 20, height: 20) - 约束会为您处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-13
      • 1970-01-01
      • 1970-01-01
      • 2015-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多