Is there a way to use SceneKit with ScreenSaverView class? - swift

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)
}
}

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.

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

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())
}
}

Live Interface Builder update with #IBDesignable for custom NSSlider control

I have problems in getting my custom NSSlider control updated live within Xcode's Interface Builder.
I have implemented #IBDesignable and prepareForInterfaceBuilder as shown in many other posts and tutorials. My little test just removes the knob from the slider control.
Here is the code I am using at the moment:
import Cocoa
#IBDesignable
class ColorSlider2: NSSlider {
override func setNeedsDisplay(_ invalidRect: NSRect) {
super.setNeedsDisplay(invalidRect)
}
override func awakeFromNib() {
super.awakeFromNib()
setupView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
private func setupView() {
if ((self.cell?.isKind(of: ColorSlider2Cell.self)) == false) {
let cell = ColorSlider2Cell()
self.cell = cell
}
self.alphaValue = 0.5
self.floatValue = 0.4
}
}
class ColorSlider2Cell: NSSliderCell {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init() {
super.init()
}
override func drawKnob(_ knobRect: NSRect) {
return
}
}
The preview in Interface Builder is neither removing the knob nor updating the floatValue:
Do you have any idea why this is the case?

What argument do I use when calling draw() function?

I want to draw a line programmatically in swift. I have working code for it (I think), but calling the function requires a CGRect argument. And I'm unsure what code to write there.
The draw() class and function looks like this:
class LineView : UIView {
override func draw(_ rect: CGRect) {
var aPath = UIBezierPath()
aPath.move(to: CGPoint(x:2, y:2))
aPath.addLine(to: CGPoint(x:6, y:6))
aPath.close()
}
}
Now calling it from the main ViewDidLoad it would look like this:
var line = LineView()
line.draw(MISSING ARGUMENT)
But I have no idea what argument I'm supposed to pass. Nothing of the CGRect is used in the function, so I'm not even sure of its purpose.
UPDATE
In main I create the object like this:
class ViewController: UIViewController {
#IBOutlet weak var gameBoard: UIView!
override func viewDidLoad() {
super.viewDidLoad()
var line = LineView()
gameBoard.addSubview(line)
}
}
And my draw class looks like this:
import UIKit
class LineView : UIView {
override init(frame: CGRect) {
super.init(frame: frame) //super = DO REGULAR INIT STUFF FOR UIView
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
var aPath = UIBezierPath()
aPath.move(to: CGPoint(x:2, y:2))
aPath.addLine(to: CGPoint(x:6, y:6))
aPath.close()
UIColor.red.set()
aPath.stroke()
aPath.fill()
setNeedsDisplay()
}
}
Got it working with:
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Don't call draw(rect: yourself. Never do.
It's implicitly called once by the framework after the view was initialized.
If you want to redraw the view call setNeedsDisplay() on the view.