A high-performance, cross-platform SwiftUI grid navigation library with full keyboard support.
- 🚀 High Performance: O(1) navigation using direct array indexing
- ⌨️ Full Keyboard Support: Arrow keys, Return, and Escape on macOS
- 📱 Cross-Platform: Works on both iOS and macOS with platform-appropriate interactions
- 🎯 Generic: Supports any data type conforming to
GridNavigable - 🔧 Flexible: Multiple column layout options and customizable views
- ♿ Accessible: Built-in focus management and accessibility support
- iOS 17.0+ / macOS 14.0+
- Swift 5.9+
- SwiftUI
Add this package to your project using Xcode:
- File → Add Package Dependencies...
- Enter the package URL
- Choose the version and add to your target
Or add it to your Package.swift:
dependencies: [
.package(url: "https://github.com/jjnicolas/GridNavigation", from: "1.0.0")
]import SwiftUI
import GridNavigation
struct Movie: GridNavigable {
let id = UUID()
let title: String
let year: Int
}
struct ContentView: View {
let movies = [
Movie(title: "The Matrix", year: 1999),
Movie(title: "Inception", year: 2010),
Movie(title: "Interstellar", year: 2014)
]
var body: some View {
NavigationStack {
GridNavigationView(
items: movies,
columnCount: 3,
columnWidth: 150
) { movie in
// Cell content
VStack {
Text(movie.title)
.font(.headline)
Text("\\(movie.year)")
.font(.caption)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
} detailContent: { movie in
// Detail content
VStack {
Text(movie.title)
.font(.largeTitle)
Text("Released in \\(movie.year)")
.font(.title2)
}
.padding()
}
}
}
}- Arrow keys: Navigate between grid items
- Return key: Open detail view
- Escape key: Close detail view (add to your detail views)
- Focus indicators: Automatic focus rings
- Smooth scrolling: Focused items stay in view
- Touch navigation: Tap to navigate
- Swipe back: Native iOS back gesture
- Navigation transitions: Standard iOS animations
Your data types must conform to GridNavigable:
struct MyItem: GridNavigable {
let id = UUID() // Required: Unique identifier
let title: String
let description: String
}GridNavigationView(
items: items,
columnCount: 4,
columnWidth: 150
) { /* ... */ } detailContent: { /* ... */ }GridNavigationView(
items: items,
minItemWidth: 100,
maxItemWidth: 200
) { /* ... */ } detailContent: { /* ... */ }let customColumns = [
GridItem(.flexible(minimum: 100)),
GridItem(.fixed(150)),
GridItem(.flexible(minimum: 100))
]
GridNavigationView(
items: items,
columns: customColumns
) { /* ... */ } detailContent: { /* ... */ }For full keyboard support on macOS, add escape key handling to your detail views:
struct MovieDetailView: View {
let movie: Movie
@Environment(\\.dismiss) private var dismiss
var body: some View {
VStack {
Text(movie.title)
.font(.largeTitle)
// ... other content
}
.padding()
#if os(macOS)
.onKeyPress(keys: [.escape]) { _ in
dismiss()
return .handled
}
#endif
}
}This library is designed for high performance:
- O(1) navigation: Direct array index access instead of UUID searches
- Lazy loading: Uses SwiftUI's LazyVGrid for memory efficiency
- Minimal state: Only essential state is maintained
- Platform optimization: Keyboard features only compiled for macOS
The library includes comprehensive examples:
- Movie Grid: Film catalog with ratings and genres
- Photo Gallery: Image grid with metadata
- Product Catalog: E-commerce style grid with availability
Part of Julien Nicolas's macOS & iOS apps & utilities.
MIT License - see LICENSE file for details.
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.