Guides··Updated ·8 min read

NSWindow level, ignoresMouseEvents and collectionBehavior

A Dynamic Island on the Mac is one borderless NSPanel pinned under the notch, and three NSWindow properties carry it: level at .statusBar so it floats over app windows, ignoresMouseEvents at true so clicks pass through, and collectionBehavior at [.canJoinAllSpaces, .stationary] so it survives a desktop switch. That is the load-bearing third of how to build a Dynamic Island for Mac (developer guide 2026); the notch geometry, the shape and the springs are the rest.

By Deepak Yadav, building NotchBay

What you’re actually building

  • There's no Dynamic Island API on macOS; you fake it with a borderless, transparent NSPanel pinned under the notch.
  • level = .statusBar keeps it over app windows, ignoresMouseEvents = true lets clicks through, collectionBehavior = [.canJoinAllSpaces, .stationary] keeps it on every desktop.
  • NSScreen (safeAreaInsets plus the auxiliary top areas) tells you exactly where the cutout is; never hardcode it.
  • Match the notch's continuous, asymmetric corners and drive the stretch with SwiftUI springs, or it reads as a floating box.
  • The rest of the week goes on per-model geometry, idle CPU, and media plus Accessibility permissions.

#Start with the shape of the problem

The first thing to accept is that Apple gives you nothing to build on. There's no Dynamic Island framework, no notch API beyond a couple of geometry helpers, and no live-activity system on macOS at all. What you're really building is an illusion: a small, always-on-top window that sits under the physical camera housing and pretends to be part of it. Get the position, the shape, and the motion right and people read it as a system feature. Get any one of them wrong and it reads as a floating box.

What follows is the architecture NotchBay actually ships, not theory, and it is the same architecture behind every app in the Dynamic Island for Mac category. Four pieces carry it: the window and its three NSWindow properties, finding the notch, the animatable shape, and hit-testing. None is huge on its own; the difficulty is that all four have to be right at the same time.

#The panel: a borderless, transparent NSPanel

The container is a single NSPanel, not an NSWindow, because a panel can float above your other windows without stealing focus. Make it borderless and transparent, kill the shadow, and park it at a high window level so it stays above ordinary app windows:

// AppKit host for the island
let panel = NSPanel(
    contentRect: rect,
    styleMask: [.borderless, .nonactivatingPanel],
    backing: .buffered, defer: false)
panel.isOpaque = false
panel.backgroundColor = .clear
panel.hasShadow = false
panel.level = .statusBar              // above normal windows
panel.collectionBehavior = [.canJoinAllSpaces, .stationary]
panel.ignoresMouseEvents = true       // pass clicks through by default

The .nonactivatingPanel flag is the one that saves you: without it, tapping the island yanks keyboard focus out of whatever you were doing. Everything visible, the resting pill, the expanded card, a music waveform, is a SwiftUI view hosted inside this panel with an NSHostingView. Keep the panel itself dumb and do all layout and animation in SwiftUI.

#level, ignoresMouseEvents and collectionBehavior

NSPanel is a subclass of NSWindow, so the three lines that decide how the island behaves are plain NSWindow properties, and they are worth understanding before anything gets drawn. Each one fixes a specific way an always-on-top window otherwise ruins the machine it runs on.

level sets the stacking order against every other window on screen. Ordinary app windows sit at .normal, and the island has to stay visible over them, so it goes to .statusBar, the band the menu bar it is pretending to belong to already occupies. Too low and a maximised window buries the island. Too high, above the alert and modal levels, and it draws on top of system dialogs, which is the worse failure of the two because the user cannot see what they are dismissing.

ignoresMouseEvents is the click-through switch, and it is the property that decides whether the app is polite. Set to true, the window is transparent to the mouse: clicks land on the menu bar and on whatever window is underneath, exactly as though the panel were not there. That is the correct resting state, because the island covers a strip of screen it does not own. Flip it to false only while a real control is on screen, then flip it straight back. It is a whole-window switch, not a per-pixel one, which is why the hit-testing further down exists.

collectionBehavior decides how the window relates to Spaces and Mission Control. .canJoinAllSpaces keeps the island on every desktop, which is what people expect, since the hardware notch is on every desktop too. .stationary stops it sliding sideways with the Space-switch animation and stops Mission Control gathering it up with the real windows. Without both flags the island vanishes the first time someone swipes to another desktop.

Specs at a glance

PropertyValue on the islandWhat it buys
level.statusBarDraws above ordinary app windows
ignoresMouseEventstrue when idleClicks fall through to the menu bar and the window behind
collectionBehavior[.canJoinAllSpaces, .stationary]On every Space, and it does not slide with the switch
styleMask[.borderless, .nonactivatingPanel]No title bar, never takes key focus
isOpaque, backgroundColor, hasShadowfalse, .clear, falseNothing visible but the SwiftUI content
Panel frame220×38 pt (16-inch), 185×32 pt (14-inch)Matches the cutout, read from NSScreen at launch

Every value is the configuration in the code above. The two frame sizes are what NSScreen reports on those two models, not a spec-sheet number.

#Finding the notch with NSScreen

You can't hardcode the notch position; it moves with screen size and scaled resolution. macOS exposes it through NSScreen. safeAreaInsets.top is the notch height, and auxiliaryTopLeftArea and auxiliaryTopRightArea are the two menu-bar slivers on either side of the camera, so the cutout width is the screen width minus both:

let screen = NSScreen.main!
let notchHeight = screen.safeAreaInsets.top          // 38 pt on a 16-inch

