Custom init for UIViewController in Swift with interface setup in storyboard

后端 未结 12 2054
长情又很酷
长情又很酷 2020-11-27 05:08

I\'m having issue for writing custom init for subclass of UIViewController, basically I want to pass the dependency through the init method for viewController rather than se

相关标签:
12条回答
  • 2020-11-27 05:30

    One way that I've done this is with a convenience initializer.

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

    Then you initialize your MemeDetailVC with let memeDetailVC = MemeDetailVC(theMeme)

    Apple's documentation on initializers is pretty good, but my personal favorite is the Ray Wenderlich: Initialization in Depth tutorial series which should give you plenty of explanation/examples on your various init options and the "proper" way to do things.


    EDIT: While you can use a convenience initializer on custom view controllers, everyone is correct in stating that you cannot use custom initializers when initializing from the storyboard or through a storyboard segue.

    If your interface is set up in the storyboard and you're creating the controller completely programmatically, then a convenience initializer is probably the easiest way to do what you're trying to do since you don't have to deal with the required init with the NSCoder (which I still don't really understand).

    If you're getting your view controller via the storyboard though, then you will need to follow @Caleb Kleveter's answer and cast the view controller into your desired subclass then set the property manually.

    0 讨论(0)
  • 2020-11-27 05:33

    As of iOS 13 you can initialize the view controller that resides in a storyboard using: instantiateViewController(identifier:creator:) method on the UIStoryboard instance.

    tutorial: https://sarunw.com/posts/better-dependency-injection-for-storyboards-in-ios13/

    0 讨论(0)
  • 2020-11-27 05:33

    Correct flow is, call the designated initializer which in this case is the init with nibName,

    init(tap: UITapGestureRecognizer)
    {
        // Initialise the variables here
    
    
        // Call the designated init of ViewController
        super.init(nibName: nil, bundle: nil)
    
        // Call your Viewcontroller custom methods here
    
    }
    
    0 讨论(0)
  • 2020-11-27 05:36

    Swift 5

    You can write custom initializer like this ->

    class MyFooClass: UIViewController {
    
        var foo: Foo?
    
        init(with foo: Foo) {
            self.foo = foo
            super.init(nibName: nil, bundle: nil)
        }
    
        public required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            self.foo = nil
        }
    }
    
    0 讨论(0)
  • 2020-11-27 05:37

    As @Caleb Kleveter has pointed out, we can't use a custom initializer while initialising from a Storyboard.

    But, we can solve the problem by using factory/class method which instantiate view controller object from Storyboard and return view controller object. I think this is a pretty cool way.

    Note: This is not an exact answer to question rather a workaround to solve the problem.

    Make class method, in MemeDetailVC class, as follows:

    // 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
    }
    

    Usage:

    let memeDetailVC = MemeDetailVC.init(meme: Meme())
    
    0 讨论(0)
  • 2020-11-27 05:38

    You can't use a custom initializer when you initialize from a Storyboard, using init?(coder aDecoder: NSCoder) is how Apple designed the storyboard to initialize a controller. However, there are ways to send data to a UIViewController.

    Your view controller's name has detail in it, so I suppose that you get there from a different controller. In this case you can use the prepareForSegue method to send data to the detail (This is Swift 3):

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

    I just used a property of type String instead of Meme for testing purposes. Also, make sure that you pass in the correct segue identifier ("identifier" was just a placeholder).

    0 讨论(0)
提交回复
热议问题