Reference for SwiftUI's built-in components, layout tools, and visual styling for creating polished, iOS-native interfaces.
Provides SwiftUI component references and layout examples for building polished iOS interfaces.
npx claudepluginhub arustydev/aiThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Reference for SwiftUI's built-in components, layout tools, and visual styling for creating polished, iOS-native interfaces.
// 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)
}
// 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)
}
}
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)")
}
}
// 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")
}
}
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)
}
}
}
// 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")
}
}
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)
}
}
// 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)
// 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)
)
// 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
]
)
// 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)
// 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)
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: {})
}
}
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
))
}
}
}
}
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)
}
}
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)
}
}
}
.primary, .secondary, .backgroundActivates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.