如何隐藏UI子类中的Storyboard和Nib特定的初始化程序

在我知道init?(coder:)和其他storyboard / nib特定的初始化器不会被调用的情况下,我是否可以避免在UI子类中实现或调用它们的需求?

背景

许多UIKit类包括UIViewControllerUIViewUIControl子类( UIButtonUITextField等)都采用NSCoding协议。 NSCoding init?(coder:)方法用于从故事板或笔尖实例化这些类。

NSCoding协议:

 public protocol NSCoding { func encode(with aCoder: NSCoder) init?(coder aDecoder: NSCoder) } 

采用初始化程序的协议类必须根据required实现该初始化程序。 必需的初始化器必须由所有子类实现。

我经常创build没有故事板或笔尖的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子类和@available(*,不可用)

*下面的代码是在Swift 3中编写和testing的

这个解决scheme的关键是创build自定义UI子类inheritance的基类子类。 在下面的例子中,这些子类被命名为BaseViewControllerBaseViewBaseButton 。 这些子类包括一个初始化程序,它默认超类的指定初始化程序的参数,这些参数对它们的子类是隐藏的。

init(coder:)必须在所有子类中实现,因为它是UI超类的必需构造器。 您可以通过将属性@available(*, unavailable)放在该初始化器的实现之上来解决这个问题。

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

的UIViewController

 class BaseViewController: UIViewController { // This initializer hides init(nibName:bundle:) from subclasses init() { super.init(nibName: nil, bundle: nil) } @available(*, unavailable) required init?(coder aDecoder: NSCoder) { fatalError("NSCoding not supported") } } class CustomViewController: BaseViewController { let someProperty: Int init(someProperty: Int) { self.someProperty = someProperty super.init() } } let customViewController = CustomViewController(someProperty: 1) 

的UIView

 class BaseView: UIView { // 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 CustomView: BaseView { let someProperty: Int init(someProperty: Int) { self.someProperty = someProperty super.init() } } let customView = CustomView(someProperty: 1) 

UIButton – UIControl子类

这个UIButton的例子演示了如何inheritanceUIControl的子类。

 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) 

注意事项

我不build议定期使用@available(*, unavailable)来避免实现所需的初始化器。 在这种情况下,减less冗余代码是非常有用的(因为你不打算使用storyboard / nibs)。 通过在基类中使用@available(*, unavailable)可以减less@available(*, unavailable)而不是每个自定义子类)的外观(并且自定义子类从基类inheritance)。

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

出于好奇,我尝试从故事板启动一个BaseViewController子类。 我希望应用程序崩溃,但没有发现错误,但它调用了init?(coder)方法,即使它在所有平台上都是隐藏的。 这可能是因为available属性并不隐藏Objective C中的init?(coder)初始化程序,而这是故事板实例化代码运行的地方。

UIKit经常使用类和inheritance,而Swift社区鼓励结构和面向协议的编程。 我在基本的UI类声明上面添加了以下头文件,以防止基础UI类成为全局设置和function的倾倒点。

 /** * 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语言指南的Initialization部分有助于理解Initializers的规则。