IBOutlet is nil, but it is connected in storyboard, Swift
Asked Answered
P

33

126

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:

  • Is it possible that because I am using a subclass of a subclass something messes up with the initialization? I am not overriding any initializers
  • awakeFromNib: is not getting called for some reason
  • Maybe it doesn't connecting to subviews on subviews

Things I have tried:

  • Matching @IBOutlet and storyboard item types exactly (instead of UIView)
  • Deleting property and outlet and re-added them

Figure A

Figure A*

Figure B

Figure B

*The obscured code in Figure A is:

@IBOutlet private var annotationOptionsView: UIView!
@IBOutlet private var arrivingLeavingSwitch: UISegmentedControl!

Thank you.

Pia answered 28/3, 2015 at 19:9 Comment(5)
Why not change the ! to ?Cowpuncher
clearView is nil because is not linked to the storyboard (see the circle on the left of the code with a hole, that indicates that is not linked), in the screenshot I can't see the declaration of annotationOptionView.Carborundum
@JavierFloresFont: clearView I expect to be nil. It is something I have yet to refactor. Also see the footnote for figure A. @ShaanSingh It should be ! because connections from storyboards are (supposed to be) set at runtime and should not be nil.Pia
How does this view controller get loaded? Show us the code that asks for it, or describe the segue that connects to it.Katar
It is getting the right storyboard: let vc = UIStoryboard(name: "LocationPickerModal", bundle: nil) .instantiateViewControllerWithIdentifier("LocationPickerModalViewController") as LocationPickerModalViewControllerPia
P
21

The storyboard wasn't recognizing any further UI things I added to it. At run time all the references were nil. So I cleared my derived data folder and then those connections worked again.

Pia answered 29/3, 2015 at 2:4 Comment(6)
This is the answer. I fought this for quite a while, cleaning the build folder and everything. In my case, in viewDidLoad() all the connections were made, but in viewWillAppear() almost all of them were nil (sometimes one or two were still connected). I tried everything and finally deleted the derived data folder, rebuilt, and all was well.Toomin
Even after 3 years, Xcode 9.2 is still has same god damn bug. Thank you so much.Wroughtup
Here to report that this still exists in Xcode 11. :(Syncopate
@KemalCanKaynak 5 years later on xcode 12 same bug ROFLNormative
after 7 years this bug is still alive on xcode 13...Magdeburg
Didn't work for me on Xcode 14Bioscopy
K
176

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.

Katar answered 28/3, 2015 at 20:41 Comment(13)
This is occurring when viewWillAppear: is called, the first time I do anything with that outlet. Accessing the view does not do anything for me. Still nil.Pia
Thanks! This worked for me as well. var v = viewcontroller.view; forced to load the view hierarchy. Nice little trick! ;)Harmaning
@YordiUytersprot where would you put this line?Afteryears
Well, if you instantiate a viewcontroller: let vc = self.storyboard?.instantianteViewControllerWithIdentifier("StoryboardIDfromVC") as? CustomVC; let view = vc.view; // Right now, you can access IBOutlets from CustomVC here.. vc.customTextField.text = "Lalala"; Hope it helps you! :)Harmaning
I run so often into this issue and it's always the same. And I always wonder the first five minutes or so... ;-)Asa
Just ran into this issue and was surprised to quickly find this fix. It seems to work for me, but I'm curious if there is a better way as this seems like a totally unnecessary hack.Gamic
Sadly objc and swift behave differently. In objc the outlets are accessible even if viewdidload was not called, in swift are not accessible. I'm just struggling with this but i'm in a loop of dependencies and i must find another way to fix it.Sevastopol
we like to put let _ = self.view in awakeFromNib(), especially when the controller implementation will be built into a framework.Fluorometer
Expression resolves to an unused l-value is what I get when I do self.view, also let _ = self.view didn't give the view a value for me.Schwaben
UIViewcontroller.loadViewIfNeeded() is the right method to use here.Localism
Sounds like a good method to use. It was added in iOS 9, which was released after I wrote this answer.Katar
So should i always use prepare to setup my viewcontroller?Therefor
Really these days you would just use a container view with the other storyboard dynamically loaded - this is, thank God, finally very easy to do these days. the couple of lines of code you need is given at the end of this long explanation: https://mcmap.net/q/17273/-how-to-add-a-subview-that-has-its-own-uiviewcontroller-in-objective-cBalkh
A
31

