如何在Swift中为UIViewController子类创建自定义初始化程序?


93

不好意思,如果以前已经问过这个问题,我已经搜索了很多内容,并且很多答案都来自早期Swift版本,当时情况有所不同。我似乎找不到确切的答案。

我想创建一个子类,UIViewController并有一个自定义的初始化程序,使我可以轻松地在代码中进行设置。我在Swift中无法做到这一点。

我想要一个init()可以用来传递特定参数的函数,NSURL然后将其与视图控制器一起使用。在我看来,它看起来像init(withImageURL: NSURL)。如果我添加了该功能,则要求我添加该init(coder: NSCoder)功能。

我相信这是因为它在超类中用required关键字标记了吗?所以我必须在子类中这样做吗?我添加它:

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

怎么办?我的特殊初始化程序被认为是convenience一个吗?指定一个?我叫超级初始化器吗?来自同一类的初始值设定项?

如何将特殊的初始化程序添加到UIViewController子类上?


初始化程序非常适合以编程方式创建的viewController,但是对于通过情节提要创建的Viewcontroller来说,您很走运,因此必须设法解决问题
Honey,

Answers:


157
class ViewController: UIViewController {

    var imageURL: NSURL?

    // this is a convenient way to create this view controller without a imageURL
    convenience init() {
        self.init(imageURL: nil)
    }

    init(imageURL: NSURL?) {
        self.imageURL = imageURL
        super.init(nibName: nil, bundle: nil)
    }

    // if this view controller is loaded from a storyboard, imageURL will be nil

    /* Xcode 6
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    */

    // Xcode 7 & 8
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

可以将imageURL改为常量吗?(“ let imageURL:NSURL?”)
Van Du Tran 2015年

2
xCode 7无法正常工作,所需的构造函数无法初始化类级别的属性,在我的情况下是Int,而不是NSURL?
克里斯·海斯

1
@NikolaLukic -我们可以通过调用创建类的新实例ViewController()ViewController(imageURL: url)或从情节串连图板加载它。
ylin0x81

3
我不得不写我的, required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }否则super.init(coder: aDecoder)我会收到属性self.someProperty未在super.init调用时初始化
亲爱的

1
不知道这有什么关系。如果您使用的是纯代码,则无需在中填充属性init(coder: aDecoder)。本fatalError就足够了
蜂蜜

26

对于那些用代码编写UI的人

class Your_ViewController : UIViewController {

    let your_property : String

    init(your_property: String) {
        self.your_property = your_property
        super.init(nibName: nil, bundle: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) is not supported")
    }
}

12

这与其他答案非常相似,但有一些解释。可接受的答案具有误导性,因为它的属性是可选的,并且没有暴露您init?(coder: NSCoder)必须初始化每个属性的事实,唯一的解决方案是使用fatalError()。最终,您可以通过将属性设置为可选属性来摆脱困境,但这并不能真正回答OP的问题。

// Think more of a OnlyNibOrProgrammatic_NOTStoryboardViewController
class ViewController: UIViewController {

    let name: String

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // I don't have a nib. It's all through my code.
    init(name: String) {
        self.name = name

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

    // I have a nib. I'd like to use my nib and also initialze the `name` property
    init(name: String, nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle? ) {
        self.name = name
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    // when you do storyboard.instantiateViewController(withIdentifier: "ViewController")
    // The SYSTEM will never call this!
    // it wants to call the required initializer!

    init?(name: String, coder aDecoder: NSCoder) {
        self.name = "name"
        super.init(coder: aDecoder)
    }

    // when you do storyboard.instantiateViewController(withIdentifier: "ViewController")
    // The SYSTEM WILL call this!
    // because this is its required initializer!
    // but what are you going to do for your `name` property?!
    // are you just going to do `self.name = "default Name" just to make it compile?!
    // Since you can't do anything then it's just best to leave it as `fatalError()`
    required init?(coder aDecoder: NSCoder) {
        fatalError("I WILL NEVER instantiate through storyboard! It's impossible to initialize super.init?(coder aDecoder: NSCoder) with any other parameter")
    }
}

基本上,您必须ABANDON从情节提要中加载它。为什么?

因为当你调用一个的viewController storyboard.instantiateViewController(withIdentifier: "viewController")然后UIKit中会做它的事和呼叫

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

您永远不能将该调用重定向到另一个init方法。

的文件instantiateViewController(withIdentifier:)

使用此方法可以创建以编程方式显示的视图控制器对象。每次调用此方法时,它都会使用该init(coder:)方法创建视图控制器的新实例。

但是,对于以编程方式创建的viewController或笔尖创建的viewControllers,您可以如上所述重定向该调用。


5

便利初始化器是辅助的,支持类的初始化器。您可以定义一个便捷初始化程序,以从与便捷初始化程序相同的类中调用一个指定初始化程序,并将某些指定初始值设定项的参数设置为默认值。您还可以定义一个便捷初始化程序,以针对特定用例或输入值类型创建该类的实例。

它们记录在这里


0

例如,如果您需要自定义init的弹出窗口,则可以使用以下方法:

创建一个将超级初始化与nibName和bundle结合使用的自定义初始化,然后访问view属性以强制加载视图层次结构。

然后,在viewDidLoad函数中,可以使用初始化中传递的参数来配置视图。

import UIKit

struct Player {
    let name: String
    let age: Int
}

class VC: UIViewController {


@IBOutlet weak var playerName: UILabel!

let player: Player

init(player: Player) {
    self.player = player
    super.init(nibName: "VC", bundle: Bundle.main)
    if let view = view, view.isHidden {}
}

override func viewDidLoad() {
    super.viewDidLoad()
    configure()
}

func configure() {
    playerName.text = player.name + "\(player.age)"
}
}

func showPlayerVC() {
    let foo = Player(name: "bar", age: 666)
    let vc = VC(player: foo)
    present(vc, animated: true, completion: nil)
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.