Reputation: 2361
Using Swift 1.1 and Xcode 6.2.
I have a UIStoryboard containing a singular, custom UIViewController
subclass. On it, I have an @IBOutlet
connection of type UIView
from that controller to a UIView
subclass on the storyboard. I also have similar outlets for subviews of that view. See figure A.
But at run time, these properties are nil (Figure B). Even though I have assured I've connected the outlets in Interface Builder.
Thoughts:
awakeFromNib:
is not getting called for some reasonThings I have tried:
@IBOutlet
and storyboard item types exactly (instead of UIView
)Figure A*
Figure B
*The obscured code in Figure A
is:
@IBOutlet private var annotationOptionsView: UIView!
@IBOutlet private var arrivingLeavingSwitch: UISegmentedControl!
Thank you.
Upvotes: 127
Views: 98404
Reputation: 548
It seems nobody provided the solution that helped in my case, so I'll post it.
When you instantiate a new viewController from storyboard, a method init?(coder:)
is invoked.
In my case this bug happened because I forgot to call super
here:
required init?(coder aDecoder: NSCoder) {
super.init(nibName: nil, bundle: nil) // this is incorrect, UIViewController doesn't see IBOutlets
}
After I fixed this stupid error, eveyrthing worked for me:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) // Now we're talking!
}
Upvotes: 0
Reputation: 908
I had this issue with a custom UITableViewCell
. It turns out the issue was that I was accessing the cell's IBOutlets during init(style:reuseIdentifier:)
. This was a mistake, because the outlets have not yet been connected when init
runs.
The solution was to wait until awakeFromNib()
to access the IBOutlets.
Upvotes: 0
Reputation: 154
If you have two storyboards/xibs, one for each 'class level' in an inheritance hierarchy, the outlet must be defined in each, and referenced in whichever class needs to operate on it. in my case, it was the parent class.
Upvotes: 0
Reputation: 12582
Say you have perhaps a container view that shows some sort of clock. So you have
class Clock: UIViewController
You actually use it in a number of places in the app.
On the main screen, on the details screen, on the edit screen.
You have a complicated snapchat-like modern app.
Clock
may actually be loaded more than once at the same time somewhere on the same screen. (Maybe it's hidden in some cases.)You start working on one instance of Clock
on one of your many storyboards.
On that storyboard you add a label, NewLabel.
Naturally you add the outlet in code. Everything should work. All the other outlets work perfectly.
You have definitely linked the outlet.
But the app crashes with NewLabel as nil.
Xcode clearly tells you "you forgot to connect the outlet".
The crash is actually not from the place you are working - it's from another storyboard, where there is no "NewLabel" item on that storyboard!!!
Frustrating.
Upvotes: 7
Reputation: 578
100% Working solution for creating ViewControllers from XIB without StoryBoards
7.1.
// creating instance
let controller = CustomViewController()
7.2.
// connecting view/xib with controller instance
let bundle = Bundle(for: type(of: controller))
bundle.loadNibNamed("CustomViewControllerView", owner: controller, options: nil)
7.3.
// get/set outlets
controller.labelOutlet.text = "title"
controller.imageOutlet.image = UIImage(named: "image1")
Upvotes: 2
Reputation: 385500
Typically this happens because your view controller hasn't loaded its view hierarchy yet. A view controller only loads its view hierarchy when something sends it the view
message. The system does this when it is time to actually put the view hierarchy on the screen, which happens after things like prepareForSegue:sender:
and viewWillAppear:
have returned.
Since your VC hasn't loaded its view hierarchy yet, your outlets are still nil.
You could force the VC to load its view hierarchy by saying _ = self.view
.
Upvotes: 176
Reputation: 16246
In my case, the app started crashing all of a sudden.
Debugging it revealed that all outlets were still nil
at the time of viewDidLoad()
.
My app still uses nibs (not storyboards) for most view controllers. Everything was in place, all outlets wired properly. I double-checked.
We typically instantiate our view controllers as
let newVC = MYCustomViewController()
...which for some reason seems to work as long as the .xib is named the same as the view controller class (not sure how that works, though. We are not calling init(nibName:bundle:)
with nil arguments, or overriding init()
to do so on self
like it is typically suggested...).
So I tried to explicitly call
let newVC = MYCustomViewController(nibName: "MYCustomViewController", bundle: .main)
...only to be greeted with the runtime exception error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle </Users/nicolasmiari/Library/Developer/CoreSimulator/Devices/3DA3CF21-108D-498F-9649-C4FC9E3C1A8D/data/Containers/Bundle/Application/C543DDC1-AE86-4D29-988C-9CCE89E23543/MyApp.app> (loaded)' with name 'MYCustomViewController''
And then, I saw it:
The "Target Membership" checkbox of the .xib file was unchecked.
Must have happened when resolving one of the frequent merge conflicts regarding the Xcode project file.
Apple definitely needs to come up with a project file format that is more SCM-friendly.
Upvotes: 3
Reputation: 9734
If you instantiate view controller
through programmatically. Then
try creating it like below
let initialVC = self.storyboard?.instantiateViewController(withIdentifier: "InitialVC") as! InitialVC
instead of directly
let initialVC = InitialVC()
This worked for me.
Upvotes: 10
Reputation: 13963
If you have two main.storyboards
and you are making changes to the wrong one this can happen. This can happen anytime you connect an outlet from an uninstantiated storyboard
.
Upvotes: 0
Reputation: 660
You can validate if the is view is loaded.
if isViewLoaded && view.window != nil {
//self.annotationOptionsView.
}
Upvotes: 1
Reputation: 722
Check to see if you have any missing or disconnected outlets.
Upvotes: 1
Reputation: 1111
Got one more ...
If you have a custom class for a UITableViewCell but forget to specify Custom in the Style of the cell.
Upvotes: 1
Reputation: 5380
Yet another case I just ran into. I changed the name of my class for the UIViewController, but I forgot to change the name of the .xib file where the interface was built.
Once I caught this and made the file names reflect the class name, it was all good!
I hope that helps someone.
Upvotes: 1
Reputation: 1919
I see you use ViewController
!? in ViewController
class you must use -viewDidLoad
, not -awakeFromNib
, -awakeFromNib
use for UIView
class
Upvotes: 0
Reputation: 707
I had a similar issue when I had previously added register(_:forCellReuseIdentifier:) for the custom cell after I had already defined the identifier in the storyboard. Had this code in the viewDidLoad() function. Once I removed it, it worked fine.
Upvotes: 2
Reputation: 2290
For me, this was crashing because containerView
was nil.
Here is my code with Crash.
@IBOutlet private var containerView: UIView! // Connected to Storyboard
override open func loadView() {
containerView.addSubview(anotherView)
}
The missing thing was calling the super.loadView()
. So adding it solved problem for me.
Fixed Code:
@IBOutlet private var containerView: UIView!
override open func loadView() {
super.loadView()
containerView.addSubview(anotherView)
}
Upvotes: 1
Reputation: 1537
I had the same problem after copying a class (linked to a xib) to reuse it with another viewcontroller class (linked to a storyboard). I forgot to remove
override var nibName
and
override var nibBundle
methods.
After removing them, my outlets started to work.
Upvotes: 0
Reputation: 27050
Accidently I subclassed my view controller with AVPlayerViewController
instead of UIViewController
. By replaying it to UIViewController
things back normal. This should help.
No build cleaning (normal&full), removing derived data folders and quitting Xcode worked for me.
Upvotes: 0
Reputation: 261
You need to load the view hierarchy first in order to instantiate the outlets in the storyboard. For this, you can manually call the loadView or loadViewIfNeeded methods.
Upvotes: 2
Reputation: 2997
For me, I had same error on a localized storyboard, an element was added in some locale and not in the other, so I had null reference for that element when switched to the missing element locale, I had to remove (redundant) localization for that storyboard using https://stackoverflow.com/a/42256341/1356559.
Upvotes: 1
Reputation: 1131
In my case, it happened because I overriden the loadView
method in my ViewController subclass, but forgot to add [super loadView]
-(void)loadView {
// blank
}
When you override the loadView
method, the it is your responsibility to init your subviews. Since you override it, the views from interface builder do not get the chance to convert to cocoa objects and thus outlets remain nil.
If you implement loadView in your view controller subclass, then it becomes your responsibility load the UI elements from from storyboard/xib into code.
Or just call
[super loadView];
So that the superclass gets the chance to load storyboard/xib into code.
Upvotes: 14
Reputation: 5789
For me, this occurred when I accidentally declared my view controller's class as
class XYZViewController: UINavigationController {
}
(ie as a UINavigationController
not a UIViewController
).
Xcode doesn't pick up on this mistake, the class seems to build fine, and override functions such as viewDidLoad
, viewWillAppear
, etc. all work correctly. But none of the IBOutlet
s get connected.
Changing the declaration to
class XYZViewController: UIViewController {
}
fixed it completely.
Upvotes: 7
Reputation: 10752
This happened for me because I was accidentally instantiating my view controller directly instead of instantiating it through the storyboard. If you instantiate directly via MyViewController()
then the outlets won't be connected.
Upvotes: 19
Reputation: 54504
You can call controller.view
to force to load the view to initialize the IBOutlets, then you will be able to assign the values.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "identifier") {
let controller = segue.destinationViewController as! YourController
let _ = controller.view //force to load the view to initialize the IBOutlets
controller.your_IBOutlet_property = xxx
...
controller.delegate = self
}
}
Upvotes: 8
Reputation: 9387
This was happening to me with my custom collection view cell. Turns out I had to replace my registerClassforReuseIdentifier method with registerNib. That fixed it for me.
Upvotes: 15
Reputation: 4097
Other case:
Your outlets won't get set until the view controller's view is actually instantiated, which in your case is probably happening shortly after initWithNibName:bundle:—at which point they'll still be nil. Any setup you do that involves those outlets should be happening in your view controller's -viewDidLoad method.
Upvotes: 1
Reputation: 1100
Check your IBOutlet connection if it connected to the File owner or the view. There could be mistakes.
Upvotes: 1
Reputation: 109
I encounter this problem recently! Here is my thought. The problem is not about you storyboard or any link issue. It is about how you initiate your ViewController. Especially when you are using Swift.(There is barely nothing in the editor when you create a class file)
By simply using the init()
from super class can not initiate anything you worked with story board. So what you need to do is changing the initialisation of the ViewController. Replace
let XXViewController = XXViewController()
by
let XXViewController = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("XXViewController") as! XXViewController
This tells the program to go to the storyboard find XXViewController and initiates all IBOutlet
in your storyboard.
Hope this help~ GL
Upvotes: 7
Reputation: 3394
.h
and .m
view controller filesUpvotes: 0