Have you tried running Product > Clean. Solved a very similar problem for me.

Alsacelorraine answered 6/4, 2015 at 1:23 Comment(4)
I did. Sorry, meant to mark my answer. It only started working after I deleted the DerivedData folder for the project.Pia
Only two of my outlets in the middle of my view hierarchy were nil.. Product --> Clean worked for me!Backache
obj c, just cleaning the project worked for me... can't believe spent more than an hour and didn't even think to clean the project : / but thank you!!Andrews
Just Product -> Clean didn't work for me, but I started a different simulator and it worked, so there must be something wrong in the DerivedData folder as well.Barcellona
S
30

Did you instantiate your view controller from a Storyboard or NIB, or did you instantiate it directly via an initializer?

If you instantiated your class directly with the initializer, the outlets won't be connected. Interface Builder creates customized instances of your classes and encodes those instances into NIBs and Storyboards for repeated decoding, it doesn't define the classes themselves. If this was your problem, you just need to change the code where you create your controller to instead use the methods on UIStoryboard, or UINib.

Stoichiometric answered 28/3, 2015 at 21:49 Comment(1)
I am creating a UIStoryboard instance and calling instantiateViewControllerWithIdentifier on it.Pia
P
21

The storyboard wasn't recognizing any further UI things I added to it. At run time all the references were nil. So I cleared my derived data folder and then those connections worked again.

Pia answered 29/3, 2015 at 2:4 Comment(6)
This is the answer. I fought this for quite a while, cleaning the build folder and everything. In my case, in viewDidLoad() all the connections were made, but in viewWillAppear() almost all of them were nil (sometimes one or two were still connected). I tried everything and finally deleted the derived data folder, rebuilt, and all was well.Toomin
Even after 3 years, Xcode 9.2 is still has same god damn bug. Thank you so much.Wroughtup
Here to report that this still exists in Xcode 11. :(Syncopate
@KemalCanKaynak 5 years later on xcode 12 same bug ROFLNormative
after 7 years this bug is still alive on xcode 13...Magdeburg
Didn't work for me on Xcode 14Bioscopy
L
19

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.

Luane answered 15/10, 2016 at 19:5 Comment(2)
If you use XIB file, instantiate directly still works successfully.Narcosynthesis
It was my issue. Thanks.Endorse
S
15

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.

Slaver answered 26/11, 2015 at 22:16 Comment(2)
You are right. I read the ARC counting content in Swift Programming Language in detail. I can not find the reason, why the IBOutlet weak property still be nil. At last, I google for '@IBOutlet swift nil' and find your answer in SO. Then I marked out the line Xcode template added 'self.collectionView!.registerClass' in viewDidLoad. Then, it works. You save my life.Hydromechanics
wow no idea why this works but it does. thanks manEnsign
C
13

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.

Cream answered 13/1, 2017 at 9:38 Comment(0)
O
10

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.

Odoriferous answered 19/7, 2018 at 6:44 Comment(0)
G
8

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
    }
}
Guthrun answered 8/8, 2016 at 3:12 Comment(0)
S
7

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

Shackelford answered 3/11, 2015 at 8:29 Comment(1)
That wasn't the issuePia
L
7

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 IBOutlets get connected.

Changing the declaration to

class XYZViewController: UIViewController {
}

fixed it completely.

Lame answered 15/7, 2016 at 14:50 Comment(0)
B
7

2019, ONE POSSIBILITY FOR THIS HORRIBLE PROBLEM:

  • 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.

