如何在Swift中编写UIViewController的init方法

71

我无法向以下UIViewController类添加init方法。 我需要在init方法中编写一些代码。 我必须编写init(coder)方法吗? 即使我添加了编码器和解码器方法,我仍然会收到错误消息。 我还尝试过使用没有任何参数的init方法,但似乎也不起作用。

class ViewController: UIViewController {

    var tap: UITapGestureRecognizer?

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)   {
        super.init(nibName: nil, bundle: nil)
        tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
    }

...
...
}

如果我调用super.init()方法而没有传入参数,则会出现“必须调用超类的指定初始化程序”错误,如果我传递参数nib和bundle,则会出现“需要初始化程序init(coder)”错误。

即使我添加了init(coder)和init(decoder),它也无法正常工作。


你想在 init() 中添加什么?如果只是触摸手势,你也可以在 viewDidLoad() 中完成,甚至可以将其做成懒加载变量。 - Eendje
8个回答

71

我使用了:

convenience init() {
    self.init(nibName:nil, bundle:nil)
}

有些人建议:

convenience init(){
    self.init()
}

但这会导致无限循环。


1
我在 "self.init(nibName:nil, bundle:nil)" 语句上遇到了 "Argument passed to call that takes no arguments" 错误。 - RenniePet
4
我的控制器没有调用它。 - Jonny
你能展示一下如何使用这种方法传递构造函数参数吗? - Petrus Theron
在第二个选项上出现了EXC_BAD_ACCESS错误。 - Anton Ogarkov

43

所有这些答案都只是半个答案。有些人在相关帖子中遇到了无限循环的问题,因为他们只添加了convenience init()(如果您不提供一个独特的init()方法让它调用,则会递归调用自己),而其他人则忽略了扩展超类。此格式将所有解决方案结合起来,完全满足问题。

// This allows you to initialise your custom UIViewController without a nib or bundle.
convenience init() {
    self.init(nibName:nil, bundle:nil)
}

// This extends the superclass.
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
}

// This is also necessary when extending the superclass.
required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented") // or see Roman Sausarnes's answer
}

编辑: 如果您希望使用传递给convenience init()方法的参数初始化任何类属性,而不会出现所有重写的init()方法产生的问题,那么您可以将所有这些属性设置为隐式解包可选类型,这是一种由苹果自己使用的模式。


你能演示如何按照描述传递参数吗? - Petrus Theron
2
这个答案是100%错误的 :) 你不需要方便初始化器。(1)如果你的目标是添加更多的初始化(即,在viewDidLoad之前),你需要两个初始化程序(而不是方便初始化)。(2)如果你的目标是手动初始化视图控制器(出于某种奇怪的原因),那么你需要方便初始化(而不是其他两个)。任何新手阅读这个答案都会完全误解正在发生的事情。 - Fattie

29

您需要重写init(nibName:bundle:)初始化器,并且提供所需的init(coder:)初始化器,并在两者中都初始化tap

class ViewController: UIViewController {

    var tap: UITapGestureRecognizer?

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)   {
        print("init nibName style")
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
    }

    // note slightly new syntax for 2017
    required init?(coder aDecoder: NSCoder) {
        print("init coder style")
        super.init(coder: aDecoder)
        tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
    }
...
...
}

同时,在调用超类的初始化方法时,确保不要仅传递nil作为nibNamebundle参数。应该将传入的任何内容向上传递。


请注意,一些答案中提到的"便利初始化方法"只有在您实际上手工初始化视图控制器时才相关。(很少有情况需要这样做。)如果您想要"在正常初始化期间执行某些操作"——例如创建手势识别器或仅初始化一些变量——唯一可能的解决方案就是此答案中给出的解决方案。


2
required init(coder aDecoder: NSCoder) 中,你有 super.init() ... 难道不应该是 super.init(coder: aDecoder) 吗? - Jim Rhoades
实际上,代码示例中有两个微不足道的拼写错误。您可以在Xcode中使用自动完成轻松地看到这些错误。Roman,如果您看到了这条消息,我已经编辑了这些错误。当然,如果需要的话,请随意更改或撤销更改。这是唯一正确的答案,所以我纠正了这些错误,谢谢。 - Fattie
为什么我们需要在两个初始化函数中都初始化“tap”?谢谢! - joliejuly
2
@joliejuly “初始化tap” 在两个init中都有,因为两个init是互斥的,只会调用其中一个而不是两个同时调用。话虽如此,这是重复的代码,应该提取到私有函数中(例如,private func commonInit()),由公共init都调用。 - Vince O'Sullivan

8

你可以创建一个方便的初始化方法,而不是重写init()方法。方便的初始化方法只是调用它,使事情更容易。

class ViewController: UIViewController {
    var tap: UITapGestureRecognizer?
    convenience init(){
        self.init()
        tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
    }
}

应用程序崩溃是因为tap为nil,即使在init中已经分配了一个值。 - Ankit Goel
你是在尝试从另一个类中使用tap吗? - Icaro
tap在同一个类中声明..就像你的代码一样。 - Ankit Goel
4
那不应该是 super.init() 吗? - Rohan Sanap
1
便利初始化器必须委托(使用“self.init”)而不是链接到超类初始化器(使用“super.init”)。 - Axel Guilmin

6

我认为你需要添加

required init(coder aDecoder: NSCoder) {
    print("init coder")
    super.init(coder: aDecoder)
}

或者

convenience init() {
    self.init()
}

你需要写入init。不要删除它。

var tap: UITapGestureRecognizer?

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)   {
    super.init(nibName: nil, bundle: nil)
}

希望它能够帮助到您。

3
这会报错,显示“必须调用父类的指定初始化方法”。 - Ankit Goel
我认为我们应该覆盖UIViewController中所有设计的init构造函数,然后方便的“init()”方法将自动继承自UIViewController,然后我们现在可以在子类中创建一个方便的init方法。 - Frank Cheng

3

我发现您之前提出了一个问题,但是没有人给出基本的答案。在Swift中,这是一个基本概念:子类必须在其超类初始化完成之前初始化其变量。

因此,正如Ankit Goel所提到的崩溃信息:"必须调用超类的指定初始化方法"

因此,更新后的代码应该是:

class ViewController: UIViewController {
    var tap: UITapGestureRecognizer?

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)   {
        print("init nibName style")
        self.tap = nil
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        self.tap = UITapGestureRecognizer(target: self, action: Selector(("handleTap:")))
    }

    required init?(coder aDecoder: NSCoder) {
        print("init coder style")
        self.tap = nil
        super.init(coder: aDecoder)
        tap = UITapGestureRecognizer(target: self, action: Selector(("handleTap:")))
    }
}

3

我在使用 Xcode 12.0 中的 Swift 5 时遇到了同样的问题,以下是我的解决方案(2020 年)。

class Foo: UIViewController {
    private var bar: Bar!

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

        setupController()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        setupController()
    }
    
    private func setupController() {
        bar = Bar()
    }
}

我现在使用的替代方法是在 loadView 函数里面进行初始化。

class Foo: UIViewController {
    private var bar: Bar!
    
    override func loadView() {
        setupController()
    }

    private func setupController() {
        bar = Bar()
    }
}

1
正确的流程是,调用指定的初始化方法,这种情况下是使用带有nibName参数的init方法。
init(tap: UITapGestureRecognizer)
{
    // Initialise the variables
    tap = UITapGestureRecognizer(target: self, action: Selector(("handleTap:")))

    // Call the designated init of ViewController
    super.init(nibName: nil, bundle: nil)

    // Call your viewcontroller custom methods here
    setupViewControllers()
}

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