How do I link storyboard and swift in a Mac application? - swift

I am trying to IBsegue from the story board to my subclass of NSHostingView. Is this the proper way to link storyboard and SwiftUI for a Mac application?(If not, what is the proper way?) If so, I get four errors in the subclass file:
import Cocoa
class hosty: NSHostingView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
#IBSegueAction func termo(_ coder: NSCoder) -> NSViewController? {
return NSHostingController(coder: coder, rootView: ContentView())
}
}
Use of undeclared type 'NSHostingView' class hosty: NSHostingView {
Method does not override any method from its superclass override func draw(_ dirtyRect: NSRect) {
'super' members cannot be referenced in a root class super.draw(dirtyRect)
Use of unresolved identifier 'NSHostingController' return NSHostingController(coder: coder, rootView: ContentView())

You need import SwiftUI and specify generics type for NSHostingView like below
import Cocoa
import SwiftUI
class hosty: NSHostingView<ContentView> {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
#IBSegueAction func termo(_ coder: NSCoder) -> NSViewController? {
return NSHostingController(coder: coder, rootView: ContentView())
}
}

Related

viewDidLoad() not called

Either there is something weird in my project (Mojave, XCode10) or I am missing something very basic.
This is my whole code:
import Foundation
import Cocoa
class ViewController: NSViewController {
public init () {
super.init(nibName: nil, bundle: nil)
self.view = ConfigView(rect: NSRect(x: 0, y: 0, width: 400, height: 300))
print("2")
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
super.loadView()
print("3")
}
override func viewDidLoad() {
super.viewDidLoad()
print("4")
}
override func viewWillAppear() {
super.viewWillAppear()
print("5")
}
override func viewDidAppear() {
super.viewDidAppear()
print("6")
print("Is loaded: \(isViewLoaded)")
}
}
class ConfigView: NSView {
public init(rect: NSRect) {
super.init(frame: rect)
print("1")
}
required public init?(coder decoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I want to setup stuff in the viewDidLoad() method, but this method is never called. The console shows:
1
2
5
6
Is loaded: true
So, obviously loadView() and viewDidLoad() are not called, but afterwards when the view appeared isViewLoaded() shows true.
Why is that lifecycle method being ignored?
I suspect what is happening here is that since you are initializing the view property in the init function, when loadView() is called, it detects that the view has already been loaded and therefore does not call the viewDidLoad().
Have you tried putting the view instantiation into the loadView() function instead of the init? In any case, I would not recommend creating the view in the init function.
If you implement loadView, you must not call super. And you must make a view and assign it as self.view. Fix that and all will be well. Of course, in real life, you would never implement loadView, so all this is academic.

Add Custom NSView into NSStackView

I have a Custom NSView. This view had a xib with all desing
import Cocoa
class CustomView: NSView {
#IBOutlet var contentView: NSView!
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
Bundle.main.loadNibNamed("CustomView", owner: self, topLevelObjects: nil)
addSubview(contentView)
contentView.frame = contentView.bounds
contentView.autoresizingMask = [.height, .width]
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
}
When I try to add this view into NSStackView doesn't work.
This is the code in my ViewController
override func viewDidLoad() {
super.viewDidLoad()
let view = CustomView()
self.stackView.addArrangedSubview(view)
}
Any idea.

Is there a way to use SceneKit with ScreenSaverView class?

Is there a way to use SceneKit's SceneView to render it into the ScreenSaverView. I tried couple of things, but can't seem to figure this out.
I know putting 3D graphics into screensaver is not a good idea, just making this for fun.
Here's the code I have right now:
import ScreenSaver
import Foundation
import SceneKit
class MainView: ScreenSaverView {
var sceneView: SCNView
// MARK: - Initialization
override init?(frame: NSRect, isPreview: Bool) {
sceneView = SCNView(frame: frame)
super.init(frame: frame, isPreview: isPreview)
sceneView.scene = SCNScene(named: "monkey")!
self.addSubview(sceneView)
}
#available(*, unavailable)
required init?(coder decoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Lifecycle
override func draw(_ rect: NSRect) {
// Draw a single frame in this function
self.sceneView.draw(bounds)
}
override func animateOneFrame() {
super.animateOneFrame()
// Update the "state" of the screensaver in this function
setNeedsDisplay(bounds)
}
}

Subclass of NSDatePicker mouseDown event changes datePicker instance

I have instantiated and initialized a subclass of NSDatePicker in AppDelegate -> applicationDidFinishLaunching and overrode mouseDown(with event: NSEvent)in the datePicker subclass. When I press the mouse button in the datePicker and break in its overridden mouseDown func the instance is not the one I instantiated in applicationDidFinishLaunching and so is not initialized.
I've tried creating the instance at different entry points thinking it might be a timing thing but I've gotten nowhere. I'm out of ideas and feeling a little feeble. Any Help?
The datePicker:
import Cocoa
class AlarmIVDatePicker: NSDatePicker {
var viewController: ViewController!
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
}
override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
return true
}
override func mouseDown(with event: NSEvent) {
let stop = 0
}
}
The ViewController:
class ViewController: NSViewController, NSWindowDelegate{
var alarmIVDatePicker: AlarmIVDatePicker!
override func viewDidLoad() {
super.viewDidLoad()
alarmIVDatePicker = AlarmIVDatePicker()
alarmIVDatePicker.viewController = self
}
I expected I could access the values I had set but the instance is not the one I created and all the values are nil
Okay. Here's what you can do to track this down. Add this code to your AlarmIVDatePicker class:
override init(frame frameRect: NSRect) {
Swift.print("AlarmIVDatePicker being called here")
super.init(frame: frameRect)
}
convenience init() {
self.init(frame: .zero)
}
required init?(coder: NSCoder) {
Swift.print("hey, you DID drop this into a storyboard or XIB file!")
super.init(coder: coder)
}
If you set breakpoints inside these init methods, you'll catch when and where they are being called where you did not expect.

Overriding convenience init

Trying to subclass an NSTextView:
class MYTextView : NSTextView {
init(frame frameRect: NSRect) {
super.init(frame: frameRect)
setup()
}
}
I get this error: Must call a designated initializer of the superclass 'NSTextView' on this line: super.init(frame: frameRect).
According to the docs Convenience initializers must call another initializer available in the same class.. See 'Initializer Chaining' below:
https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_286
But for NSTextViews the only designated inits i get are super.init(frame:, textContainer:) & super.init(coder: coder) & super.inti(). init(frame:) does some setup which I'd rather not implement myself.
Is there some way to use a super class's convenience initializer?
Override the designated initialisers:
class MyTextView : NSTextView {
init(frame frameRect: NSRect, textContainer aTextContainer: NSTextContainer!) {
super.init(frame: frameRect, textContainer: aTextContainer)
setup();
}
func setup() {
...
}
}
var textView = MyTextView(frame: NSRect())
Since all the designated initialisers are overridden, all convenience will be automatically inherited.
There are two other designated initialzers to override:
init() {
}
and
init(coder:) {
}
I was just tripped up by this exact problem as well. I still Swift initialization with inheritance tricky, so definitely possible I'm not understanding something else here.
The accepted answer seems to suggest overriding init(frame: textContainer:), init, and init(coder:) will make init(frame:) accessible, but is does not for me.
The only way I was able to get things working as I wanted was with this:
override init(frame frameRect: NSRect, textContainer container: NSTextContainer?) {
super.init(frame: frameRect, textContainer: container)
setup()
}
override init(frame frameRect: NSRect) {
// this will end up calling init(frame:textContainer:)
super.init(frame: frameRect)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}