Window scene, so the macOS CommandGroup + Window recipe does not port. Present a shared AboutView in a .sheet with .presentationSizing(.fitted), optionally animated with .matchedTransitionSource and .navigationTransition(.zoom(sourceID:in:)) on iOS 18+, and anchor the trigger in a toolbar menu or a sidebar footer.Why this happens
On macOS the About box is a system-level concept: the app menu has an “About” item, and you can replace it with CommandGroup(replacing: CommandGroupPlacement.appInfo) and open your own window. That whole mechanism is AppKit menu plumbing. iOS has no app menu, no CommandGroupPlacement.appInfo, and no Window scene — Window is available on macOS and visionOS only. On iOS you get WindowGroup, and the only way to put something in front of the user is a sheet, a full-screen cover, a popover, or a push.
Apple’s Human Interface Guidelines also don’t spell out an About box for iPad apps the way they do for the Mac. So there is no “correct” anchor point to find. What you pick is a design decision, and the code follows from it: keep one AboutView that both platforms share, and change only the container — a Window on macOS, a fitted sheet on iOS.
A fitted sheet is the closest iOS equivalent. It sizes itself to the content instead of stretching to the full screen, which is what makes it read as an About panel rather than a page.
How to fix it
1. Write one platform-neutral AboutView
Keep the view free of any window or sheet logic. Read the version from the bundle instead of hardcoding it, so the About box can never drift from the build you shipped.
import SwiftUI
struct AboutView: View {
private var version: String {
let info = Bundle.main.infoDictionary
let short = info?["CFBundleShortVersionString"] as? String ?? "1.0"
let build = info?["CFBundleVersion"] as? String ?? "1"
return "Version \(short) (\(build))"
}
var body: some View {
VStack(spacing: 12) {
Image(systemName: "app.dashed")
.font(.system(size: 48))
.foregroundStyle(.tint)
Text("My App")
.font(.title2.bold())
Text(version)
.font(.footnote)
.foregroundStyle(.secondary)
Link("Website", destination: URL(string: "https://example.com")!)
Link("Privacy Policy", destination: URL(string: "https://example.com/privacy")!)
}
.font(.callout)
.multilineTextAlignment(.center)
.padding(24)
}
}
2. Present it as a fitted sheet on iOS
Put the trigger where an iOS user expects app-level information: a toolbar item that opens a small menu, or the footer of a NavigationSplitView sidebar on iPad. Then attach the sheet to the container, not to the button.
import SwiftUI
struct RootView: View {
@State private var showingAbout = false
@Namespace private var aboutTransition
var body: some View {
NavigationStack {
List {
Text("Main content")
}
.navigationTitle("My App")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
showingAbout = true
} label: {
Label("About", systemImage: "info.circle")
}
.matchedTransitionSource(id: "about", in: aboutTransition)
}
}
}
.sheet(isPresented: $showingAbout) {
AboutView()
.navigationTransition(.zoom(sourceID: "about", in: aboutTransition))
.presentationSizing(.fitted)
}
}
}
.presentationSizing(.fitted), .matchedTransitionSource(id:in:) and .navigationTransition(.zoom(sourceID:in:)) all require iOS 18 or later. The zoom transition is what makes the sheet appear to grow out of the toolbar button, which is the detail that sells it as an About panel. If you still support iOS 16 or 17, drop those two lines and use .presentationDetents([.medium]) instead — the sheet still fits the content reasonably well.
Swipe-to-dismiss works on a sheet, but an explicit control is friendlier. Add @Environment(\.dismiss) private var dismiss inside AboutView and a Button("Done") { dismiss() } at the bottom, or wrap the sheet content in a NavigationStack with a .confirmationAction toolbar item.
3. Keep the macOS window and share the same view
On macOS, keep the recipe you already have, but wrap it so the iOS target still compiles. Window and CommandGroupPlacement.appInfo are macOS-only, so they belong inside #if os(macOS).
import SwiftUI
@main
struct MyApp: App {
@Environment(\.openWindow) private var openWindow
var body: some Scene {
WindowGroup {
RootView()
}
#if os(macOS)
Window("About My App", id: "about") {
AboutView()
.frame(width: 320)
}
.windowResizability(.contentSize)
.commands {
CommandGroup(replacing: CommandGroupPlacement.appInfo) {
Button("About My App") {
openWindow(id: "about")
}
}
}
#endif
}
}
RootView is used unchanged on both platforms: on iOS the toolbar button opens the sheet, on macOS the same button can call openWindow(id: "about") if you want an in-app entry point as well as the menu item. Branch on #if os(macOS) inside the button action rather than duplicating the whole view.
4. Alternative anchors worth considering
- iPad sidebar footer. In a
NavigationSplitView, put the About button under aDivider()at the bottom of the sidebar, the way Mail and Notes surface account and app info. This mirrors the Mac’s menu placement without a menu. - A settings list row. If your app already has a settings screen, an “About” row that pushes
AboutViewis the least surprising option and needs no sheet at all. - A popover on iPad.
popover(isPresented:arrowEdge:)gives a small anchored panel, but it adapts to a sheet on compact width, so you end up maintaining two presentations for little gain.
How to prevent it
- Don’t copy macOS scene code into a multiplatform target without an
#if os(macOS)guard —Window,SettingsandCommandGroupPlacement.appInfowill not compile for iOS. - Never hardcode the version string in the About view; read
CFBundleShortVersionStringandCFBundleVersionfromBundle.main. - Attach
.sheetto a stable container such as theNavigationStack, not to a row that can be recycled by theList. - Keep
AboutViewfree of presentation modifiers so the same file works in a macOSWindowand an iOS sheet. - If you use the zoom transition, make sure the source view is still on screen when the sheet opens, otherwise the animation has nothing to grow from.
FAQ
Is there an Apple convention for an About screen on iOS?
No. The Human Interface Guidelines cover About windows for macOS apps but don’t prescribe an About box for iPad or iPhone apps. A fitted sheet, a settings row, or a sidebar footer are all reasonable; pick the one that matches where your app already puts app-level information.
Can I use the Window scene on iOS?
No. Window is available on macOS and visionOS only. On iOS the equivalent is a WindowGroup plus a sheet or a navigation push, which is why the macOS About-window recipe needs platform-specific code.
Where should the About button go on an iPhone?
A toolbar item in the top-right that opens the sheet is the simplest option and scales down from iPad. If your app has a settings screen, an “About” row there is even less intrusive and avoids adding a toolbar control.
Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.
