Swift:自定义ViewController初始化程序

95

如何在Swift中给UIViewController子类添加自定义初始化器?

我创建了一个UIViewController的子类,看起来像这样:

class MyViewController : UIViewController
{
    init(leftVC:UIViewController, rightVC:UIViewController, gap:Int)
    {
        self.leftVC = leftVC;
        self.rightVC = rightVC;
        self.gap = gap;

        super.init();

        setupScrollView();
        setupViewControllers();
    }
}

当我运行它时,我遇到了致命错误:

致命错误:使用未实现的初始化程序 'init(nibName:bundle:)',适用于类 'MyApp.MyViewController'

我在别处读到过,当添加自定义初始化程序时,必须同时重写 init(coder aDecoder:NSCoder),所以让我们重写 init 并看看会发生什么:

override init(coder aDecoder: NSCoder)
{
    super.init(coder: aDecoder);
}

如果我添加这个,Xcode会抱怨说self.leftVC在super.init调用时未初始化。所以我猜那也不可能是解决方案。那么我想知道如何正确地向Swift的ViewController子类添加自定义初始化程序(因为在Objective-C中似乎并不是问题)?


你是如何尝试实例化你的 MyViewController 的? - Mike Pollard
3
因为通常需要使用初始化器中给定的参数安全初始化实例属性。在viewDidLoad中无法这样做,因为不能保证在需要时该属性可用。 - BadmintonCat
抱歉,只是在我的脑海中,我将viewDidLoad()视为初始化程序。谢谢解释。 - tudoricc
@tudoricc 不用谢!viewDidLoad()不是一个初始化器,而且提供除初始化器之外的必需属性并不能保证它们可用。在我的情况下,这些是两个视图控制器(leftVC和rightVC)以及间隙。 - BadmintonCat
@Avalon 我把那些东西看作按钮。 - tudoricc
显示剩余3条评论
4个回答

134

问题已解决!在这种情况下,必须调用指定的初始化程序,即使用nibName的init,显然......

init(leftVC:UIViewController, rightVC:UIViewController, gap:Int)
{
    self.leftVC = leftVC
    self.rightVC = rightVC
    self.gap = gap

    super.init(nibName: nil, bundle: nil)

    setupScrollView()
    setupViewControllers()
}

38
摆脱那些丑陋的分号 :) - MobileMon
9
我喜欢分号。它们让代码更加清晰明了,尤其是在那些极其冗长的Cocoa/Cocoa-Touch API名称和签名中。在我看来,Swift设计者删除分号是一个错误的决定。 - BadmintonCat
29
在我看来,如果您键入一个字符并且它不影响编译后的二进制文件,那么您就不应该键入它 :) - Sam Soffes
37
所以你不使用(额外的)空格吗?并且你将所有符号混淆为单个字符? - Victor Jalencas
14
这个讨论让我想起了硅谷里的制表符 vs 空格。 - Aaron
显示剩余8条评论

20

Swift 5

如果您想为使用storyBoard.instantiateViewController(withIdentifier: "ViewControllerIdentifier")初始化的UIViewController编写自定义初始化程序

您只能为Optional属性编写自定义初始化程序。

class MyFooClass: UIViewController {
    var foo: Foo?

    init(with foo: Foo) {
        self.foo = foo
        super.init(nibName: nil, bundle: nil)
    }

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.foo = nil
    }
}

2
“你可以为可选属性编写自定义初始化器。”这是关键,我最初错过了它。 - sarawanak

18

如果您想使用更通用的UIViewController,可以从Swift 2.0开始使用此方法。

init() {
    super.init(nibName: nil, bundle: nil)
}

11

不确定您是否已经完全解决了这个问题……但是根据您希望您的类接口看起来和您是否实际需要编码器功能的方式,您也可以使用以下内容:

convenience required init(coder aDecoder: NSCoder)
{
    //set some defaults for leftVC, rightVC, and gap
    self.init(leftVC, rightVC, gap)
}

由于init:leftVC:rightVC:gap是指定初始化程序,您可以通过将其作为方便的初始化程序来调用您的指定初始化程序,从而满足实现init:coder的要求。

这比之前的方法更好。

override init(coder aDecoder: NSCoder)
{
    super.init(coder: aDecoder);
}

因为如果您需要初始化某些属性,则需要重写它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接