Ios 如何在UI子类中隐藏故事板和Nib特定的初始值设定项 问题:

Ios 如何在UI子类中隐藏故事板和Nib特定的初始值设定项 问题:,ios,swift,Ios,Swift,如果我知道init?(编码者:)和其他故事板/nib特定的初始化器将不会被调用,我可以避免在UI子类中实现或调用它们吗 背景 许多UIKit类,包括UIViewController,UIView和UIControl的子类(UIButton,UITextField等)采用NSCoding协议。NSCodinginit?(coder:)方法用于从故事板或nib实例化这些类 NSCoding协议: public protocol NSCoding { func encode(with aCod

如果我知道
init?(编码者:)
和其他故事板/nib特定的初始化器将不会被调用,我可以避免在UI子类中实现或调用它们吗

背景 许多UIKit类,包括
UIViewController
UIView
UIControl
的子类(
UIButton
UITextField
等)采用
NSCoding
协议。
NSCoding
init?(coder:)
方法用于从故事板或nib实例化这些类

NSCoding
协议:

public protocol NSCoding {
   func encode(with aCoder: NSCoder)
   init?(coder aDecoder: NSCoder)
}
采用带有初始值设定项的协议的类必须按照
所需的
实现该初始值设定项。所有子类都必须实现所需的初始值设定项

internal class BaseButton: UIButton {

    // This initializer hides init(frame:) from subclasses
    init() {
        super.init(frame: CGRect.zero)
    }

    @available(*, unavailable)
    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
}

class CustomButton: BaseButton {
    let someProperty: Int

    init(someProperty: Int) {
        self.someProperty = someProperty
        super.init()
    }
}

let customButton = CustomButton(someProperty: 1)
我经常构建没有故事板或笔尖的iOS应用程序。我完全在代码中实现UIViewController、UIView和UIControl子类。但是,如果我想提供自己的
init
方法(我经常这样做),我必须在子类中实现
init?(coder:)
,以安抚编译器。下面的例子说明了这一点

以下内容未编译

class CustomView: UIView {
    init() {
        super.init(frame: CGRect.zero)
    }
}

// Error:'required' initializer 'init(coder:)' must be provided by subclass of 'UIView'
以下代码确实可以编译,因为我提供了
init?(coder:)
的一个实现。对于纯代码的UI子类,我通常通过抛出一个致命错误来实现“init(coder:)”,断言我不希望调用它

class CustomView: UIView {
    init() {
        super.init(frame: CGRect.zero)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
}
出于上述原因,CustomView的子类还需要实现“init(coder:)”

class SubClassOfCustomView: CustomView {
    let someProperty: Int

    init(someProperty: Int) {
        self.someProperty = someProperty
        super.init()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
}
UI子类和@可用(*,不可用) *下面的代码是用Swift 3编写和测试的

此解决方案的关键是创建自定义UI子类继承自的基本子类。在下面的示例中,这些子类被命名为
BaseViewController
BaseView
BaseButton
。这些子类包括一个初始值设定项,该初始值设定项默认对其子类隐藏的超类指定初始值设定项的参数

internal class BaseButton: UIButton {

    // This initializer hides init(frame:) from subclasses
    init() {
        super.init(frame: CGRect.zero)
    }

    @available(*, unavailable)
    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
}

class CustomButton: BaseButton {
    let someProperty: Int

    init(someProperty: Int) {
        self.someProperty = someProperty
        super.init()
    }
}

let customButton = CustomButton(someProperty: 1)
init(coder:)
必须在所有子类中实现,因为它是UI超类的必需初始值设定项。您可以通过将属性
@available(*,unavailable)
放在该初始值设定项的实现之上来解决此问题

用于“指示声明相对于特定平台和操作系统版本的生命周期”。将此属性与以下参数一起使用:
@available(*,unavailable)
使后面的代码块在所有平台的所有版本上都不可用。

UIViewController UIView UIButton-UIControl子类 此UIButton示例说明如何对UIControl子类进行子类化

internal class BaseButton: UIButton {

    // This initializer hides init(frame:) from subclasses
    init() {
        super.init(frame: CGRect.zero)
    }

    @available(*, unavailable)
    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
}

class CustomButton: BaseButton {
    let someProperty: Int

    init(someProperty: Int) {
        self.someProperty = someProperty
        super.init()
    }
}

let customButton = CustomButton(someProperty: 1)

考虑 我不建议定期使用
@available(*,unavailable)
来避免实现所需的初始值设定项。这有助于减少在这种情况下不会调用的冗余代码(因为您不打算使用故事板/NIB)。通过在基类中使用
@available(*,unavailable)
而不是在每个自定义子类中使用它,可以减少
@available(*,unavailable)
的外观

我知道这在Swift 2和3中有效。Swift的未来版本可能不允许这样做。然而,我希望Swift团队能想出一个更好的方法来避免自定义UI子类中的这种冗余代码

internal class BaseButton: UIButton {

    // This initializer hides init(frame:) from subclasses
    init() {
        super.init(frame: CGRect.zero)
    }

    @available(*, unavailable)
    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
}

class CustomButton: BaseButton {
    let someProperty: Int

    init(someProperty: Int) {
        self.someProperty = someProperty
        super.init()
    }
}

let customButton = CustomButton(someProperty: 1)
出于好奇,我尝试从故事板启动BaseViewController子类。我预计应用程序会因选择器未找到错误而崩溃,但它调用了
init?(coder)
方法,尽管它对所有平台都是隐藏的。这可能是因为
available
属性没有对目标C隐藏
init?(coder)
初始值设定项,而脚本实例化代码就是在这里运行的

UIKit经常使用类和继承,而Swift社区鼓励结构和面向协议的编程。我在我的基本UI类声明上面包含以下标题,以阻止基本UI类成为全局设置和功能的垃圾场

/**
 *  This base UIViewController subclass removes the requirement to override init(coder:) and hides init(nibName:bundle:) from subclasses.
 *  It is not intended to create global functionality inherited by all UIViewControllers.
 *  Alternatively, functionality can be added to UIViewController's via composition and/or protocol oriented programming.
 */

/**
 *  This base UIView subclass removes the requirement to override init(coder:) and hides init(frame:) from subclasses.
 *  It is not intended to create global functionality inherited by all UIViews.
 *  Alternatively, functionality can be added to UIView's via composition and/or protocol oriented programming.
 */
参考:我发现Swift语言指南的章节有助于理解初始值设定者的规则。

UI子类和@available(*,available) *下面的代码是用Swift 3编写和测试的

此解决方案的关键是创建自定义UI子类继承自的基本子类。在下面的示例中,这些子类被命名为
BaseViewController
BaseView
BaseButton
。这些子类包括一个初始值设定项,该初始值设定项默认对其子类隐藏的超类指定初始值设定项的参数

internal class BaseButton: UIButton {

    // This initializer hides init(frame:) from subclasses
    init() {
        super.init(frame: CGRect.zero)
    }

    @available(*, unavailable)
    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
}

class CustomButton: BaseButton {
    let someProperty: Int

    init(someProperty: Int) {
        self.someProperty = someProperty
        super.init()
    }
}

let customButton = CustomButton(someProperty: 1)
init(coder:)
必须在所有子类中实现,因为它是UI超类的必需初始值设定项。您可以通过将属性
@available(*,unavailable)
放在该初始值设定项的实现之上来解决此问题

用于“指示声明相对于特定平台和操作系统版本的生命周期”。将此属性与以下参数一起使用:
@available(*,unavailable)
使后面的代码块在所有平台的所有版本上都不可用。

UIViewController UIView UIButton-UIControl子类 此UIButton示例说明如何对UIControl子类进行子类化

internal class BaseButton: UIButton {

    // This initializer hides init(frame:) from subclasses
    init() {
        super.init(frame: CGRect.zero)
    }

    @available(*, unavailable)
    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
}

class CustomButton: BaseButton {
    let someProperty: Int

    init(someProperty: Int) {
        self.someProperty = someProperty
        super.init()
    }
}

let customButton = CustomButton(someProperty: 1)

考虑 我不建议定期使用
@available(*,unavailable)
来避免实现所需的初始值设定项。这有助于减少在这种情况下不会调用的冗余代码(因为您不打算使用故事板/NIB)。
@available(*,unavailable)
的外观通过在基类中使用它(并使自定义子类继承自基类)作为操作来减少