Programmatically create an NSViewController without an XIB in Swift 3
A careful reading of the documentation is enlightening. Specifically:
If this property’s value is not already set when you access it, the view controller invokes the
loadView()
method.
Meaning, loadView()
is only invoked when an attempt is made to read the view
property before the property has a value. Assigning a value to the property directly will bypass the method call.
So if AppDelegate.applicationDidFinishLaunching(_:)
is written like this:
let pc = PrimaryController(nibName: nil, bundle: nil)!
print(pc.isViewLoaded)
let x = pc.view // accessing the view property before it has a value
print(pc.isViewLoaded)
...then loadView
will be invoked by the system to attempt to load the view:
false
PrimaryController.loadView
false
Note that viewDidLoad()
is still not invoked, since no view can be automatically associated with the controller (ie, the nib is nil
and PrimaryController.xib
doesn't exist). The correct way to complete the process is to manually load the view in PrimaryController.loadView()
:
print("PrimaryController.loadView")
view = NSView() // instantiate and bind a view to the controller
Now the program gives the desired result:
false
PrimaryController.loadView
PrimaryController.viewDidLoad
true
Initialize a subclass of NSViewController without a xib file
From the NSViewController documentation:
If you pass in a nil for nibNameOrNil then nibName will return nil and
loadView will throw an exception; in this case you must invoke
setView: before view is invoked, or override loadView.
The initializer for MyViewController()
uses nil
for the nibName.
Two potential fixes:
1. Set the view in your AppDelegate
func applicationDidFinishLaunching(aNotification: NSNotification) {
viewController = MyViewController()
viewController!.view = NSView() // added this line; edit to set any view of your choice
self.window.contentView!.addSubview(viewController!.view)
}
Alternately,
2. Override loadView in your subclassed ViewController
import Cocoa
class MyViewController: NSViewController {
var textField: NSTextField?
override func loadView() {
self.view = NSView() // any view of your choice
}
override func viewDidLoad() {
super.viewDidLoad()
textField = NSTextField(frame: NSRect(x: 10, y: 10, width: 100, height: 100))
textField!.bezeled = false
textField!.drawsBackground = false
textField!.editable = false
textField!.selectable = false
textField!.stringValue = "TEST"
self.view.addSubview(textField!)
}
}
macOS: How to create and launch a NSViewController without using Xib/Storyboard
The size of the view is (0, 0) and the window is resized to the fit the view. Use a bigger view:
override func loadView() {
view = NSView(frame: NSMakeRect(0, 0, 500, 500))
}
Instantiate UIViewController programmatically without nib
According to the docs:
If the view controller does not have an associated nib file, this method creates a plain UIView object instead.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instp/UIViewController/view
If you do not provide a nib file when instantiating the view controller, UIViewController calls it's loadView()
method which creates a plain UIView object and assigns it to its view property.
The reason why you are seeing a transparent view is because the UIView object has no background color. To verify this, you can set the background color of the view controller's view property right before you push it on your navigation stack.
let viewController = TestViewController()
viewController.view.backgroundColor = .blueColor()
navigationController?.pushViewController(viewController, animated: true)
Instantiating UIViewController Without A Storyboard Or xib File
init(coder aDecoder: NSCoder)
is only ever called when a view is instantiated from a storyboard. To create a custom UIViewController and instantiate in manually, all you need to do is add:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
to your custom view controller. Now you're free to create your own initialization method and pass it whatever variables you like.
OSX application without storyboard or xib files using Swift
if you don't want to have the @NSApplicationMain attribute, do:
have a file main.swift
add following top-level code:
import Cocoa
let delegate = AppDelegate() //alloc main app's delegate class
NSApplication.shared.delegate = delegate //set as app's delegate
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv) //start of run loop
// Old versions:
// NSApplicationMain(C_ARGC, C_ARGV)
// NSApplicationMain(Process.argc, Process.unsafeArgv);
the rest should be inside your app delegate. e.g.:
import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate {
var newWindow: NSWindow?
var controller: ViewController?
func applicationDidFinishLaunching(aNotification: NSNotification) {
newWindow = NSWindow(contentRect: NSMakeRect(10, 10, 300, 300), styleMask: .resizable, backing: .buffered, defer: false)
controller = ViewController()
let content = newWindow!.contentView! as NSView
let view = controller!.view
content.addSubview(view)
newWindow!.makeKeyAndOrderFront(nil)
}
}
then you have a viewController
import Cocoa
class ViewController : NSViewController {
override func loadView() {
let view = NSView(frame: NSMakeRect(0,0,100,100))
view.wantsLayer = true
view.layer?.borderWidth = 2
view.layer?.borderColor = NSColor.red.cgColor
self.view = view
}
}
Programmatically navigate to another view controller/scene
I already found the answer
Swift 4
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "nextView") as! NextViewController
self.present(nextViewController, animated:true, completion:nil)
Swift 3
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewControllerWithIdentifier("nextView") as NextViewController
self.presentViewController(nextViewController, animated:true, completion:nil)
Related Topics
Does Untimeintervalnotificationtrigger Nexttriggerdate() Give the Wrong Date
Swift Combine: Using Timer Publisher in an Observable Object
Different Portrait/Landscape Views in Storyboard and Swift
Gcd Pattern for Chaining Async Operations While Piping the Results
Select All Text in a Nstextfield Using Swift
Could Not Find an Overload for '+' That Accepts the Supplied Arguments
How to Call Presentviewcontroller from Inside Class
How to Get Distinct Results from a Single Field in Core Data (Swift 4)
Conform to Protocol and Keep Property Private
Need Help Converting (Cfpropertylistref *)Nsdictionary to Swift
Getting a Segmentation Fault: 11 with Swift 5.2 When Using Filemanager.Default.Currentdirectorypath
iOS 12 Errors: Appears to Be from a Different Nsmanagedobjectmodel Than This Context'S