Cocoa: How to set window title from within view controller in Swift? - xcode

I've tried to build on a Cocoa app which uses storyboard and Swift in Xcode 6. However, when I tried to alter the title of window from within NSViewController, the following code doesn't work.
self.title = "changed label"
When I wrote the above code in viewDidLoad() function, the resultant app's title still remains window.
Also, the following code causes an error, since View Controller doesn't have such property as window.
self.window.title = "changed label"
So how can I change the title of window programmatically in Cocoa app which is built on storyboard?

There are 2 problems with your code:
viewDidLoad is called before the view is added to the window
NSViewController does not have a window property
To fix the first one, you could override viewDidAppear(). This method is called after the view has fully transitioned onto the screen. At that point it is already added to a window.
To get a reference to the window title, you can access a view controller's window via its view: self.view.window.title
Just add the following to your view controller subclass, and the window title should change:
override func viewDidAppear() {
super.viewDidAppear()
self.view.window?.title = "changed label"
}

This worked for me, currentDict is NSDictionary passed from previous viewController
var currentDict:NSDictionary?
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if let myString:String = currentDict?["title"] as? String {
self.title = myString
}
}

Related

How to get state restoration to restore all windows/tabs on macOS?

I have a non-document based macOS AppKit app. It has one window instantiated automatically by the storyboard. I have sub-classed NSWindowController and added a override func newWindowForTab(_ sender: Any?) to enable the + button on the tab-bar. My main view controller lets the user rename the tab title and the window title is set to the same. This is kind of like how Xcode tab renaming works.
Additionally I have sub-classed NSWindow and added a restorableStateKeyPaths to ensure tab and window titles are automatically restored on app restart.
This all works great.
But only for the first tab. The main window is loaded and it has the tab and window titles set automatically.
The other tabs (windows) are not restored.
Any hints on what I miss to make all tabs restored?
My NSWindowController:
class MyWindowController: NSWindowController {
var subview: MyWindowController?
#IBAction override func newWindowForTab(_ sender: Any?) {
let story = self.storyboard
let windowVC = story?.instantiateInitialController() as! Self
window?.addTabbedWindow(windowVC.window!, ordered: .above)
subview = windowVC
windowVC.window?.orderFront(self.window)
windowVC.window?.makeKey()
}
}
My NSWindow:
class MyWindow: NSWindow {
override class var restorableStateKeyPaths: [String] {
return [ "self.tab.title", "self.title" ]
}
}
First you need to make sure that state restoration is enabled for your user, you can do this by going to Preferences->General and unchecking "Close windows when quitting an app".
Then you should use a restoration class in order to restore all open windows.
Basically if an NSWindow doesn't have a restoration class it won't be preserved across launches, that includes your storyboard loaded window. In this case what is happening is Cocoa is ignoring all window preservation because you haven't defined a restoration class for any of your windows so it resorts to its default behavior which is loading the initial storyboard controller.
Implementing restoration class is easy, just create a restoration class that inherits from NSObject and conforms to NSWindowRestoration, then implement its only required type method restoreWindow(identifier:state:completionHandler) like so:
class MyAppWindowRestoration: NSObject, NSWindowRestoration {
static func restoreWindow(withIdentifier identifier: NSUserInterfaceItemIdentifier,
state: NSCoder,
completionHandler: #escaping (NSWindow?, Error?) -> Void) {
// 1.- Retrieve and show the window
// Retrieve a new instance of the only window
let window = (NSStoryboard.main?.instantiateInitialController() as? NSWindowController)?.window
// Call the completion handler with the window and no errors
completionHandler(window, nil)
}
}
Then just assign this class as the window restoration class on every window you want restored, you can do this everywhere after the window has loaded:
window.restorationClass = MyAppWindowRestoration.self
Unfortunately Apple's documentation on state restoration completely sucks so if you have any more questions let me know ;)

Why in ElCapitan GM and Xcode 7 GM the popover appears outside of the view?

Does someone knows why in ElCapitan GM and Xcode 7 GM the popover appears outside of the view?
The popover is triggered by the "Button".
The picture below is a new project with no code written by me, jut a button.
Is it a bug or a new "feature"?
I just tried it, and it seems that you can't set the popover anchor in the Storyboard. Perhaps this is indeed a bug in the new release.
To display the popover programmatically, set the StoryboardID of your popover view controller, for example: "PopoverViewController". Below, it's implemented in the main view controller as a lazy var, so it's instantiated just once, the first time it's referenced.
Connect an IBAction from your button to the main view controller — here, a function called "displayPopover". The "guard let" statement makes sure the sender can be cast as an NSButton.
Then, just call:
presentViewController:asPopover...
lazy var popoverViewController: NSViewController = {
return self.storyboard!.instantiateControllerWithIdentifier("PopoverViewController")
as! NSViewController
}()
#IBAction func displayPopover(sender: AnyObject) {
guard let button = sender as? NSButton else {return}
self.presentViewController(popoverViewController, asPopoverRelativeToRect: button.frame, ofView: button, preferredEdge: NSRectEdge.MaxY, behavior: NSPopoverBehavior.Transient)
}

How to create modal slide-out window in Mac OS?

