Álvaro Ramírez
SwiftUI macOS desk clock
For time display, I've gone back and forth between an always-displayed macOS's menu bar to an auto-hide menu bar, and letting Emacs display the time. Neither felt great nor settled.
With some tweaks, Paul Hudson's How to use a timer with SwiftUI, led me to build a simple desk clock. Ok, let's not get fancy. It's really just an always-on-top floating window, showing a swiftUI label, but hey I like the minimalist feel ;)
Let's see if it sticks around or it gets in the way… Either way, here's standalone snippet. Run with swift deskclock.swift.
import Cocoa import SwiftUI let application = NSApplication.shared let appDelegate = AppDelegate() NSApp.setActivationPolicy(.regular) application.delegate = appDelegate application.mainMenu = NSMenu.makeMenu() application.run() struct ClockView: View { @State var time = "--:--" let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() var body: some View { GeometryReader { geometry in VStack { Text(time) .onReceive(timer) { input in let formatter = DateFormatter() formatter.dateFormat = "HH:mm" time = formatter.string(from: input) } .font(.system(size: 40)) .padding() }.frame(width: geometry.size.width, height: geometry.size.height) .background(Color.black) .cornerRadius(10) .frame(maxWidth: .infinity, maxHeight: .infinity) } } } extension NSWindow { static func makeWindow() -> NSWindow { let window = NSWindow( contentRect: NSRect.makeDefault(), styleMask: [.closable, .miniaturizable, .resizable, .fullSizeContentView], backing: .buffered, defer: false) window.level = .floating window.setFrameAutosaveName("everclock") window.collectionBehavior = [.canJoinAllSpaces, .stationary, .ignoresCycle, .fullScreenPrimary] window.makeKeyAndOrderFront(nil) window.isMovableByWindowBackground = true window.titleVisibility = .hidden window.backgroundColor = .clear return window } } class AppDelegate: NSObject, NSApplicationDelegate { var window = NSWindow.makeWindow() var hostingView: NSView? func applicationDidFinishLaunching(_ notification: Notification) { hostingView = NSHostingView(rootView: ClockView()) window.contentView = hostingView NSApp.activate(ignoringOtherApps: true) } } extension NSRect { static func makeDefault() -> NSRect { let initialMargin = CGFloat(60) let fallback = NSRect(x: 0, y: 0, width: 100, height: 150) guard let screenFrame = NSScreen.main?.frame else { return fallback } return NSRect( x: screenFrame.maxX - fallback.width - initialMargin, y: screenFrame.maxY - fallback.height - initialMargin, width: fallback.width, height: fallback.height) } } extension NSMenu { static func makeMenu() -> NSMenu { let appMenu = NSMenuItem() appMenu.submenu = NSMenu() appMenu.submenu?.addItem( NSMenuItem( title: "Quit \(ProcessInfo.processInfo.processName)", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q" )) let mainMenu = NSMenu(title: "Main Menu") mainMenu.addItem(appMenu) return mainMenu } }