In fact, 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 reason is this .......... you have "NewLabel" on only one of the storyboard uses of Clock!

The crash is actually from >>> an other place <<<< you are using Clock!!!!

Xcode does not tell you the crash is from another place altogether, not from where you are working!

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.

Balkh answered 3/9, 2019 at 2:11 Comment(0)
S
3

For Swift 3.

func configureView() {
    let _  = self.view
}
Signally answered 9/4, 2017 at 14:36 Comment(0)
M
3

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.

Mcnew answered 3/9, 2018 at 2:20 Comment(1)
this change was driving me crazy, thanks the target was unchecked here too.Valerio
W
2

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.

Wheelbarrow answered 31/5, 2017 at 0:47 Comment(0)
K
2

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.

Kubetz answered 25/10, 2017 at 18:7 Comment(0)
S
2

100% Working solution for creating ViewControllers from XIB without StoryBoards

  1. Create class CustomViewController : UIViewController
  2. Create view CustomViewControllerView.xib
  3. In CustomViewControllerView.xib in Interface Builder select Placeholders -> File's Owner
  4. In "Attributes inspector" set Class to CustomViewController
  5. In "Connections inspector" connect "view" to top-level view of xib (ensure top-level view's Class is not pointing to CustomViewController)
  6. In "Connections inspector" connect other outlets (if needed/exist)
  7. Create an instance of CustomViewController in parent view controller/App delegate

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")
Sunset answered 17/10, 2018 at 3:24 Comment(2)
Fantastic answer, thanks! For what its worth, you can also override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) and call step 7.2 with self directly in the CustomViewController class.Phloem
If you don't want to work with a full UIViewController but instead only with a simple UIView, then there's no "view" in the "Connections inspector".Individualize
C
1

Check your IBOutlet connection if it connected to the File owner or the view. There could be mistakes.

Compensation answered 22/5, 2016 at 8:53 Comment(0)
S
1

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.

Simonette answered 3/6, 2016 at 12:16 Comment(0)
D
1

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://mcmap.net/q/176005/-how-to-remove-localization-from-file.

Deconsecrate answered 29/5, 2017 at 12:38 Comment(0)
W
1

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)
}
Whelan answered 25/9, 2017 at 14:25 Comment(1)
you shouldn't never call loadView() directly (developer.apple.com/documentation/uikit/uiviewcontroller/…), call self.view insteadWondering
A
1

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.

Agrarian answered 5/12, 2017 at 1:0 Comment(0)
F
1

Got one more ...

If you have a custom class for a UITableViewCell but forget to specify Custom in the Style of the cell.

Franco answered 13/2, 2018 at 2:15 Comment(0)
M
1

Check to see if you have any missing or disconnected outlets.

enter image description here

Mcnelly answered 2/3, 2018 at 13:49 Comment(0)
G
1

You can validate if the is view is loaded.

if isViewLoaded && view.window != nil {
 //self.annotationOptionsView.
}
Gynecium answered 28/5, 2018 at 16:13 Comment(0)
T
0
  1. select both .h and .m view controller files
  2. remove the reference of those files
  3. re-add the files to your project tree
  4. open the storyboard, eventually re-build the project
Taproot answered 10/9, 2015 at 5:52 Comment(0)
T
0

I see you use ViewController!? in ViewController class you must use -viewDidLoad, not -awakeFromNib, -awakeFromNib use for UIView class

Toxophilite answered 25/4, 2017 at 9:4 Comment(0)
D
0

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.

Drawer answered 23/7, 2017 at 5:52 Comment(0)
A
0

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.

Albinus answered 24/8, 2017 at 15:17 Comment(0)
S
0

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.

Schwaben answered 16/6, 2018 at 22:21 Comment(0)
S
0

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.

Syncopate answered 5/2, 2020 at 16:14 Comment(0)
W
0

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.

Walkling answered 3/5, 2023 at 5:57 Comment(0)
B
0

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!
} 
Bioscopy answered 20/9, 2023 at 9:24 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.