Search code examples
swiftcocoansviewcontroller

Why this swift code does not show up line and the animation?


Why is the MyViewController does not get's shown in the window although the it's stated in the contentViewController: MyViewController() ?

I'm not on storyboard or XIB and I don't wanna use a nib file.

import Cocoa

@main
class AppDelegate: NSObject, NSApplicationDelegate {

    @IBOutlet var window: NSWindow!

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Create a new window
        window = NSWindow(contentViewController: MyViewController())
        // Make the window visible
        window.makeKeyAndOrderFront(nil)
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }

    func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
        return true
    }
}

    
class MyViewController: NSViewController {
    
    override func loadView() {
        let view = NSView(frame: NSMakeRect(0, 0, 400, 400))
        self.view = view
        
        // Create a shape layer for the rectangle
        let shapeLayer = CAShapeLayer()
        shapeLayer.strokeColor = NSColor.black.cgColor
        shapeLayer.lineWidth = 2
        shapeLayer.lineJoin = .round
        shapeLayer.lineDashPattern = [NSNumber(value: 6), NSNumber(value: 4)]
        
        // Create a path for the rectangle
        let path = CGMutablePath()
        path.addRect(NSRect(x: 50, y: 50, width: 100, height: 100))
        shapeLayer.path = path
        
        // Create an animation for the dashed line
        let animation = CABasicAnimation(keyPath: "lineDashPhase")
        animation.fromValue = 0
        animation.toValue = shapeLayer.lineDashPattern?.reduce(0) { $0 + $1.intValue }
        animation.duration = 1
        animation.repeatCount = .infinity
        shapeLayer.add(animation, forKey: "lineDashPhase")
        
        // Add the shape layer to the view hierarchy
        view.layer?.addSublayer(shapeLayer)
    }
    
}

Solution

  • I'm not on storyboard or XIB and I don't wanna use a nib file.

    If you want to use the programmatic approach then you don't need the controllers for a basic window as shown in the demo below. To run the following in Xcode first create a Swift project, add a 'main.swift' file, and then delete the pre-supplied AppDelegate file. Copy/paste this code into the 'main' file and run. I added a green background to your view so that you could see its bounds. You need to set .wantsLayer to true and then add the view as a subview to the window.contentView.

    import Cocoa
    
    class AppDelegate: NSObject, NSApplicationDelegate {
        var window:NSWindow!
        var view: NSView!
        
        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
            
            window = NSWindow(contentRect:NSMakeRect(0,0,_wndW,_wndH),styleMask:[.titled, .closable, .miniaturizable, .resizable], backing:.buffered, defer:false)
            window.center()
            window.title = "Swift Test Window"
            window.makeKeyAndOrderFront(window)
            
            // **** CAShapeLayer with Animation **** //
            view = NSView(frame: NSMakeRect(0, 0, 400, 400))
            window.contentView!.addSubview (view)
            view.wantsLayer = true
            view.layer?.backgroundColor = NSColor.green.cgColor
            
            // Create a shape layer for the rectangle
            let shapeLayer = CAShapeLayer()
            shapeLayer.strokeColor = NSColor.black.cgColor
            shapeLayer.lineWidth = 2
            shapeLayer.lineJoin = .round
            shapeLayer.lineDashPattern = [NSNumber(value: 6), NSNumber(value: 4)]
            
            // Create a path for the rectangle
            let path = CGMutablePath()
            path.addRect(NSRect(x: 50, y: 50, width: 100, height: 100))
            shapeLayer.path = path
            
            // Create an animation for the dashed line
            let animation = CABasicAnimation(keyPath: "lineDashPhase")
            animation.fromValue = 0
            animation.toValue = shapeLayer.lineDashPattern?.reduce(0) { $0 + $1.intValue }
            animation.duration = 1
            animation.repeatCount = .infinity
            shapeLayer.add(animation, forKey: "lineDashPhase")
            
            // Add the shape layer to the view hierarchy
            view.layer?.addSublayer(shapeLayer)
            
            // **** Quit btn **** //
            let quitBtn = NSButton (frame:NSMakeRect( _wndW - 50, 10, 40, 40 ))
            quitBtn.bezelStyle = .circular
            quitBtn.autoresizingMask = [.minXMargin,.maxYMargin]
            quitBtn.title = "Q"
            quitBtn.action = #selector(NSApplication.terminate)
            window.contentView!.addSubview(quitBtn)
        }
        
        func applicationDidFinishLaunching(_ notification: Notification) {
            buildMenu()
            buildWnd()
        }
        
        func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
            return true
        }
        
    }
    let appDelegate = AppDelegate()
    
    // **** main.swift **** //
    let app = NSApplication.shared
    app.delegate = appDelegate
    app.setActivationPolicy(.regular)
    app.activate(ignoringOtherApps:true)
    app.run()
    
    

    enter image description here