swiftship/internal/skills/data/always-tvos/navigation/SKILL.md
tvOS navigation: top TabView tabs, focus-based drill-down, full-screen presentations. Use when working on tvOS navigation patterns, tab bars, or focus-driven transitions. Triggers: TabView, NavigationStack, focus, tab bar, navigation.
npx skillsauth add abdullah4ai/apple-dev-docs navigationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| Pattern | When to Use |
|---------|-------------|
| TabView (top tabs) | Main app sections (2-7 tabs at top of screen) |
| NavigationStack | Hierarchical drill-down within a tab |
| .fullScreenCover | Immersive content (player, detail) |
| .sheet | Secondary input, settings |
| .alert / .confirmationDialog | Confirmation, destructive actions |
tvOS shows tabs at the TOP of the screen, not bottom:
TabView {
Tab("Home", systemImage: "house") {
HomeView()
}
Tab("Browse", systemImage: "square.grid.2x2") {
BrowseView()
}
Tab("Search", systemImage: "magnifyingglass") {
SearchView()
}
Tab("Settings", systemImage: "gear") {
SettingsView()
}
}
NavigationStack {
ScrollView {
LazyVStack(alignment: .leading, spacing: 60) {
ShelfSection(title: "Continue Watching", items: continueWatching)
ShelfSection(title: "Recommended", items: recommended)
}
}
.navigationDestination(for: Item.self) { item in
ItemDetailView(item: item)
}
.navigationTitle("Home")
}
@State private var playingItem: Item?
.fullScreenCover(item: $playingItem) { item in
PlayerView(item: item)
}
enum Route: Hashable {
case detail(Item)
case category(Category)
case settings
}
NavigationStack {
ContentView()
.navigationDestination(for: Route.self) { route in
switch route {
case .detail(let item):
ItemDetailView(item: item)
case .category(let cat):
CategoryView(category: cat)
case .settings:
SettingsView()
}
}
}
The Siri Remote moves focus between elements — no touch events:
@FocusState private var focusedItem: Item.ID?
ForEach(items) { item in
Button { select(item) } label: {
ItemCard(item: item)
}
.focused($focusedItem, equals: item.id)
}
.onChange(of: focusedItem) { _, newValue in
// Update preview/hero when focus changes
if let id = newValue {
selectedPreview = items.first(where: { $0.id == id })
}
}
.popover — use .sheet or .fullScreenCoverNavigationSplitView sidebar — use TabView with sections.presentationDetents — sheets have fixed presentationTabView with top tabs for main app sectionsNavigationStack for drill-down within a tab.fullScreenCover for immersive content like media playbacktesting
Use for 3D games: racing, 3D sports, board games, marble maze, tower defense, bowling. SceneKit + SceneView architecture, 3D scene hierarchy, physics, game loop, primitives, materials, cameras, particles, audio.
documentation
Game UI patterns: SwiftUI HUD overlays on SpriteKit, menus (main/pause/game-over), virtual joystick/d-pad, score displays, health bars, tutorial onboarding.
tools
Download free game sprites/textures/3D models and generate procedural assets. Covers nw_download_asset tool, texture factories, sprite atlas organization, 3D model loading, and programmatic asset creation.
testing
Use for 2D games: arcade, puzzle, sports, ping pong, platformer, shooter, 2D racing. SpriteKit + SpriteView architecture, scene hierarchy, physics, game loop, audio, particles, game feel.