A collection of SwiftUI layout patterns and UI components for iOS development.
The PersistentBackgroundNavigation component maintains consistent backgrounds across NavigationStack transitions.
This pattern was developed through extensive iteration to address SwiftUI's navigation background consistency issues.
- Automatic Navigation: Background persistence without manual modifiers
- Persistent Background System: NavigationStack wrapper that maintains backgrounds during transitions
- Flexible API: Choose automatic convenience wrappers or manual control
- Custom Backgrounds: Use any SwiftUI view as a persistent background (images, videos, animations)
- Gradient Backgrounds: Configurable gradients with vignette effects (iOS 18+ Liquid Glass compatible)
- Color Palettes: Extensible palette system with nine built-in themes
- Appearance Adaptive: Automatic light/dark mode support
- Material Integration: Compatible with iOS 18+ material system
- Conditional Modifiers: Clean
.if()syntax for conditional view transformations - Glass Shadows: Unified
.glassShadow()system for Liquid Glass UI with press states - Adaptive Corner Radius: Proportional corner radius that scales across device sizes
- Adaptive Inner Border: Luminance-aware highlight borders for colored buttons
- Luminance Analysis: ITU-R BT.709 perceptual brightness calculations
- Contrast Detection: Automatic text color selection for backgrounds
- Parameterized Thresholds: Custom thresholds for
isLight/isDarkchecks - Gradient Luminance: Weighted luminance calculation for multi-color backgrounds
- Color Blending: Linear interpolation with efficient pre-resolved components
- Hex Conversion: Full support for RGB, RRGGBB, and AARRGGBB formats
- Haptic Feedback: Low-latency haptics with pre-instantiated generators (iOS)
- Shake Detection: Simple
.onShake()modifier (iOS) - Time Formatting: Duration formatting as MM:SS
- Pure SwiftUI: No external dependencies
- Battery Optimized: Static gradients with minimal impact
- Pre-instantiated Haptics: Avoid allocation delays during interactions
import SwiftUI
import SeaBearKit
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
PersistentBackgroundNavigation(palette: .sunset) {
ContentView()
}
}
}
}Automatic (Recommended) - No manual configuration required:
PersistentNavigationLink("View Details") {
DetailView() // Background persists automatically!
}Manual (Advanced) - For precise control:
NavigationLink("View Details") {
DetailView()
.clearNavigationBackground()
}Both approaches work - choose what fits your style.
- iOS 17.0+ (iOS 18+ recommended for optimal experience)
- Swift 6.0+
- Xcode 15.0+
Note: While iOS 17 is supported, iOS 18+ provides the optimal experience using .containerBackground(for: .navigation). iOS 17 uses a fallback approach that works well in most cases.
Add to your Package.swift:
dependencies: [
.package(url: "https://github.com/seabearDEV/SeaBearKit.git", from: "1.0.0")
]Or in Xcode: File → Add Package Dependencies → Enter repository URL
SeaBearKit provides flexibility to match your coding style:
Use PersistentNavigationLink for automatic navigation:
PersistentNavigationLink("Details") {
DetailView()
}
// Custom label
PersistentNavigationLink {
DetailView()
} label: {
HStack {
Image(systemName: "star")
Text("Featured")
}
}Value-based navigation:
NavigationStack {
List(items) { item in
Button(item.name) {
selectedItem = item
}
}
}
.persistentNavigationDestination(for: Item.self) { item in
ItemDetailView(item: item)
}Use standard NavigationLink with .clearNavigationBackground():
NavigationLink("Settings") {
SettingsView()
.clearNavigationBackground()
.customModifier() // Add your own modifiers
}- Automatic: Large apps, team projects, when you want it to "just work"
- Manual: Edge cases, when you need additional modifiers, precise control
Both approaches coexist - use what makes sense for each screen.
The library includes nine built-in palettes and supports custom palette creation:
// Built-in palettes
.sunset // Warm tones: coral, orange, yellow
.ocean // Cool blues and teals
.forest // Natural greens and earth tones
.monochrome // Grayscale
.midnight // Dark blues and purples (for dark themes)
.cherryBlossom // Soft pinks and roses
.autumn // Warm oranges, reds, and browns
.lavender // Calming purple and blue tones
.mint // Fresh greens and blues
// Custom palette
ColorPalette(
name: "Custom",
colors: [.red, .orange, .yellow, .green, .blue],
gradientIndices: [0, 2, 4],
gradientOpacities: [0.4, 0.5, 0.6]
)Two configuration modes are available:
// Standard (gradient background)
PersistentBackgroundNavigation(palette: .sunset)
// Minimal (system background only)
PersistentBackgroundNavigation.minimal(palette: .forest)Use any SwiftUI view as a persistent background:
PersistentBackgroundNavigation {
// Your custom background
Image("hero-image")
.resizable()
.aspectRatio(contentMode: .fill)
.ignoresSafeArea()
} content: {
ContentView()
}Examples of custom backgrounds:
- Images: Hero images, patterns, textures
- Videos: Background video playback
- Animated Gradients: Time-based color transitions
- Custom Views: Any SwiftUI composition
The same persistent architecture applies - your custom background stays consistent across all navigation transitions.
SeaBearKit provides essential view modifiers for cleaner, more maintainable SwiftUI code.
Apply transformations based on conditions without duplicating view code:
Text("Hello")
.if(isHighlighted) { view in
view.foregroundStyle(.red)
}
// With both branches
Text("Status")
.if(isActive,
then: { $0.foregroundStyle(.green) },
else: { $0.foregroundStyle(.gray) }
)Unified shadow system optimized for Liquid Glass design with automatic press state handling:
// Basic shadow
Button("Action") { }
.buttonStyle(.borderedProminent)
.glassShadow()
// With press state
Text("Press Me")
.padding()
.background(.regularMaterial)
.glassShadow(isPressed: isPressed, intensity: .regular)
// Different intensities
view.glassShadow(intensity: .subtle) // Subtle elevation
view.glassShadow(intensity: .regular) // Standard (default)
view.glassShadow(intensity: .prominent) // High elevationProportional corner radius system that scales consistently across device sizes:
// Using percentage (0% = square, 100% = circle)
RoundedRectangle(cornerRadius: 0)
.fill(.blue)
.frame(width: 100, height: 100)
.adaptiveCornerRadius(40, size: CGSize(width: 100, height: 100))
// Using predefined styles
CornerRadiusStyle.square // 0% - Perfect squares
CornerRadiusStyle.slight // 15% - Subtle corners
CornerRadiusStyle.moderate // 40% - Balanced
CornerRadiusStyle.round // 65% - Prominent curves
CornerRadiusStyle.circle // 100% - Perfect circlesLuminance-aware highlight border that creates depth on colored buttons:
RoundedRectangle(cornerRadius: 12)
.fill(buttonColor)
.adaptiveInnerBorder(color: buttonColor, cornerRadius: 12)Light colors get a subtle dark top edge, dark colors get a subtle light top edge.
Comprehensive color manipulation for dynamic theming:
// Luminance and contrast
let textColor = backgroundColor.contrastingColor() // Returns .black or .white
let isReadable = backgroundColor.isLight // true if luminance > 0.6
// Custom thresholds for light/dark detection
let isVeryLight = myColor.isLight(threshold: 0.7) // Stricter threshold
let isVeryDark = myColor.isDark(threshold: 0.2) // Stricter threshold
// Color blending
let blended = Color.blend(from: .red, to: .blue, progress: 0.5)
let lighter = myColor.adjustedBrightness(0.2)
let darker = myColor.adjustedBrightness(-0.2)
// Hex conversion
let hex = myColor.toHex() // "#ff5500"
let color = Color(hex: "#ff5500") // Supports RGB, RRGGBB, AARRGGBB
// Efficient repeated blending (resolve once, blend many)
let resolved = startColor.resolvedRGBA
for progress in stride(from: 0, to: 1, by: 0.1) {
let color = resolved.blend(to: endColor.resolvedRGBA, progress: progress)
}
// Gradient luminance (for multi-color backgrounds)
let gradientColors: [WeightedColor] = [
WeightedColor(color: .blue, opacity: 0.5),
WeightedColor(color: .purple, opacity: 0.7)
]
let effectiveLuminance = gradientColors.weightedLuminance
let textColor = gradientColors.contrastingColor() // Black or white for the gradientLow-latency haptic feedback with pre-instantiated generators:
// Impact feedback
HapticHelper.impact(.light)
HapticHelper.impact(.medium)
HapticHelper.impact(.rigid)
HapticHelper.impact(.heavy, intensity: 0.5)
// Notification feedback
HapticHelper.notification(.success)
HapticHelper.notification(.warning)
HapticHelper.notification(.error)
// Selection feedback (for pickers, segments)
HapticHelper.selection()
// Pre-warm for lower latency (optional)
HapticHelper.prepare(.medium)Simple shake gesture handling:
ContentView()
.onShake {
undoLastAction()
}Simple duration formatting:
45.formattedAsTime // "0:45"
125.formattedAsTime // "2:05"
3661.formattedAsTime // "61:01"
let duration: TimeInterval = 125.7
duration.formattedAsTime // "2:05"- Usage Guide - Comprehensive examples and best practices
- Architecture - Technical details and design decisions
- Liquid Glass Compliance - iOS 18+ Liquid Glass design system integration
- IMPORTANT - Critical requirement for all navigation views
- Demo App - Full working example showcasing all features
Standard SwiftUI NavigationStack implementations create their own backgrounds that can change inconsistently during transitions. This library addresses the issue through a layered approach:
ZStack {
PersistentBackground(palette: palette) // Lives outside navigation lifecycle
NavigationStack {
Content()
.containerBackground(for: .navigation) {
Color.clear // Makes navigation transparent
}
}
}This approach ensures consistent background rendering throughout navigation transitions.
This pattern was developed through iterative refinement to resolve background consistency issues in SwiftUI navigation. Key development milestones:
- Resolved background consistency during color palette transitions
- Centralized background rendering and improved gradient visibility
- Removed LinearGradient tint overlays to improve navigation rendering
- Standardized navigation transitions and view backgrounds
To view interactive previews:
git clone https://github.com/seabearDEV/SeaBearKit.git
cd SeaBearKit
open Package.swiftNavigate to PersistentBackgroundNavigation.swift and open Canvas (⌥⌘↩) for interactive preview.
Complete demo instructions available in DEMO.md
# Run all tests
swift test
# Build the package
swift build
# Open in Xcode
open Package.swiftAdd the package locally for testing:
// In your Package.swift
dependencies: [
.package(path: "/path/to/SeaBearKit")
]Or use Xcode: File → Add Package Dependencies → Add Local
Contributions are welcome! Please read CONTRIBUTING.md for:
- Development setup
- Code standards
- Testing requirements
- Pull request process
All contributions should maintain:
- iOS 17+ compatibility (iOS 18+ for optimal experience)
- Swift 6 strict concurrency compliance
- All tests passing (
swift test) - Documentation updates for API changes
- CHANGELOG.md updates
MIT License - see LICENSE file for details
Kory Hoopes - seabearDEV
This library was developed to address common challenges encountered in iOS development.