How can I create modal slide-out window/view "in-window" in Xcode like in these screenshot?
I've tried create new Window controller with "Authentication panel style" animation but then I'm receiving only Xcode crashes.
That kind of modal window is called a Sheet. It's very easy to get this behavior with a Storyboard segue, or programmatically with an NSViewController subclass. The example below is just a blank OS X Cocoa application as created by Xcode. (I chose Swift as the language, but it will work the same way with Objective-C.)
The only things I added to the storyboard was a second View Controller for the sheet view, and a label and pushbutton on each view.
Displaying The Sheet View With A Storyboard Segue
With the Sheet View controller selected and the Connections Inspector tab displayed, connect "Presenting Segues - sheet" to the "Display Sheet" button.
Connect "Received Actions - dismissController:" to the "Close Sheet" button.
That's it! There's no code needed to make this example work; just build and run.
Displaying The Sheet View Programmatically
Note that Xcode creates the default project with two custom class files. In the Storyboard, AppDelegate.swift is represented in the Application scene:
We don't need to use the AppDelegate for this example, but you could use it for interaction with the Main Menu, or other things.
The custom ViewController.swift custom class will be used to present the sheet. It is represented in the View Controller scene:
To instantiate the Sheet View Controller programmatically, it needs a Storyboard ID. Here, we'll give it the ID "SheetViewController". Note that it's still a plain NSViewController; we don't need to make it a custom class for this example, but your application might want to:
Displaying the ViewController.swift file in the assistant editor, Ctrl-drag a connection from the "Display Sheet" button into the custom class. This will create stub code for an #IBAction function we'll name "displaySheet":
In the ViewController.swift file, we'll implement the Sheet View Controller as a lazy var. It will get instantiated only once, the first time it's accessed. That will happen the first time the displaySheet function is called.
// ViewController.swift
import Cocoa
class ViewController: NSViewController {
lazy var sheetViewController: NSViewController = {
return self.storyboard!.instantiateControllerWithIdentifier("SheetViewController")
as! NSViewController
}()
#IBAction func displaySheet(sender: AnyObject) {
self.presentViewControllerAsSheet(sheetViewController)
}
}
Swift 4 version:
// ViewController.swift
import Cocoa
class ViewController: NSViewController {
lazy var sheetViewController: NSViewController = {
return self.storyboard!.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "SheetViewController"))
as! NSViewController
}()
#IBAction func displaySheet(sender: AnyObject) {
self.presentViewControllerAsSheet(sheetViewController)
}
}
As in the first example, the "Close Sheet" button is connected to the "dismissController:" action on the Sheet View Controller. Alternatively, you could call that function programmatically from your ViewController class:
self.dismissController(sheetViewController)
For more information, refer to the Apple "Sheets Programming Topics" document:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Sheets/Sheets.html
Objective-C version:
- (IBAction)displaySheet:(id)sender {
NSStoryboard *storyboard = [NSStoryboard storyboardWithName:#"Main" bundle: nil];
NSViewController * vc = [storyboard instantiateControllerWithIdentifier:#"SheetViewController"];
[self presentViewControllerAsSheet:vc];
}

How do you re-open a closed window created in the storyboard in OS X

My question is essential this question, but the answer doesn't seem to work with Swift/Storyboards.
Cocoa: programmatically show the main window after closing it with X
Basically, I have a more or less default application with a menu, a window, and a ViewController. If the user closes the window while the application is running, how do I reopen it?
I have created an action in the app delegate the connects to the "Open" Menu Item. Within this function, I would like to ensure that the window is visible. So if the user has closed it, it should reappear. But I cannot figure out how to access the closed window. Storyboard does not seem to allow me to create an outlet for my Window in my app delegate.
This is quite simple to archive, even it is not an elegant solution. Add a new property to your app delegate for your main window controller. In the following example, I call the controller MainWindowController.
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var mainWindowController: MainWindowController? = nil
func applicationShouldHandleReopen(sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
mainWindowController?.window?.makeKeyAndOrderFront(self)
return false
}
}
In the initialisation of the main window controller I register the controller in the app delegate:
class MainWindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
// ...initialisation...
// Register the controller in the app delegate
let appDelegate = NSApp.delegate as! AppDelegate
appDelegate.mainWindowController = self
}
}
That is all, works perfectly for me.

addSubview(...) causes additional window

I am facing a strange behavior when loading a view controllers view with addSubview():
Hard- and Software:
Cocoa Swift App on OSX 10.9.5 Mavericks, XCode 6 Beta 6
Goal:
App, programmatically loading a ViewController from its own xib and map the VCs view on main window
MainMenu.xib:
Window with just one view (NSView) as anchor for the controllers view
SpecialsVC.xib
View ( NSView) with just one label „Specials View“, view linked to Files owner
Swift Code of AppDelegate and view controller SpecialsVC:
class SpecialsVC : NSViewController {
required init(coder aDecoder: NSCoder!) { super.init(coder: aDecoder); }
override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
#IBOutlet weak var anchorView: NSView!
var specialsVC = SpecialsVC(nibName: "SpecialsVC", bundle: nil );
func applicationDidFinishLaunching(aNotification: NSNotification?) {
anchorView.addSubview( specialsVC.view ); // <— 2nd window opens here
specialsVC.view.frame = anchorView.bounds;
}
func applicationWillTerminate(aNotification: NSNotification?) { }
}
Running the app:
1 ) Main window is opening as expected, view controller is loaded from nib, its view is placed on the anchor view of main window. —> OK
2 ) 2nd and empty window with title „Window“ opens. Can be closed, no effect to main window. —> ?????????
Debugging:
The unexpected window comes up when stepping over anchorView.addSubview( specialsVC.view )
Also tried 1:
Load view controller not programmatically, but via outlet from View-Controller-Object in MainMenu.xib —> same effect
Also tried 2:
Put a button on the main window and loaded the view controller into a local variable in the buttons action. --> no 2nd window comes up, but the controller is lost when leaving the action. Copying the local var to an instance var —> 2nd window comes up
Hint:
Size of 2nd window does not change when size of view controllers view is changed.
What am I doing wrong? Where comes the 2nd window from?
Kind regards Ulrich
I think that you should be putting be specialsVC.view.frame = anchorView.bounds before your addSubview() method. I think that they will be ignored otherwise. It's like adding a declaration in a function after the return statement.

Resources