Skip to content

Instantly share code, notes, and snippets.

@stephancasas
Last active April 28, 2025 12:59
Show Gist options
  • Save stephancasas/828f68b34d8f57a560c856fc0d12e55d to your computer and use it in GitHub Desktop.
Save stephancasas/828f68b34d8f57a560c856fc0d12e55d to your computer and use it in GitHub Desktop.
A SwiftUI MenuBarExtra window with custom corners
//
// MenuBarTestApp.swift
// MenuBarTest
//
// Created by Stephan Casas on 7/7/23.
//
import SwiftUI
@main
struct MenuBarTestApp: App {
var body: some Scene {
MenuBarExtra(content: {
ContentView()
.background(MenuBarExtraWindowHelperView())
}, label: { Image(systemName: "camera.viewfinder") })
.menuBarExtraStyle(.window)
}
}
// MARK: - Content
struct ContentView: View {
var body: some View {
VStack(content: {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
})
.frame(width: 500, height: 500)
}
}
// MARK: - Lossless Method Exchanger
extension NSObject {
/// Swap the given named instance method of the given named class with the given
/// named instance method of this class.
/// - Parameters:
/// - method: The name of the instance method whose implementation will be exchanged.
/// - className: The name of the class whose instance method implementation will be exchanged.
/// - newMethod: The name of the instance method on this class which will replace the first given method.
static func exchange(method: String, in className: String, for newMethod: String) {
guard let classRef = objc_getClass(className) as? AnyClass,
let original = class_getInstanceMethod(classRef, Selector((method))),
let replacement = class_getInstanceMethod(self, Selector((newMethod)))
else {
fatalError("Could not exchange method \(method) on class \(className).");
}
method_exchangeImplementations(original, replacement);
}
}
// MARK: - Custom Window Corner Mask Implementation
/// Exchange Flag
///
var __SwiftUIMenuBarExtraPanel___cornerMask__didExchange = false;
/// Custom Corner Radius
///
fileprivate let kWindowCornerRadius: CGFloat = 40;
extension NSObject {
@objc func __SwiftUIMenuBarExtraPanel___cornerMask() -> NSImage? {
let width = kWindowCornerRadius * 2;
let height = kWindowCornerRadius * 2;
let image = NSImage(size: CGSizeMake(width, height));
image.lockFocus();
/// Draw a rounded-rectangle corner mask.
///
NSColor.black.setFill();
NSBezierPath(
roundedRect: CGRectMake(0, 0, width, height),
xRadius: kWindowCornerRadius,
yRadius: kWindowCornerRadius).fill();
image.unlockFocus();
image.capInsets = .init(
top: kWindowCornerRadius,
left: kWindowCornerRadius,
bottom: kWindowCornerRadius,
right: kWindowCornerRadius);
return image;
}
}
// MARK: - Context Window Accessor
struct MenuBarExtraWindowHelperView: NSViewRepresentable {
class WindowHelper: NSView {
override func viewWillDraw() {
if __SwiftUIMenuBarExtraPanel___cornerMask__didExchange { return }
guard
let window: AnyObject = self.window,
let windowClass = window.className
else { return }
NSObject.exchange(
method: "_cornerMask",
in: windowClass,
for: "__SwiftUIMenuBarExtraPanel___cornerMask");
let _ = window.perform(Selector(("_cornerMaskChanged")));
__SwiftUIMenuBarExtraPanel___cornerMask__didExchange = true;
}
}
func updateNSView(_ nsView: WindowHelper, context: Context) { }
func makeNSView(context: Context) -> WindowHelper { WindowHelper() }
}
@mike-n-1974
Copy link

A curious question, what license would this code be under?

–Mike 😃

@stephancasas
Copy link
Author

@mike-n-1974 MIT. Have fun!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment