提问者:小点点

自定义初始化的UIViewController在Swift与接口设置在故事板


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

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

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中初始化属性……

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

我怀疑我的viewController的init有问题,它总是会返回selfinit?(编码器aDecoder:NSCoder),它没有实现。

有人知道如何正确地为您的viewController创建自定义init吗?注意:我的viewController的界面设置在故事板中

这是我的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)
    }

}

共3个答案

匿名用户

正如上面的答案之一所指明的,您不能同时使用自定义初始化方法和故事板。

但是您仍然可以使用静态方法从故事板实例化ViewController并对其执行额外的设置。

它看起来像这样:

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

别忘了在故事板中指定IdfierOfYouViewController作为视图控制器标识符。您可能还需要在上面的代码中更改故事板的名称。

匿名用户

当您从Storyboard初始化时,您不能使用自定义初始化程序,使用init?(coder aDecoder: NSCoder)是Apple设计故事板初始化控制器的方式。但是,有一些方法可以将数据发送到UIViewController

您的视图控制器的名称中有详细信息,所以我假设您是从不同的控制器到达那里的。在这种情况下,您可以使用准备ForSegue方法将数据发送到详细信息(这是Swift 3):

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

我只是使用了String类型的属性而不是Meme用于测试目的。此外,请确保传入正确的segue标识符(“标识符”只是一个占位符)。

匿名用户

正如@Caleb Kleveter所指出的,我们不能在从故事板初始化时使用自定义初始化程序。

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

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

make类方法,在MemeDetailVC类中,如下:

// 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())