在Swift中用UIViewController的自定义init,在storyboard中设置界面

我有问题为UIViewController的子类编写自定义的init,基本上我想通过viewController的init方法传递依赖,而不是像viewControllerB.property = value直接设置属性

所以我为我的viewController做了一个自定义的init,并调用超级指定的init

 init(meme: Meme?) { self.meme = meme super.init(nibName: nil, bundle: nil) } 

视图控制器界面驻留在故事板中,我也使自定义类的界面成为我的视图控制器。 即使你没有在这个方法中做任何事情,Swift也需要调用这个init方法。 否则,编译器会抱怨…

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

问题是,当我尝试使用MyViewController(meme: meme)调用我的自定义init时,它并没有在我的viewController中初始化属性…

我试图debugging,我发现在我的viewController, init(coder aDecoder: NSCoder)先调用,然后我的自定义的init后来调用。 但是这两个init方法返回不同的self内存地址。

我怀疑我的viewController初始化有什么问题,它总是会返回selfinit?(coder aDecoder: NSCoder) ,没有实现。

有谁知道如何正确地为您的viewController自定义初始化? 注意:我的viewController的界面是在storyboard中设置的

这里是我的viewController代码:

 class MemeDetailVC : UIViewController { var meme : Meme! @IBOutlet weak var editedImage: UIImageView! // TODO: incorrect init init(meme: Meme?) { self.meme = meme super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override func viewDidLoad() { /// setup nav title title = "Detail Meme" super.viewDidLoad() } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) editedImage = UIImageView(image: meme.editedImage) } } 

正如在上面的答案中指定的那样,你不能同时使用自定义的init方法和storyboard。 但是您仍然可以使用静态方法从故事板实例化ViewController并对其执行其他设置。 它看起来像这样:

 class MemeDetailVC : UIViewController { var meme : Meme! static func makeMemeDetailVC(meme: Meme) -> MemeDetailVC { let newViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("IdentifierOfYouViewController") as! MemeDetailVC newViewController.meme = meme return newViewController } } 

不要忘了在Storyboard中指定IdentifierOfYouViewController作为视图控制器标识符。 也不要在你的viewDidLoad中使用meme var,因为它在这个时候还没有被初始化。 您可能还需要在上面的代码中更改故事板的名称。

当你使用init?(coder aDecoder: NSCoder)初始化Storyboard的时候,你不能使用自定义的初始化init?(coder aDecoder: NSCoder)苹果是如何devise故事板来初始化控制器的。 但是,有办法将数据发送到UIViewController

您的视图控制器的名称中有detail ,所以我想你从一个不同的控制器。 在这种情况下,您可以使用prepareForSegue方法将数据发送到详细信息(这是Swift 3):

 override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "identifier" { if let controller = segue.destinationViewController as? MemeDetailVC { controller.meme = "Meme" } } } 

我只是使用Stringtypes的属性而不是Meme来进行testing。 此外,请确保您传递正确的segue标识符( "identifier"只是一个占位符)。

我做的一个方法是使用一个方便的初始化器。

 class MemeDetailVC : UIViewController { convenience init(meme: Meme) { self.init() self.meme = meme } } 

然后你初始化你的MemeDetailVC let memeDetailVC = MemeDetailVC(theMeme)

苹果有关初始化程序的文档是相当不错的,但是我个人最喜欢的是Ray Wenderlich:深入初始化教程系列,它会给你很多关于你的各种初始化选项的解释/例子和“正确”的做法。


编辑 :虽然您可以在自定义视图控制器上使用便利初始值设定项,但是每个人在说明从故事板或通过故事板继续初始化时都不能使用自定义初始值设定项是正确的。

如果您的界面是在故事板中设置的,而且您是以编程方式完全创build控制器,那么便捷初始化程序可能是最简单的方法来执行您要做的事情,因为您不必处理所需的init NSCoder(我仍然不太明白)。

如果你通过storyboard获得你的视图控制器,那么你需要遵循@Caleb Kleveter的回答 ,并将视图控制器转换为你想要的子类,然后手动设置属性。

最初有几个答案,即使它们基本正确,也被牛投票和删除。 答案是,你不能。

从故事板定义工作时,您的视图控制器实例全部归档。 因此,要初始化它们,需要使用init?(coder...coder是所有设置/视图信息的来源。

所以,在这种情况下,用自定义参数调用一些其他的init函数是不可能的。 它应该在准备阶段时被设置为属性,或者可以从情节串联板直接join实例并configuration它们(基本上是使用故事板的工厂模式)。

在所有情况下,您都使用SDK所需的初始化函数,然后传递其他参数。

UIViewController类符合NSCoding协议,定义如下:

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

所以UIViewController有两个指定的初始化程序init?(coder aDecoder: NSCoder)init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)

Storyborad直接调用init?(coder aDecoder: NSCoder)来初始化UIViewControllerUIView ,没有空间让你传递参数。

一个麻烦的解决方法是使用临时caching:

 class TempCache{ static let sharedInstance = TempCache() var meme: Meme? } TempCache.sharedInstance.meme = meme // call this before init your ViewController required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder); self.meme = TempCache.sharedInstance.meme } 

正如@Caleb Kleveter指出的那样,我们不能在从Storyboard初始化的时候使用自定义的初始化器。

但是,我们可以通过使用从Storyboard实例化视图控制器对象的工厂/类方法来解决问题,并返回视图控制器对象。 我认为这是一个非常酷的方式。

注意:这不是问题的确切答案,而是解决问题的解决方法。

在MemeDetailVC类中创build类方法,如下所示:

 // Considering your view controller resides in Main.storyboard and it's identifier is set to "MemeDetailVC" class func `init`(meme: Meme) -> MemeDetailVC { let storyboard = UIStoryboard(name: "Main", bundle: nil) let vc = storyboard.instantiateViewController(withIdentifier: "MemeDetailVC") as? MemeDetailVC vc.meme = meme return vc } 

用法:

 let memeDetailVC = MemeDetailVC.init(meme: Meme())