2

I recently upgraded my project to Swift and I'm trying to figure out how to instantiate my XIBs dynamically while maintaining strong references to my viewcontrollers. I hate having to call NSBundle.mainBundle().loadNibNamed("someXib") because having a string-representation of your viewcontroller can make it prone to typos, hard to remember what each XIB is named, and very difficult to rename a XIB if I so choose.

In objective-c, I would simply instantiate the viewcontroller and push it like so:

CRMyViewController *myVC = [CRMyViewController new];
[self.navigationController presentViewController:myVC animated:YES completion:nil];

This allows me to strongly-reference my viewcontrollers (and their associated XIB) and if I misspelled or renamed my viewcontroller, my project would simply not compile.

When I tried this approach referencing a Swift-XIB, my view never loaded. However I was able to get close to what I wanted by simply overriding loadView inside my viewcontroler:

override func loadView() {              
    NSBundle.mainBundle().loadNibNamed("CRMyViewController", owner:self, options:nil) 
}

This is close to what I want since I only hard-code the xib name once (and inside the actual viewcontroller) but I'd still prefer a more dynamic approach.

Is there any way to load the Swift-XIB along with the viewcontroller dynamically? The only thing I can think of is to create my own base viewcontroller and override the loadView in a more dynamic fashion:

override func loadView() {        
    var className:NSString = NSStringFromClass(self.classForCoder)
    className = className.componentsSeparatedByString(".").last as NSString
    NSBundle.mainBundle().loadNibNamed(className, owner:self, options:nil)
}

Is there a better approach to load the XIB along with the viewcontroller? How come this is necessary in Swift and not objective-c?

Oren
  • 5,055
  • 3
  • 34
  • 52
  • I don't quite understand the first part of your question. Instantiating a new view controller in Objective C shouldn't have loaded your nib either unless the view controller was loading the nib in code inside of itself. It should be no different in Swift. There was no way in Objective C to load a nib without specifying the nib name just as there is no way in Swift. – bjtitus Jan 26 '15 at 00:13
  • That's how I do it every time in objective-c. All I do is instantiate the viewcontroller and present it and my xib/view is loaded along with it. I didn't have to do anything fancy like the swift code above. – Oren Jan 26 '15 at 00:23
  • It looks like I am wrong. The nib documentation mentions the linking of view controllers and an associated nib. I've never used it like that before. Seems fragile. I'll try to dig in more and see what's going on. – bjtitus Jan 26 '15 at 00:32
  • My initial thought is that, because the Swift class name is not going to be the same as in Objective C, the `_loadViewFromNibNamed` will not work. I'll try to type up some more details later. – bjtitus Jan 26 '15 at 00:45

1 Answers1

5

You can dictate the name of a class by specifying it in an @objc block:

@objc(CRMyViewController) class CRMyViewController: UIViewController {

}

This should fix the issue with _loadViewFromNibNamed:bundle: using the wrong name.

You can find out a little more about how class names work in Swift here: Get class name of object as string in Swift

Community
  • 1
  • 1
bjtitus
  • 4,231
  • 1
  • 27
  • 35
  • You're right, that worked! So you think the problem was swift was getting the wrong name? To be honest though, I'm not sure your solution is better than my baseviewcontroller overriding loadview. I rather not have to remember to wrap each class with @objc. It's much easier to just inherit from an existing baseviewcontroller that I can continue extending. +1 for an answer that works. -1 to swift for being dumb – Oren Jan 26 '15 at 01:27
  • @Oren The benefit to doing it this way is that any other private API relying on the class name will work. In your solution to splitting on the `.` you are relying on a specific implementation of the class name string, even though this is unlikely to change, it could and you would probably have no warning. – bjtitus Jan 26 '15 at 02:02
  • Why wouldn't xcode just add that by default? Is there any harm/repercussions? – Oren Jan 26 '15 at 04:03