if let l = screen.auxiliaryTopLeftArea,
   let r = screen.auxiliaryTopRightArea {
    let notchWidth = screen.frame.width - l.width - r.width
    // center a panel of this width at the top of the screen
}

If safeAreaInsets.top comes back 0, that Mac has no notch, so either bail or fall back to a simulated pill near the menu bar. On real hardware this returns 220×38 points on the 16-inch MacBook Pro and 185×32 on the 14-inch; the full set across models is in the notch size breakdown, and the geometry the island is drawn to is written up on the design spec page. Recompute all of it whenever the screen parameters change (listen for didChangeScreenParametersNotification), because plugging in a display or changing resolution shifts everything.

#The shape, and the spring that stretches it

A plain rounded rectangle looks close but reads as wrong, because the real cutout isn't one. Its top corners are tighter than its bottom corners (roughly 4 pt versus 8 pt), and Apple uses continuous, squircle-style curvature rather than a plain arc. In SwiftUI the cheapest honest approximation is a continuous corner style:

RoundedRectangle(cornerRadius: 12, style: .continuous)

for the expanded states, with hand-tuned asymmetric corners on the resting pill so its top hugs the hardware. The .continuous style is doing real work; the default .circular corners are the giveaway that separates a convincing island from a floating badge.

Motion is the other half. The island's whole appeal is that it stretches: a compact pill snaps open into a wide card when a call arrives, then settles back. Drive that with SwiftUI springs, not linear curves, because the overshoot-and-settle is what sells it as physical:

withAnimation(.spring(response: 0.35, dampingFraction: 0.72)) {
    state = .expanded
}

Animate width, height, and corner radius together and let the spring carry them. Expect to spend more time tuning response and dampingFraction than on any other pair of numbers: a hair too bouncy and it feels like a toy, a hair too stiff and it feels dead.

#Click-through, staying light, and permissions

Here's what the quick tutorials skip. Four things will eat your week:

  • Per-pixel hit-testing. ignoresMouseEvents is all-or-nothing, and the moment a button is on screen you need the window live and its empty regions still dead. Override hitTest on the content view so only actual controls return a view; every transparent pixel returns nil and the click falls through to whatever's behind. Miss this and a 220-point-wide invisible rectangle eats the menu bar.
  • Notch geometry per model. The 14-inch and 16-inch cutouts differ, the Air's differs again, and scaled resolutions move all of them. Anything you hardcode will be a point off on someone's machine, and at this size a point is visible. Derive it from NSScreen, every launch.
  • Staying light. This runs all day. It has to idle at effectively zero CPU when nothing's happening and wake only for real events. Poll in a tight loop and you'll show up in everyone's battery complaints; prefer notifications and observers over timers.
  • Permissions. The island itself needs none, but the features people want do. Now-playing info comes from MediaRemote-style plumbing Apple keeps semi-private; call controls that press another app's mute button need Accessibility; anything touching the mic triggers its own prompt. Each capability is a separate permission and a separate review headache.

#Or skip the build and use NotchBay

Building one is a genuinely fun AppKit-and-SwiftUI project, and if that's why you're here, go do it. But if you just want the feature on your Mac today, the honest answer is that a polished island is weeks of tuning, not an afternoon, and several good apps already exist:

  • boring.notch is free and open source, so it doubles as a working reference: media controls, a file shelf, a HUD replacement.
  • NotchNook and Alcove are the polished commercial options, refined widgets and files on one, animation and notifications on the other. Both are paid; check their sites for current pricing, which moves around.
  • DynamicLake Pro covers a wide spread, from media and files to Bluetooth and live activities.
  • NotchBay, this site's own app, is the work-focused pick, led by meeting controls (one-tap Join, Zoom and Meet from the notch) and a clipboard tray that holds the last 60 clips and OCRs screenshots. It's $9 one-time with a 14-day money-back guarantee, and it needs macOS 26 Tahoe and a notched MacBook. It is newer than NotchNook and Alcove, its call controls need Accessibility permission, and its Meet controls drive Chromium browsers, not Safari.

Before deciding to build or buy, there is a plain walkthrough of how to get one installed in five minutes. That will tell you fast whether building it is worth your weekend.

#Frequently asked questions

Do these properties work on a plain NSWindow, or is NSPanel required?

level, ignoresMouseEvents and collectionBehavior are all NSWindow properties, so a plain NSWindow takes them and behaves the same. NSPanel is chosen for one thing the base class cannot do: the .nonactivatingPanel style mask, which stops a click on the island from activating the app and pulling key focus off whatever the user was typing into.

Does ignoresMouseEvents also block hover?

Yes. A window with ignoresMouseEvents set to true receives no mouse events at all, so mouse-moved events and tracking areas inside it are dead too. Hover-to-expand therefore needs either the property switched to false while the pointer is in range, or a global event monitor outside the window that watches the pointer and flips it.

Which collectionBehavior flag covers full-screen apps?

.canJoinAllSpaces covers ordinary desktops but not a full-screen Space, which macOS treats as its own Space owned by one app. Adding .fullScreenAuxiliary is what lets the panel appear alongside a full-screen window. Decide first whether it should: an island over a full-screen video is a design choice, not a default.

Everything above is the architecture NotchBay ships; where a build diverges, trust the profiler over this page. Corrections: hello@thedeepflux.com.
Deepak YadavCrafting beautiful digital consumer products.

Product designer and indie hacker. Founder of Ossian Design Lab. Builds and ships business and consumer digital products in public.

Follow on X

Read next.

Look up.
It's all right there.