Agents swiftui-components
Reference for SwiftUI built-in components, layout tools, and visual styling for iOS-native interfaces
install
source · Clone the upstream repo
git clone https://github.com/aRustyDev/agents
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/aRustyDev/agents "$T" && mkdir -p ~/.claude/skills && cp -r "$T/content/plugins/frontend/swiftui-dev/skills/swiftui-components" ~/.claude/skills/arustydev-agents-swiftui-components && rm -rf "$T"
manifest:
content/plugins/frontend/swiftui-dev/skills/swiftui-components/SKILL.mdsource content
SwiftUI Components & Layouts
Purpose
Reference for SwiftUI's built-in components, layout tools, and visual styling for creating polished, iOS-native interfaces.
Layout Containers
Stack Views
// Vertical stack VStack(alignment: .leading, spacing: 12) { Text("Title") .font(.headline) Text("Subtitle") .font(.subheadline) .foregroundStyle(.secondary) } // Horizontal stack HStack(spacing: 16) { Image(systemName: "star.fill") Text("Featured") Spacer() Text("5.0") } // Layered stack (back to front) ZStack(alignment: .bottomTrailing) { Image("photo") .resizable() .aspectRatio(contentMode: .fill) Text("New") .padding(8) .background(.red) .clipShape(Capsule()) .padding(8) }
Lazy Grids
// Adaptive grid - columns adjust to fit let adaptiveColumns = [ GridItem(.adaptive(minimum: 150, maximum: 200)) ] LazyVGrid(columns: adaptiveColumns, spacing: 16) { ForEach(items) { item in ItemCard(item: item) } } // Fixed grid - explicit column count let fixedColumns = [ GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible()) ] LazyVGrid(columns: fixedColumns, spacing: 12) { ForEach(items) { item in ItemThumbnail(item: item) } } // Horizontal grid let rows = [GridItem(.fixed(100)), GridItem(.fixed(100))] LazyHGrid(rows: rows, spacing: 16) { ForEach(items) { item in ItemRow(item: item) } }
GeometryReader
struct ResponsiveCard: View { var body: some View { GeometryReader { geometry in HStack(spacing: 0) { // Left side takes 1/3 Image("thumbnail") .resizable() .frame(width: geometry.size.width * 0.33) // Right side takes 2/3 VStack(alignment: .leading) { Text("Title") Text("Description") } .frame(maxWidth: .infinity) .padding() } } .frame(height: 120) } } // Reading safe area GeometryReader { geometry in VStack { Text("Top inset: \(geometry.safeAreaInsets.top)") } }
ViewThatFits
// Automatically chooses layout that fits ViewThatFits { // Try horizontal first HStack { Image(systemName: "star") Text("Long descriptive label") Text("Details") } // Fall back to vertical if horizontal doesn't fit VStack { Image(systemName: "star") Text("Long descriptive label") Text("Details") } }
Built-in Components
Lists
List { // Simple rows ForEach(items) { item in Text(item.name) } // Sections Section("Favorites") { ForEach(favorites) { item in ItemRow(item: item) } } Section("Recent") { ForEach(recent) { item in ItemRow(item: item) } } } .listStyle(.insetGrouped) // iOS-style grouped list // Swipe actions List { ForEach(items) { item in ItemRow(item: item) .swipeActions(edge: .trailing) { Button(role: .destructive) { delete(item) } label: { Label("Delete", systemImage: "trash") } } .swipeActions(edge: .leading) { Button { toggleFavorite(item) } label: { Label("Favorite", systemImage: "star") } .tint(.yellow) } } }
Navigation
// Tab-based navigation TabView { HomeView() .tabItem { Label("Home", systemImage: "house") } SearchView() .tabItem { Label("Search", systemImage: "magnifyingglass") } ProfileView() .tabItem { Label("Profile", systemImage: "person") } } // Stack-based navigation (iOS 16+) NavigationStack { List(items) { item in NavigationLink(value: item) { ItemRow(item: item) } } .navigationTitle("Items") .navigationDestination(for: Item.self) { item in ItemDetailView(item: item) } } // Split view (iPad/Mac) NavigationSplitView { List(categories, selection: $selectedCategory) { category in Text(category.name) } } detail: { if let category = selectedCategory { CategoryDetailView(category: category) } else { Text("Select a category") } }
Forms & Controls
Form { Section("Account") { TextField("Username", text: $username) SecureField("Password", text: $password) } Section("Preferences") { Toggle("Notifications", isOn: $notificationsEnabled) Picker("Theme", selection: $theme) { Text("System").tag(Theme.system) Text("Light").tag(Theme.light) Text("Dark").tag(Theme.dark) } Stepper("Font Size: \(fontSize)", value: $fontSize, in: 12...24) Slider(value: $volume, in: 0...100) { Text("Volume") } } Section { DatePicker("Date", selection: $date, displayedComponents: .date) ColorPicker("Accent Color", selection: $accentColor) } }
SF Symbols
// Basic usage Image(systemName: "star.fill") // With configuration Image(systemName: "heart.fill") .symbolRenderingMode(.multicolor) .font(.title) // Variable value (iOS 16+) Image(systemName: "speaker.wave.3.fill", variableValue: volume) // Symbol effects (iOS 17+) Image(systemName: "wifi") .symbolEffect(.variableColor.iterative) Image(systemName: "arrow.down.circle") .symbolEffect(.bounce, value: downloadStarted) // In labels Label("Downloads", systemImage: "arrow.down.circle") .labelStyle(.titleAndIcon)
Visual Styling
Shadows & Depth
// Drop shadow Text("Elevated") .padding() .background(.white) .clipShape(RoundedRectangle(cornerRadius: 12)) .shadow(color: .black.opacity(0.1), radius: 8, x: 0, y: 4) // Inner shadow effect RoundedRectangle(cornerRadius: 12) .fill(.white) .overlay( RoundedRectangle(cornerRadius: 12) .stroke(.gray.opacity(0.2), lineWidth: 1) )
Gradients
// Linear gradient Rectangle() .fill( LinearGradient( colors: [.blue, .purple], startPoint: .topLeading, endPoint: .bottomTrailing ) ) // Radial gradient Circle() .fill( RadialGradient( colors: [.yellow, .orange], center: .center, startRadius: 0, endRadius: 100 ) ) // Angular gradient Circle() .fill( AngularGradient( colors: [.red, .yellow, .green, .blue, .purple, .red], center: .center ) ) // Mesh gradient (iOS 18+) MeshGradient( width: 3, height: 3, points: [ [0, 0], [0.5, 0], [1, 0], [0, 0.5], [0.5, 0.5], [1, 0.5], [0, 1], [0.5, 1], [1, 1] ], colors: [ .red, .orange, .yellow, .green, .blue, .purple, .pink, .cyan, .mint ] )
Blur & Materials
// Background blur ZStack { Image("background") .resizable() VStack { Text("Content") } .padding() .background(.ultraThinMaterial) .clipShape(RoundedRectangle(cornerRadius: 16)) } // Material options: .regularMaterial, .thinMaterial, .ultraThinMaterial, // .thickMaterial, .ultraThickMaterial, .bar // Custom blur Image("photo") .blur(radius: 10)
Custom Shapes
// Built-in shapes Circle() Ellipse() Rectangle() RoundedRectangle(cornerRadius: 12) Capsule() UnevenRoundedRectangle( topLeadingRadius: 20, bottomLeadingRadius: 0, bottomTrailingRadius: 0, topTrailingRadius: 20 ) // Custom shape struct Triangle: Shape { func path(in rect: CGRect) -> Path { Path { path in path.move(to: CGPoint(x: rect.midX, y: rect.minY)) path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY)) path.closeSubpath() } } } // Usage Triangle() .fill(.blue) .frame(width: 100, height: 100)
Animations
Basic Animations
struct AnimatedButton: View { @State private var isPressed = false var body: some View { Button("Tap Me") { // Action } .scaleEffect(isPressed ? 0.95 : 1.0) .animation(.spring(response: 0.3, dampingFraction: 0.6), value: isPressed) .onLongPressGesture(minimumDuration: .infinity, pressing: { pressing in isPressed = pressing }, perform: {}) } }
Transitions
struct ContentView: View { @State private var showDetails = false var body: some View { VStack { Button("Toggle") { withAnimation(.spring) { showDetails.toggle() } } if showDetails { DetailView() .transition(.asymmetric( insertion: .scale.combined(with: .opacity), removal: .slide )) } } } }
Matched Geometry Effect
struct HeroAnimation: View { @Namespace private var animation @State private var isExpanded = false var body: some View { VStack { if isExpanded { ExpandedCard(namespace: animation) } else { CompactCard(namespace: animation) } } .onTapGesture { withAnimation(.spring(response: 0.4, dampingFraction: 0.8)) { isExpanded.toggle() } } } } struct CompactCard: View { var namespace: Namespace.ID var body: some View { RoundedRectangle(cornerRadius: 12) .fill(.blue) .matchedGeometryEffect(id: "card", in: namespace) .frame(width: 100, height: 100) } }
Phase Animator (iOS 17+)
struct PulsingDot: View { var body: some View { Circle() .fill(.blue) .frame(width: 20, height: 20) .phaseAnimator([false, true]) { content, phase in content .scaleEffect(phase ? 1.2 : 1.0) .opacity(phase ? 0.7 : 1.0) } animation: { _ in .easeInOut(duration: 0.8) } } }
Best Practices
- Use semantic colors -
,.primary
,.secondary.background - Prefer built-in components - Match platform conventions
- Use SF Symbols - Consistent, scalable iconography
- Test on multiple sizes - iPhone SE to iPad Pro
- Support Dark Mode - Use adaptive colors and materials
- Animate state changes - Smooth transitions improve UX
Related Skills
- swiftui-architecture: Overall app structure
- swiftui-gestures: User interactions
- swiftui-data-flow: State management