Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

self.window is always nil

Tags:

macos

swift

I'm currently trying to display a window with a window controller. That is what I have:

NSWindow subclass

import Cocoa
import CoreLocation

class TweetWindow: NSWindow {

var locationManager: CLLocationManager!
var geoCoder: CLGeocoder!

@IBAction func tweetButtonPressed(sender:NSButton) {

}

func initialize() {
    self.titleVisibility = NSWindowTitleVisibility.Hidden;
    self.locationManager = CLLocationManager();
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
    self.locationManager.distanceFilter = 10;

    self.geoCoder = CLGeocoder();
}

func windowWillShow() {
    if !self.visible {
        let systemAppearanceName = (NSUserDefaults.standardUserDefaults().stringForKey("AppleInterfaceStyle") ?? "Light").lowercaseString;
        let systemAppearance = systemAppearanceName == "dark" ? NSAppearance(named: NSAppearanceNameVibrantDark) : NSAppearance(named: NSAppearanceNameVibrantLight);
        self.appearance = systemAppearance;

        self.locationManager.startUpdatingLocation();
    }
}

func windowWillClose() {
    self.locationManager.stopUpdatingLocation();
}
}

NSWindowController subclass:

import Cocoa

class TweetWindowController: NSWindowController {

var tweetWindow: TweetWindow { return self.window as! TweetWindow; }

override func windowDidLoad() {
    super.windowDidLoad()
    self.tweetWindow.initialize()
    // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}

override func showWindow(sender: AnyObject?) {
    self.tweetWindow.windowWillShow()
    super.showWindow(sender)
}
}

Of course, I've got a .xib-file, too, that contains my window. It is called "TweetWindow.xib". Now, so far this should be ok.

In my AppDelegate.swift I do the following:

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

var tweetWindowController:TweetWindowController!;

func applicationDidFinishLaunching(aNotification: NSNotification) {
    // Insert code here to initialize your application
    tweetWindowController = TweetWindowController(windowNibName: "TweetWindow");
}

func applicationWillTerminate(aNotification: NSNotification) {
    // Insert code here to tear down your application
}

func showTweetWindowInternal() {
    NSApp.activateIgnoringOtherApps(true);
    tweetWindowController.showWindow(nil);
}

@IBAction func showTweetWindow(sender: AnyObject) {
    showTweetWindowInternal();
}

@IBAction func quitApp(sender: AnyObject) {
    NSApplication.sharedApplication().terminate(self);
}
}

My problem is the following: When I try to click on my button that is associated with the IBAction down there to show the window, an exception is thrown here: var tweetWindow: TweetWindow { return self.window as! TweetWindow; }

It says fatal error: unexpectedly found nil while unwrapping an Optional value, so window is nil.

Why is window nil there? Am I trying to access the value too early or something?

Here are some photos:

Window

File's Owner

File's Owner Identity Inspector

Thanks.

like image 277
Dominic B. Avatar asked Aug 10 '15 16:08

Dominic B.


1 Answers

Initializing an instance of NSWindowController or a subclass does not load the NIB. The NIB is not loaded until the window property is accessed or the showWindow() method is called (which basically accesses the window property indirectly). In your case, since you're overriding showWindow(), it's important to know that the NIB is not loaded until the superclass implementation is called.

So, yes, your call to self.tweetWindow.windowWillShow() in your showWindow() override, before calling through to super, is too early. The NIB has not been loaded at that point, so the window outlet has not been connected to anything.

Of course, you have to make sure the outlet is actually connected in the NIB or it will never be connected, even after the NIB is loaded. But trying to access it before it's loaded is the first problem.

I think your windowWillShow() method is misguided, at least as implemented. The window can be shown in various ways, not just by the window controller's showWindow() method. For example, something outside of both the window and the window controller could do tweetWindowController.window.makeKeyAndOrderFront(nil). If you really want to do something like this, have the window class override the various order...() methods to see if the window is being ordered in for the first time and, if so, call your method.


Update:

You have several things misconfigured in your NIB. Here's what you need to do to fix them:

  • Break the current connection from the "delegate" outlet of File's Owner to the window. Click the "x" button seen in either of the screenshots you posted.
  • Change the class of File's Owner. It is currently TweetWindow. It should be TweetWindowController. The controller is what loads and owns the NIB, so the class of File's Owner should be the controller class.
  • Connect the "window" outlet of File's Owner to the window.
  • Connect the "delegate" outlet of the window to File's Owner.
like image 185
Ken Thomases Avatar answered Nov 15 '22 19:11

Ken Thomases