How do I make a custom initializer for a UIViewController subclass in Swift?

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

我想子类 UIViewController,并有一个自定义初始值设定项,让我设置它在代码容易。我在斯威夫特做这个有点困难。

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

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

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

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

如何将我的特殊初始化器添加到 UIViewController子类中?

87010 次浏览

方便的初始值设定项是次要的,它支持 可以定义一个方便的初始值设定项来调用指定的 方便的初始值设定项来自同一类 指定初始化程序的一些参数设置为默认值。 还可以定义一个方便的初始值设定项,以创建 该类用于特定的用例或输入值类型。

他们是记录在案的 给你

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
    

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

例如,如果您需要一个自定义 init 来实现 popover,您可以使用以下方法:

创建一个自定义 init,使用带有 nibName 和 bundle 的 super init,然后访问 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)
}

对于那些用代码编写 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")
}
}

这与其他答案非常相似,但有一些解释。接受的答案是误导性的,因为它的属性是 可以选择,并没有暴露这样一个事实,即您的 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")
}
}

你基本上必须放弃从故事板加载它。为什么?

Because when you call a viewController storyboard.instantiateViewController(withIdentifier: "viewController") then UIKit will do its thing and call

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

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

instantiateViewController(withIdentifier:)的文档:

使用此方法创建要显示的视图控制器对象 每次调用此方法时,它都会创建一个新的 使用 init(coder:)方法的视图控制器的实例。

然而,对于编程创建的 viewController 或 nib 创建的 viewController,您可以重定向该调用,如上所示。