在子类中使用常量来覆盖许多初始化器是很繁琐的。看看下面的类,我需要复制两个初始化器中的代码。
class Test : UIView {
let subview: UIView
override init(frame: CGRect) {
subview = UIView() // once
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
subview = UIView() // twice
super.init(coder: aDecoder)
}
}
如果我尝试使用公共初始化程序,那么我会收到以下错误(请参阅注释)
override init(frame: CGRect) {
commonInit() // 1: Use of 'self' in method call 'commonInit' before super.init initializes self
super.init(frame: frame) // 2: Property 'self.subview' is not in initialized at super.init call
}
private func commonInit() {
subview = UIView() // 3: Cannot assign to 'subview' in 'self'
}
如果我不使用常量并定义子视图,它可以正常工作:
var subview: UIView?
然后当然在init中切换顺序如下:
super.init(frame: frame)
commonInit()
所以我的问题是:到目前为止,还没有办法在Swift中使用常量的初始化器吗?
编辑:我完全忘记提到这里的斗争是我在初始化之前无法启动子视图,它是根据声明常量时未知的数据启动的。
答案 0 :(得分:3)
另一种选择:
class Test : UIView {
let subview:UIView
init(frame: CGRect?, coder: NSCoder?) {
// The first phase initialization here
subview = UIView()
if let frame = frame {
super.init(frame: frame)
}
else if let coder = coder {
super.init(coder: coder)
}
else {
super.init()
}
// the Second phase initialization here
self.addSubview(subview)
}
convenience init() {
self.init(frame: nil, coder: nil)
}
override convenience init(frame: CGRect) {
self.init(frame: frame, coder: nil)
}
required convenience init(coder aDecoder: NSCoder) {
self.init(frame: nil, coder: aDecoder)
}
}
更清洁的选择:
class Test : UIView {
let subview:UIView
private enum SuperInitArg {
case Frame(CGRect), Coder(NSCoder), None
}
private init(_ arg: SuperInitArg) {
subview = UIView()
switch arg {
case .Frame(let frame): super.init(frame:frame)
case .Coder(let coder): super.init(coder:coder)
case .None: super.init()
}
addSubview(subview)
}
convenience init() {
self.init(.None)
}
override convenience init(frame: CGRect) {
self.init(.Frame(frame))
}
required convenience init(coder aDecoder: NSCoder) {
self.init(.Coder(aDecoder))
}
}
答案 1 :(得分:2)
试试这个:
class Test : UIView {
let subview = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
答案 2 :(得分:1)
这对我很有用:
// Declare this somewhere (it can be used by multiple classes)
class FrameCoder: NSCoder {
let frame: CGRect
init(_ frame: CGRect) {
self.frame = frame
super.init()
}
}
然后,当您想要一个通用的初始化模式时,请使用:
class MyView: UIView {
let something: SomeType
required init(coder aDecoder: NSCoder) {
if (aDecoder is FrameCoder) {
super.init(frame: (aDecoder as! FrameCoder).frame)
}
else {
super.init(coder: aDecoder)
}
// Common initializer code goes here...
something = // some value
}
override init(frame: CGRect) {
self.init(coder: FrameCoder(frame))
}
}
使用此方法的优点是您不需要为let
定义创建默认值 - 您可以在上下文中将它们设置为正确的值,就像您只有一个初始化程序一样
请注意,您可以将此技术用于获取任意值的初始值设定项(不仅适用于init(frame: CGRect)
) - 您可以创建特定的NSCoder子类来包装传递给初始值设定项所需的任何值和类型,然后将其链接到您的init(coder:)
方法。
(另外,可能有一些方法可以用通用的方法做到这一点......还没有完全弄明白呢!任何人......?)
答案 3 :(得分:0)
执行以下操作:
class Test : UIView {
let subview = UIView()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//edit subview properties as needed
}
}
答案 4 :(得分:0)
一个选项是遵循 Xcode 模式:
class Test : UIView {
var subview: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() {
subview = UIView()
}
}
请注意,您的subview
是var
另一种选择是:
class Test : UIView {
let subview: UIView = {
let sv = UIView()
// some config, (i.e.: bgColor etc., frame is not yet _real_
// can't yet access instance's frame and other properties
return sv
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() {
// frame might be valid, preferably use layout constraints
addSubview(subview)
}
}
希望这有帮助