rcaferati/swift-awesome-button
`SwiftAwesomeButton` is the SwiftUI package for expressive 3D-style buttons,
Install
Add the package in Xcode through File > Add Package Dependencies...:
https://github.com/rcaferati/swift-awesome-button.gitThen add the SwiftAwesomeButton product to your iOS app target.
Current Swift support:
- Swift 5.10
- iOS 16.0+
- SwiftUI
Basic Usage
import SwiftAwesomeButton
import SwiftUI
struct SaveButton: View {
var body: some View {
AwesomeButton(
child: "Save",
onPress: { _ in
print("Pressed")
}
)
}
}AwesomeButton supports both plain string labels and arbitrary SwiftUI labels.
AwesomeButton(
onPress: { _ in
print("Pressed")
}
) {
Label("Continue", systemImage: "arrow.right")
}<table> <tr> <td width="33%"> <img alt="Blue demo" src="screenshots/demo-button-blue-new.gif" /> </td> <td width="33%"> <img alt="Cartman demo" src="screenshots/demo-button-cartman.gif" /> </td> <td width="33%"> <img alt="Rick demo" src="screenshots/demo-button-rick.gif" /> </td> </tr> </table>
Size Changes
animateSize is enabled by default.
- fixed
width/heightchanges animate with the package size animation ThemedButtonsize preset changes animate because they resolve to fixed
width and height updates
- auto-width string labels grow and shrink when their measured target width
changes
- with
textTransitionplus auto width, wider labels animate text while
growing and narrower labels start text first, then shrink width after the text transition begins
animateSize: falsekeeps size changes instant- fixed-to-auto and auto-to-fixed changes remain instant
Swift keeps auto-width target measurement inside the package renderer. The hidden measurement path is internal and does not intercept input.
import SwiftAwesomeButton
import SwiftUI
struct SizeExample: View {
let isLong: Bool
var body: some View {
let label = isLong ? "Open analytics dashboard" : "Open"
VStack(spacing: 12) {
ThemedButton(
child: label,
name: .basic,
autoWidth: true,
textTransition: true
)
ThemedButton(
child: label,
name: .basic,
autoWidth: true,
animateSize: false
)
}
}
}Before / After / Extra Content
Use before and after for inline content rendered inside the button face, and extra for content rendered behind the active/content layers.
import SwiftAwesomeButton
import SwiftUI
struct ButtonContentExample: View {
var body: some View {
AwesomeButton(
before: AnyView(Image(systemName: "arrow.left").foregroundStyle(.white)),
after: AnyView(Image(systemName: "arrow.right").foregroundStyle(.white)),
extra: AnyView(
LinearGradient(
colors: [
Color(red: 0.30, green: 0.39, blue: 0.82),
Color(red: 0.74, green: 0.19, blue: 0.51),
Color(red: 0.96, green: 0.44, blue: 0.20),
Color(red: 1.00, green: 0.84, blue: 0.46),
],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
),
style: AwesomeButtonStyle(
foregroundColor: .white
)
) {
Text("Continue")
.fontWeight(.bold)
.foregroundStyle(.white)
}
}
}Built-in Theme Contract
Theme Names
basicbojackcartmanmysterionc137ricksummerbruce
Variants
primarysecondaryanchordangerdisabledflatxmessengerfacebookgithublinkedinwhatsappredditpinterestyoutube
Sizes
iconsmallmediumlarge
Selected Parameters
The public surface is typed through AwesomeButton, ThemedButton, and their UIKit wrappers.
AwesomeButton
| Parameter | Type | Default | Description | | --- | --- | --- | --- | | child | String? | nil | Plain string label. String labels support textTransition. | | onPress | AwesomeButtonPressCallback? | nil | Main press callback. In progress mode it receives the completion handle. | | onLongPress | (() -> Void)? | nil | Optional long-press callback. | | disabled | Bool | false | Disables interactions. | | width | CGFloat? | nil | Fixed width, or leave nil for auto width. Pair with stretch for full width. | | height | CGFloat | 52 | Face height before the raise layer is added. | | paddingHorizontal | CGFloat? | 16 resolved | Horizontal content padding. | | before | AnyView? | nil | Content rendered before the main label inside the button face. | | after | AnyView? | nil | Content rendered after the main label inside the button face. | | extra | AnyView? | nil | Content rendered behind the active/content layers. | | stretch | Bool | false | Makes the button fill the available horizontal space. | | style | AwesomeButtonStyle? | nil | Visual override surface for colors, border, raise, animation, and typography. | | activeOpacity | Double | 1 | Opacity applied while the non-progress button is pressed. | | debouncedPressTime | TimeInterval | 0 | Debounces onPress dispatch. | | progress | Bool | false | Enables the progress-button flow. | | showProgressBar | Bool | true | Shows or hides the loading bar during progress. | | progressLoadingTime | TimeInterval | 3 | Duration of the loading bar travel in progress mode. | | animateSize | Bool | true | Animates fixed-size geometry changes and auto-width string-label changes. | | textTransition | Bool | false | Enables the built-in scramble/reveal animation when a plain string label changes. | | animatedPlaceholder | Bool | true | Enables the shimmer loop when the button has no child. | | hapticOnPress | Bool | true | Enables iOS haptic feedback on press. |
ThemedButton Additional Parameters
| Parameter | Type | Default | Description | | --- | --- | --- | --- | | config | ThemeDefinition? | nil | Explicit theme object. When provided, it takes precedence over name and index. | | index | Int? | nil | Theme index used by getTheme(index:) when config and name are not provided. | | name | ThemeName? | nil | Named built-in theme selector. | | type | ButtonVariant | .primary | Built-in variant to resolve from the selected theme. | | size | ButtonSize | .medium | Built-in theme size preset. | | flat | Bool | false | Requests the flat theme variant when available. | | transparent | Bool | false | Makes the visible shell layers transparent while keeping content, press, and progress feedback active. | | autoWidth | Bool | false | Requests measured auto width instead of the size preset width. |
Development
Primary package quality gate:
xcodebuild -scheme swift-awesome-button -destination 'platform=iOS Simulator,name=iPhone 17 Pro' testTo validate the demo app:
xcodebuild -project Examples/IOSAwesomeButtonDemoApp/IOSAwesomeButtonDemoApp.xcodeproj -scheme IOSAwesomeButtonDemoApp -destination 'generic/platform=iOS Simulator' buildPlain swift test is not the supported validation command for this repository. The package is intentionally iOS-only, and that command attempts to compile the SwiftUI target for macOS. Use the iOS Simulator xcodebuild test command above for release validation.
Example App
The manual acceptance app lives in Examples/IOSAwesomeButtonDemoApp.
The app includes:
Themedtab with nested theme navigation and the full themed showcaseProgresstab with dedicated progress-button demosSocialtab with social-button demosSize Changestab for text and geometry transition demos
Open it in Xcode from the repository root:
open Examples/IOSAwesomeButtonDemoApp/IOSAwesomeButtonDemoApp.xcodeprojBuild it from the repository root:
xcodebuild -project Examples/IOSAwesomeButtonDemoApp/IOSAwesomeButtonDemoApp.xcodeproj -scheme IOSAwesomeButtonDemoApp -destination 'generic/platform=iOS Simulator' buildLicense
MIT. See LICENSE. Third-party asset notices are listed in NOTICE.md.
Package Metadata
Repository: rcaferati/swift-awesome-button
Default branch: main
README: README.md