子类化NSLayoutConstraint,常量的奇异行为

Fat*_*tie 8 ios nslayoutconstraint

对于你目前正在进行的任何项目,简单地说

  1. 在任何屏幕上放下UIView

  2. 只需添加一个宽度约束(666或其他任何好的),

  3. 将约束的自定义类更改为 Constrainty

  4. 运行应用程序,

在此输入图像描述

这怎么可能?

它是否实际上要求常量的值,"在类初始化之前",还是其他一些?

怎么会发生,怎么解决?

起初,我有两个变量@IBInspectable,我很惊讶,根本没有工作.但后来我将它们改为普通的常量 - 这不起作用!

class Constrainty: NSLayoutConstraint {

    let percentageWidth: CGFloat = 77 // nothing up my sleeve
    let limitWidth: CGFloat = 350

    override var constant: CGFloat {

        get { return teste() }
        set { super.constant = newValue }
    }

    func teste()->CGFloat {

        print("\n\n HERE WE GO \(percentageWidth) \(limitWidth) \n\n")

        if let sw = (firstItem as? UIView)?.superview?.bounds.width {

            let w = sw * ( percentageWidth / 100.0 )
            let r = w > limitWidth ? limitWidth : w
            print("result is \(r) \n\n")
            return r
        }

        return 50
    }

}
Run Code Online (Sandbox Code Playgroud)

rob*_*off 9

我不认为子类是个好主意NSLayoutConstraint.我不认为它是专门设计为在Apple之外的子类.

无论如何,问题是NSLayoutConstraint符合NSCoding协议,但没有声明它符合NSCoding头文件.因此,Swift不知道NSLayoutConstraint可以初始化-[NSLayoutConstraint initWithCoder:],因此它不会生成覆盖initWithCoder:,初始化您在子类中添加的实例变量.

这是如何解决它.

首先,如果您的项目没有桥接标题,请添加一个.添加一个最简单的方法是在你的项目中创建一个新的Objective-C类,接受Xcode的报价创建桥接报头,然后将其删除.h,并.m属于该类创建的文件(但保留桥接报头).

然后,在桥接头中,声明NSLayoutConstraint符合NSCoding:

//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//

@import UIKit;

@interface NSLayoutConstraint (MyProject) <NSCoding>
@end
Run Code Online (Sandbox Code Playgroud)

最后,在你的Constrainty班级中,覆盖init(coder:)如下:

required init(coder decoder: NSCoder) {
    super.init(coder: decoder)!
}
Run Code Online (Sandbox Code Playgroud)

等瞧:

 HERE WE GO 77.0 350.0 


result is 246.4 
Run Code Online (Sandbox Code Playgroud)

  • [提交错误报告](https://bugreport.apple.com/)要求将"NSCoding"的一致性公之于众也是合理的.这至少可以得到一个响应,表明你不应该继承`NSLayoutConstraint`. (2认同)

Vah*_*yan 7

我的猜测是因为一个名为的类UIClassSwapper.它是一个私有类,它处理Interface Builder文件中的所有UI对象初始化.我建议let用计算属性替换你的常量.

//...
var percentageWidht: CGFloat { // nothing up my sleeve
    return 77.0
}

var limitWidth: CGFloat {
    return 110.0
}
//...
Run Code Online (Sandbox Code Playgroud)

UPD

在初始化程序调用之前设置Swift 默认属性值(在其声明中具有值的属性).如果你有一个MyClass带有属性的类let someVar: CGFloat = 12.0并且它被桥接到Objective-C,当你为你的对象分配内存并且不调用初始化程序时,MyClass *obj = [MyClass alloc]你的变量将具有默认值0.0并且将保持如此,除非你将调用初始化程序喜欢[obj init].所以我的第二个猜测是因为NSLayoutConstraint类是用Objective-C编写的,并且它的initWithCoder:初始化程序没有在它的头文件中声明(它是私有的),ObjC-Swift桥接机制不能将它的调用识别为初始化程序调用(它认为它它只是一个简单的实例方法),因此根本没有初始化具有默认值的Swift属性.