Why this window does not show up at all?

84 Views Asked by At

I'm trying to understand how to change data in a variable (var or a label) in between two different views or functions.

I'm migrating from swiftUi, I was used to create stateobject and accessing the variable through environment variables throughout the app. But now the cocoa framework confuses me a bit.

So I created two views CustomView1 and CustomView2, one of them contains a button and other contains a label. So, when the button clicks, the label's string value should change.

But the issue is nothing shows up when run. Not even the window show up.

Error: No errors

main.swift


import Cocoa

let app = NSApplication.shared
 
let appDelegate = App2()
app.delegate = appDelegate
app.setActivationPolicy(.regular)
app.activate(ignoringOtherApps:true)
app.run()

App2.swift

import Cocoa
 
 
class App2: NSObject, NSApplicationDelegate {
    var window:NSWindow!
    var view: NSView!
    var customView1: CustomView1!
    var customView2: CustomView2!
    
    
    
    func buildMenu() {
        let mainMenu = NSMenu()
        NSApp.mainMenu = mainMenu
        // **** App menu **** //
        let appMenuItem = NSMenuItem()
        mainMenu.addItem(appMenuItem)
        let appMenu = NSMenu()
        appMenuItem.submenu = appMenu
        appMenu.addItem(withTitle: "Quit", action:#selector(NSApplication.terminate), keyEquivalent: "q")
    }
    
    func buildWnd() {
        let _wndW: CGFloat = 400
        let _wndH: CGFloat = 400
        //@State var title: String = "app title"
        let appTitle: String = "app title"
        
        
        window = NSWindow(contentRect: NSMakeRect(0, 0, _wndW, _wndH), styleMask: [.titled, .closable, .miniaturizable, .resizable], backing: .buffered, defer: false)
        window.center()
        window.title = appTitle
        window.makeKeyAndOrderFront(window)
        
        // Create a label
        let labelFrame = NSRect(x: 50, y: 50, width: 200, height: 30)
        let labelText = "Hello, Cocoa!"
        
        
        customView1 = CustomView1(frame: NSRect(x: 10, y: 50, width: 120, height: 50))
        customView2 = CustomView2(frame: NSRect(x: 10, y: 100, width: 120, height: 50))
        
        window.contentView?.addSubview(customView1)
        window.contentView?.addSubview(customView2)
        
        
    }
    
    
    class CustomView1: NSView {
        let customView2 = CustomView2(frame: NSRect(x: 10, y: 50, width: 100, height: 30))
        
        func createButton(frame: NSRect, title: String) -> NSButton {
            let button = NSButton(frame: frame)
            button.title = title
            button.bezelStyle = .rounded
            button.target = self
            button.action = #selector(buttonClicked(_:))
            return button
        }
        
        override func draw(_ dirtyRect: NSRect) {
            super.draw(dirtyRect)
            
            let button = createButton(frame: NSRect(x: 10, y: 10, width: 100, height: 30), title: "Click me!")
            self.addSubview(button)
            self.addSubview(customView2)
        }
        
        @objc func buttonClicked(_ sender: NSButton) {
            customView2.label.stringValue = "Button clicked!"
        }
    }
    
    class CustomView2: NSView {
        let label = NSTextField(frame: NSRect(x: 10, y: 10, width: 100, height: 20))
        
        override init(frame frameRect: NSRect) {
            super.init(frame: frameRect)
            label.stringValue = "Hello, world!"
            label.isEditable = false
            label.isSelectable = false
            label.isBezeled = false
            label.drawsBackground = false
            self.addSubview(label)
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
      
    
    func applicationDidFinishLaunching(_ notification: Notification) {
        buildMenu()
        buildWnd()
    }
    
    func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
        return true
    }
    
}
    
1

There are 1 best solutions below

2
apodidae On

The following source code is a re-arrangement of your demo. The two custom views need to be separate from the appDelegate. Custom view2 is more correct than Custom view1 as far as adding controls. You will need to create a new file called 'main.swift' by using Xcode's File/New/File. Copy/paste the following code into that file in its entirety, then delete the pre-supplied AppDelegate and it should run without error.

import Cocoa

var customView1: CustomView1!
var customView2: CustomView2!

class CustomView1: NSView {
    let button = NSButton(frame:NSMakeRect(10,12,100,24))
    
    @objc func buttonClicked(_ sender: NSButton) {
        customView2.label.stringValue = "Button clicked!"
    }
    
    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        button.title = "Click Me"
        button.bezelStyle = .rounded
        button.target = self
        button.action = #selector(buttonClicked(_:))
        self.addSubview(button)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        let bkgrnd = NSBezierPath(rect: dirtyRect)
        NSColor.lightGray.set()
        bkgrnd.fill()
    }
}

class CustomView2: NSView {
    let label = NSTextField(frame: NSMakeRect(10,14,100,20))
    
    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        label.stringValue = "Hello, world!"
        label.isBordered = false
        label.isSelectable = false
        label.backgroundColor = NSColor.clear
        self.addSubview(label)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        let bkgrnd = NSBezierPath(rect: dirtyRect)
        NSColor.orange.set()
        bkgrnd.fill()
    }
}

class ApplicationDelegate: NSObject, NSApplicationDelegate {
    var window:NSWindow!
    
    func buildMenu() {
        let mainMenu = NSMenu()
        NSApp.mainMenu = mainMenu
        // **** App menu **** //
        let appMenuItem = NSMenuItem()
        mainMenu.addItem(appMenuItem)
        let appMenu = NSMenu()
        appMenuItem.submenu = appMenu
        appMenu.addItem(withTitle: "Quit", action:#selector(NSApplication.terminate), keyEquivalent: "q")
    }
    
    func buildWnd() {
        let _wndW: CGFloat = 400
        let _wndH: CGFloat = 300
        
        let appTitle: String = "app title"
        
        window = NSWindow(contentRect: NSMakeRect(0,0,_wndW,_wndH), styleMask: [.titled, .closable, .miniaturizable, .resizable], backing: .buffered, defer: false)
        window.center()
        window.title = appTitle
        window.makeKeyAndOrderFront(window)
        
        // Create a label from NSTextField
        let txtFld = NSTextField (frame:NSMakeRect(30,_wndH - 50,300,30))
        txtFld.isSelectable = false
        txtFld.isBordered = false
        txtFld.backgroundColor = NSColor.clear
        txtFld.textColor = NSColor.red
        txtFld.font = NSFont(name: "Menlo", size: 28)
        txtFld.stringValue = "Hello, Cocoa!"
        window.contentView!.addSubview(txtFld)
        
        customView1 = CustomView1(frame: NSMakeRect(30,_wndH - 190,120,50))
        customView2 = CustomView2(frame: NSMakeRect(30,_wndH - 130,120,50))
        
        window.contentView!.addSubview(customView1)
        window.contentView!.addSubview(customView2)
    }
    
    func applicationDidFinishLaunching(_ notification: Notification) {
        buildMenu()
        buildWnd()
    }
    
    func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
        return true
    }
}

let applicationDelegate = ApplicationDelegate()

let application = NSApplication.shared
application.setActivationPolicy(NSApplication.ActivationPolicy.regular)
application.delegate = applicationDelegate
application.activate(ignoringOtherApps:true)
application.run()