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

时间:2018-01-04 18:10:27

标签: ios nslayoutconstraint

无论你目前正在做什么项目,只需

  1. 在UIView的任何屏幕上放下

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

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

  4. 运行应用

  5.   

    enter image description here

    这怎么可能?

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

    怎么会发生,怎么解决?

    起初,我有两个变量为@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
        }
    
    }
    

2 个答案:

答案 0 :(得分:8)

我认为继承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

最后,在您的Constrainty课程中,覆盖init(coder:),如下所示:

required init(coder decoder: NSCoder) {
    super.init(coder: decoder)!
}

Et瞧:

 HERE WE GO 77.0 350.0 


result is 246.4 

答案 1 :(得分:7)

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

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

var limitWidth: CGFloat {
    return 110.0
}
//...

<强> 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属性根本没有被初始化。