使用常量时的公共init

时间:2015-05-28 11:12:18

标签: ios swift cocoa-touch

在子类中使用常量来覆盖许多初始化器是很繁琐的。看看下面的类,我需要复制两个初始化器中的代码。

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中使用常量的初始化器吗?

编辑:我完全忘记提到这里的斗争是我在初始化之前无法启动子视图,它是根据声明常量时未知的数据启动的。

5 个答案:

答案 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()
    }
}

请注意,您的subviewvar

另一种选择是:

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)
    }
}

希望这有帮助