子类化 NSLayoutConstraint,常量的奇怪行为
Subclassing NSLayoutConstraint, bizarre behavior of constants
无论您目前在做什么项目,只需
在 UIView 中的任何屏幕上拖放
只需添加宽度限制(666 或其他都可以),
将约束的自定义 class 更改为 Constrainty
运行 应用程序,
这怎么可能?
它是否真的需要常量的值,"before the class is initialized," 或类似的值?
怎么会发生,怎么解决?
起初,我将两个变量设为 @IBInspectable
,令我惊讶的是它根本不起作用。但后来我将它们更改为普通的 let 常量 - 但这是行不通的!
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
}
}
我的胡乱猜测都是因为一个名为 UIClassSwapper
的 class。它是一个私有 class 处理来自 Interface Builder 文件的所有 UI 对象初始化。我建议用计算属性替换您的 let
常量。
//...
var percentageWidht: CGFloat { // nothing up my sleeve
return 77.0
}
var limitWidth: CGFloat {
return 110.0
}
//...
UPD
Swift default 属性 values(在其声明中具有值的属性)在初始化程序调用之前设置。例如。如果你有一个 class MyClass
和一个 属性 let someVar: CGFloat = 12.0
并且它被桥接到 Objective-C,当你为你的对象分配内存并且不调用初始化程序时 MyClass *obj = [MyClass alloc]
您的变量将具有默认值 0.0 并将保持不变,除非您调用 [obj init]
之类的初始化程序。所以我的第二个大胆的猜测是,因为 NSLayoutConstraint
class 写在 Objective-C 中并且它的 initWithCoder:
初始化程序没有在它的头文件中声明(它是私有的),ObjC-Swift 桥接机制无法将它的调用识别为初始化程序调用(它认为这只是一个简单的实例方法),因此您的 Swift 具有默认值的属性根本没有被初始化。
我认为subclass NSLayoutConstraint
不是个好主意。我不认为它被设计为在 Apple 之外进行 subclassed。
无论如何,问题是NSLayoutConstraint
符合NSCoding
协议,但没有在header文件中声明它符合NSCoding
。正因为如此,Swift 不知道 NSLayoutConstraint
可以被 -[NSLayoutConstraint initWithCoder:]
初始化,所以它不会生成对 initWithCoder:
的覆盖来初始化你添加的实例变量在你的子class.
修复方法如下。
首先,如果您的项目没有桥接 header,请添加一个。添加一个最简单的方法是在您的项目中创建一个新的 Objective-C class,接受 Xcode 的提议以创建桥接 header,然后删除 .h
和它为 class 创建的 .m
文件(但保留桥接 header)。
然后,在桥接header中声明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
class 中,像这样覆盖 init(coder:)
:
required init(coder decoder: NSCoder) {
super.init(coder: decoder)!
}
瞧瞧:
HERE WE GO 77.0 350.0
result is 246.4
无论您目前在做什么项目,只需
在 UIView 中的任何屏幕上拖放
只需添加宽度限制(666 或其他都可以),
将约束的自定义 class 更改为
Constrainty
运行 应用程序,
这怎么可能?
它是否真的需要常量的值,"before the class is initialized," 或类似的值?
怎么会发生,怎么解决?
起初,我将两个变量设为 @IBInspectable
,令我惊讶的是它根本不起作用。但后来我将它们更改为普通的 let 常量 - 但这是行不通的!
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
}
}
我的胡乱猜测都是因为一个名为 UIClassSwapper
的 class。它是一个私有 class 处理来自 Interface Builder 文件的所有 UI 对象初始化。我建议用计算属性替换您的 let
常量。
//...
var percentageWidht: CGFloat { // nothing up my sleeve
return 77.0
}
var limitWidth: CGFloat {
return 110.0
}
//...
UPD
Swift default 属性 values(在其声明中具有值的属性)在初始化程序调用之前设置。例如。如果你有一个 class MyClass
和一个 属性 let someVar: CGFloat = 12.0
并且它被桥接到 Objective-C,当你为你的对象分配内存并且不调用初始化程序时 MyClass *obj = [MyClass alloc]
您的变量将具有默认值 0.0 并将保持不变,除非您调用 [obj init]
之类的初始化程序。所以我的第二个大胆的猜测是,因为 NSLayoutConstraint
class 写在 Objective-C 中并且它的 initWithCoder:
初始化程序没有在它的头文件中声明(它是私有的),ObjC-Swift 桥接机制无法将它的调用识别为初始化程序调用(它认为这只是一个简单的实例方法),因此您的 Swift 具有默认值的属性根本没有被初始化。
我认为subclass NSLayoutConstraint
不是个好主意。我不认为它被设计为在 Apple 之外进行 subclassed。
无论如何,问题是NSLayoutConstraint
符合NSCoding
协议,但没有在header文件中声明它符合NSCoding
。正因为如此,Swift 不知道 NSLayoutConstraint
可以被 -[NSLayoutConstraint initWithCoder:]
初始化,所以它不会生成对 initWithCoder:
的覆盖来初始化你添加的实例变量在你的子class.
修复方法如下。
首先,如果您的项目没有桥接 header,请添加一个。添加一个最简单的方法是在您的项目中创建一个新的 Objective-C class,接受 Xcode 的提议以创建桥接 header,然后删除 .h
和它为 class 创建的 .m
文件(但保留桥接 header)。
然后,在桥接header中声明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
class 中,像这样覆盖 init(coder:)
:
required init(coder decoder: NSCoder) {
super.init(coder: decoder)!
}
瞧瞧:
HERE WE GO 77.0 350.0
result is 246.4