Swift package that handles the annoying task of positioning UI elements around the iPhone's notch or Dynamic Island, (aka the device ears!)
NotchMyProblem is a lightweight Swift package that makes it easy to position buttons and other UI elements around the notch or Dynamic Island on modern iPhones. It automatically detects the device's top cutout and provides tools to create beautiful, adaptive layouts without the hassle of manual positioning.
- In Xcode go to File > Add Packages…
- Enter the repository URL:
https://github.com/Aeastr/NotchMyProblem - Click Add Package
Alternatively, add it to your Package.swift dependencies:
// Package.swift
dependencies: [
.package(url: "https://github.com/Aeastr/NotchMyProblem.git", from: "2.0.0")
]NotchMyProblem automatically detects the device type and adjusts the UI accordingly, ensuring your buttons are perfectly positioned regardless of the device model.
| Model | Dynamic Island | Tested |
|---|---|---|
| iPhone 17 | ✅ | ✅ |
| iPhone 17 Pro | ✅ | ✅ |
| iPhone 17 Pro Max | ✅ | ✅ |
| iPhone Air | ✅ | ✅ |
The simplest way to use NotchMyProblem is with the included CutoutAccessoryView.
import SwiftUI
import NotchMyProblem
struct MyView: View {
var body: some View {
ZStack {
// Your main content here
// Buttons positioned around the notch/island
CutoutAccessoryView(
padding: .auto,
leadingContent: {
Button(action: { print("Left tapped") }) {
Image(systemName: "gear")
}
},
trailingContent: {
Button(action: { print("Right tapped") }) {
Text("Save")
}
}
)
}
}
}Modern iPhones have Notches, Dynamic Islands, and heavily rounded corners. If you place buttons or other UI elements too close to these cutouts you risk:
- Elements appearing cramped or uncomfortably close to the cutout
- Parts of your UI being clipped by the curved screen edges
- Inconsistent spacing across different device models
By adding padding that scales with the actual cutout dimensions, NotchMyProblem ensures that your content:
- Always sits at a safe distance from the notch/island
- Never collides with the device’s rounded corners
- Maintains a consistent, polished look on every supported iPhone
- Cutout padding – extra space around the display cut out
- Content padding – extra space on either side of your HStack content
- Vertical padding – extra space above and below your content
Use the padding parameter when initializing CutoutAccessoryView:
CutoutAccessoryView(
padding: .auto,
leadingContent: { /* … */ },
trailingContent:{ /* … */ }
)-
.auto
Uses intelligent curves that adapt to cutout size. Smaller cutouts get more padding, larger cutouts get less padding - the opposite of simple percentage-based approaches. This ensures optimal spacing across all device types:- Dynamic Island (narrow): Gets generous padding for breathing room
- iPhone notch (wide): Gets minimal padding since the cutout already creates natural spacing
- Uses inverse relationship:
padding = base - (cutoutWidth × slope)with min/max bounds
-
.none
No extra padding; your views will hug the safe-area edges exactly. -
.custom(cutout: , content: , vertical:)
Supply closures to compute each padding dynamically:CutoutAccessoryView( padding: .custom( cutout: { cutoutW in cutoutW / 12 }, // 1/12 of cutout width content: { cutoutW in cutoutW / 6 }, // 1/6 of cutout width vertical:{ cutoutH in cutoutH * 0.2 } // 20% of cutout height ), leadingContent: { /* … */ }, trailingContent:{ /* … */ } )
Some devices report incorrect notch dimensions through the API. Overrides correct the reported values to match actual device dimensions, ensuring consistent UI across all devices.
NotchMyProblem has overrides by default already, devices like the iPhone 16e require this to correctly proportion elements, you do not need to figure these out for yourself, but you can create your own adjustments, or configure other devices
// In your App’s initialization (e.g. in @main or AppDelegate)
NotchMyProblem.globalOverrides = [
.series(prefix: "iPhone13", scale: 0.95, heightFactor: 1.0, radius: 27),
DeviceOverride(modelIdentifier: "iPhone14,3", scale: 0.8, heightFactor: 0.7)
]// At runtime, for specific cases
NotchMyProblem.shared.overrides = [
DeviceOverride(modelIdentifier: "iPhone14,3", scale: 0.8, heightFactor: 0.7)
]CutoutAccessoryView(/* … */)
.notchOverride(.series(prefix: "iPhone14", scale: 0.6, heightFactor: 0.6))- View-specific overrides
- Instance-specific exact model
- Instance-specific series prefix
- Global exact model
- Global series prefix
let rawRect = NotchMyProblem.exclusionRect // raw API result
let adjusted = NotchMyProblem.shared.adjustedExclusionRect // with global/instance overrides
let customRect = NotchMyProblem.shared.adjustedExclusionRect(using: myOverrides)Filter Console with subsystem com.notchmyproblem to see debug/info/error logs.
- TopNotch
- Uses private APIs safely—use at your own risk
Built with 🍏📱🏝️ by Aether



