A library of reusable SwiftUI components that are missing from the SwiftUI framework. Includes buttons, gestures, grids, scroll views, view modifiers, property wrappers, and more.
- Button styles with press, highlight, opacity, and tap types
- Gesture modifiers for drag, magnification, and rotation
- Non-lazy grids for horizontal and vertical layouts
- Scroll view enhancements with scroll tracking and scroll-to support
- View modifiers for first-appear, notification listening, stretchy headers, and more
- Property wrappers for UserDefaults with type safety
- Async view builders for loading/error states
- Reusable views like AsyncCallToActionButton, CountdownViewBuilder, FlipView, and RootView
Details (Click to expand)
Add SwiftfulUI to your project.
https://github.com/SwiftfulThinking/SwiftfulUI.git
Import the package.
import SwiftfulUIDetails (Click to expand)
Wrap any view in a button with a custom style.
// Tap (default) — no visual effect
Text("Tap me")
.asButton {
// action
}
// Press — scale effect (0.975)
Text("Press me")
.asButton(.press) {
// action
}
// Highlight — accent color overlay with scale
Text("Highlight me")
.asButton(.highlight) {
// action
}
// Opacity — opacity effect (0.85)
Text("Opacity")
.asButton(.opacity) {
// action
}
// Custom — configure scale, opacity, brightness
Text("Custom")
.asButton(scale: 0.9, opacity: 0.8, brightness: 0.1) {
// action
}Wrap a view in a Link with a custom style.
Text("Visit Google")
.asWebLink {
URL(string: "https://www.google.com")
}Details (Click to expand)
// Wrap in AnyView
myView.any()
// Invisible tappable background
myView.tappableBackground()
// Remove default List row formatting
myView.removeListRowFormatting()
// Conditional view modifier
myView.ifSatisfiesCondition(someCondition) { view in
view.opacity(0.5)
}
// Call-to-action button styling
Text("Get Started")
.callToActionButton()
// With custom parameters
Text("Subscribe")
.callToActionButton(
font: .headline,
foregroundColor: .white,
backgroundColor: .accentColor,
verticalPadding: 12,
horizontalPadding: nil,
cornerRadius: 16
)Details (Click to expand)
// Execute action only on first appear (sync)
myView.onFirstAppear {
loadData()
}
// Execute async action only on first appear (iOS 15+)
myView.onFirstTask {
await fetchData()
}
// Execute action only on first disappear
myView.onFirstDisappear {
cleanup()
}
// Listen for NotificationCenter notifications
myView.onNotificationReceived(name: .myNotification) { notification in
handleNotification(notification)
}
// Stretchy header effect
ScrollView {
myHeaderView
.asStretchyHeader(startingHeight: 300)
}Details (Click to expand)
// Drag gesture with callbacks
myView.addDragGesture(
onChanged: { value in },
onEnded: { value in }
)
// Magnification gesture
myView.addMagnificationGesture(
onChanged: { value in },
onEnded: { value in }
)
// Rotation gesture
myView.addRotationGesture(
onChanged: { value in },
onEnded: { value in }
)Details (Click to expand)
Non-lazy grids that render all items immediately.
// Vertical grid
NonLazyVGrid(columns: 3, items: myItems) { item in
ItemView(item: item)
}
// Horizontal grid
NonLazyHGrid(rows: 2, items: myItems) { item in
ItemView(item: item)
}Details (Click to expand)
// Track scroll position changes
ScrollViewWithOnScrollChanged(
onScrollChanged: { offset in
// React to scroll offset
},
content: {
// Your content
}
)Details (Click to expand)
Type-safe UserDefaults property wrappers.
// For standard types (Bool, Int, Float, Double, String, URL)
@UserDefault(key: "has_seen_onboarding", startingValue: false)
var hasSeenOnboarding: Bool
// For String-backed enums
@UserDefaultEnum(key: "theme", startingValue: .system)
var theme: ThemeOptionDetails (Click to expand)
Handle async loading states declaratively.
AsyncCallToActionButton(
isLoading: isLoading,
title: "Save",
action: {
// action
}
)Details (Click to expand)
// RootView — handles app lifecycle events
RootView(
delegate: RootDelegate(
onApplicationDidAppear: { },
onApplicationWillEnterForeground: { _ in },
onApplicationDidBecomeActive: { },
onApplicationWillResignActive: { },
onApplicationDidEnterBackground: { },
onApplicationWillTerminate: { }
),
content: {
// Your app content
}
)
// CountdownViewBuilder — countdown timer display
CountdownViewBuilder(endDate: futureDate) { timeRemaining in
Text(timeRemaining)
}
// FlipView — 3D flip between front and back
FlipView(isFlipped: $isFlipped, front: {
FrontView()
}, back: {
BackView()
})Details (Click to expand)
Customizable progress bar with support for any numeric range (iOS 15+).
// Basic usage (0 to 100)
CustomProgressBar(
selection: 55,
range: 0...100
)
// With custom colors
CustomProgressBar(
selection: progress,
range: 0...100,
backgroundColor: Color.gray.opacity(0.3),
foregroundColor: .blue,
cornerRadius: 100,
height: 8
)
// With gradient foreground
CustomProgressBar(
selection: progress,
range: 0...100,
foreground: AnyShapeStyle(
LinearGradient(colors: [.blue, .purple], startPoint: .leading, endPoint: .trailing)
)
)Details (Click to expand)
A ZStack that lazily renders views based on a selection, with built-in support for SwiftUI transitions. Only the selected view is rendered (or optionally allows simultaneous rendering for transition animations).
// With Identifiable items
LazyZStack(
allowSimultaneous: true,
selection: selectedItem,
items: items
) { item in
ItemView(item: item)
.transition(.slide)
}
// With Int selection
LazyZStack(
allowSimultaneous: true,
selection: selectedIndex,
items: 0..<4
) { index in
PageView(index: index)
.transition(.slide)
}
// With Bool selection
LazyZStack(
allowSimultaneous: false,
selection: isShowingDetail
) { (value: Bool) in
if value {
DetailView()
} else {
ListView()
}
}allowSimultaneous: true— both old and new views render during transitionsallowSimultaneous: false— only the selected view renders at a time
Details (Click to expand)
- Backgrounds & Borders — Fill, border, and gradient background modifiers
- Fonts — Custom font modifiers with animation support
- GeometryReaders — Frame and location readers
- Redacted — Redacted/skeleton loading modifier
- TabBars — Customizable tab bar with TabBarItem support
- Toggles — Custom toggle view
See the SwiftUI Previews within source files for example implementations.
This package includes a .claude/swiftful-ui-rules.md with usage guidelines, component selection advice, and integration patterns for projects using Claude Code.
- iOS 13.0+
- macOS 10.14+
- tvOS 13.0+
SwiftfulUI is available under the MIT license.