Can't Load UIViewController XIB file in Storyboard in Swift
Asked Answered
R

2

24

I've read Using XCode storyboard to instantiate view controller that uses XIB for its design but I'm having troubles making this work in Swift (Using Xcode 6 Beta 6). I'm wondering if I've done something wrong or if this functionality isn't available anymore?

I created a simple repository, https://github.com/jer-k/StoryboardTesting-Swift, that showcases the above approach.

I managed to solve the issue by adding and override to init

required init(coder aDecoder: NSCoder) {
    super.init(nibName: "TestViewController", bundle: NSBundle.mainBundle())
}

but I'm wondering if it is still possible to have the storyboard handle this for me. Creating a superclass to have all my UIViewControllers inherit from with the above code isn't the most cumbersome thing in the world, but I'm just curious at this point.

Rosco answered 28/8, 2014 at 0:32 Comment(2)
I was wondering when someone would ask about this... :)Dettmer
#27151526Sphacelus
D
41

What's happened is that Seed 5 broke the mechanism whereby a view controller can find its xib by name, if the view controller is a Swift class. The reason is that the name of the class, in Swift's mind, is not the same as the name you gave it (and the name you gave the xib file); the name has been "mangled", in particular by prepending the module name (i.e. Swift classes have namespacing).

I offer three workarounds:

  • Your workaround is a good one (load the .xib file by name explicitly)

  • Name the .xib file MyModule.TestViewController.xib, where MyModule is the name of your bundle (i.e. the name of the project) (this is what Apple advises, but I hate it)

  • Use @objc(TestViewController) before the view controller's class declaration to overcome the name mangling which is what's breaking the mechanism (this is the approach I favor)

See my discussion here: https://mcmap.net/q/66238/-all-iboutlets-become-nil-after-switching-to-xcode-6-beta-5 and my further discussion linked to from there: https://mcmap.net/q/66239/-are-view-controllers-with-nib-files-broken-in-ios-8-beta-5

EDIT This bug is fixed in iOS 9 beta 4. If the nib file search fails, iOS 9 now strips the module name off the view controller class name and performs the nib file search a second time.

Dettmer answered 28/8, 2014 at 0:35 Comment(19)
I have some slight hope that Apple will respond to my bug reports by fixing this, but the fact that they closed my bug with "works as intended" is discouraging.Dettmer
I see your discussion the second linked answer and I understand. Was it ever documented that we needed to start prepending the module name onto xibs? Maybe I missed it. Either way, awesome work. I'm going to use the @objc approach for now because it will be very easy to remove if they decide to drop the need for the prepended module name. If we come out of beta and it is finalized that we have to prepend the module name I may switch since it is their advised approach, but now I like your solution.Rosco
A caveat to add: I had to go back into the Storyboard and reset the name to pick up the un-mangled version, but it works like a charm.Rosco
"Was it ever documented that we needed to start prepending the module name onto xibs" No, it's just something Apple told me directly...Dettmer
the @objc(testViewController) approach doesn't work for me... apparently it has a "compiler error: Expected declaration".Cambrian
@StevenHernandez Then you're doing it wrong. I do this all the time. See for example github.com/mattneub/Programming-iOS-Book-Examples/blob/master/…Dettmer
AHH see now thats a better example of how to use it. With your first example I assumed incorrectly that I could just copy paste that sstatement anywhere inside the Class declaration of the ViewController.. Thanks for the help +1Cambrian
These are not working for me. Maybe because I am loading a xib from a framework into my storyboard.Schutt
@Schutt What does "into my storyboard" mean? You can't load a .xib "into a storyboard" in any way that I can make sense of.Dettmer
I mean what the OP said. To load a view controller from a xib in a storyboard. I got it working now but only the OP's suggestion works.Schutt
@Schutt I'm now (mentally) with you... Interesting stuff. Your issue might make a good separate question, because you're doing something different (because of the framework) - and then you could answer your own question and solve this for others. Just an idea, if you have time.Dettmer
Sometimes, and I don't know how this happens but IB will create this issue again and again, IB will not leave the module blank for some random Objective-C classes. In that case, hacking the storyboard works. This happened to some guy -- https://mcmap.net/q/63930/-xcode-6-bug-unknown-class-in-interface-builder-file - and also to me (another guy).Blayne
Had to manually remove this from the storyboard: customModule="xxxx" customModuleProvider="target"even though everything is in all targets.This only happens to me with one particular storyboard: not sure what makes it special.Blayne
@Dettmer Are the proposed fixes still working in XCode 9.1? I've tried them all and none work for me. ViewController is not loaded in the Storyboard, although it loads in the runtime, e.g. when I run the app in the simulator.Blackburn
@SrđanStanić The "fixes" have not been needed for several years. It sounds like you're having some other problem. I suggest you ask about it as a real question, giving actual details (and blip me if you want me to take a look).Dettmer
@Dettmer Thanks for your reply. Before I create a new question, I just want to make sure that it's a different issue. I've downloaded the repo from here github.com/jer-k/StoryboardTesting-Swift and in the main storyboard I would expect to see "Am I working" but instead I'm seeing grey Test View Controller, which means that its view did not load from the nib file. Is your experience or expectation different?Blackburn
That repo uses such an old version of Swift that I can't even open it. Instead of living in the past, try it yourself with a new project.Dettmer
@Dettmer Here is a new question if you want to take a look. I appreciate your assistance so far. #47229838Blackburn
@SrđanStanić So it turns out that your question has nothing to do with what I'm talking about here (or very little to do with it). You are seeing correct and expected behavior.Dettmer
M
2

Another answer is:

override func loadView() {
    if #available(iOS 9, *) {
        super.loadView()
    } else {
        let classString = String(self.dynamicType)
        NSBundle.mainBundle().loadNibNamed(classString, owner: self, options: nil)
    }
}

link: http://japko.net/2014/09/08/loading-swift-uiviewcontroller-from-xib-in-storyboard/

EDIT:

override var nibName: String? {
    get {
        let classString = String(self.dynamicType)
        return classString
    }
}
override var nibBundle: NSBundle? {
    get {
        return NSBundle.mainBundle()
    }
}

This looks more beautiful. Works on iOS 8/9 (maybe on iOS 7 too, who knows...))

Memphis answered 8/3, 2016 at 20:6 Comment(1)
The second answer works for me. Now I can test with iOS8 simulatorTroubadour

© 2022 - 2024 — McMap. All rights reserved.