Custom Alert View
Beautifully styled custom modal alert overlay with title, message, and customizable action buttons.
SwiftUI Implementation
Pure Vanilla Swift • No DependenciesalertBasicCustom.swift
import SwiftUI
struct UseCustomAlert: View {
@State private var showAlert: Bool = true
private func handleAction() {
print("Action triggered")
}
var body: some View {
VStack {
Button("Show Alert") {
showAlert.toggle()
}
}
.customAlert(
isPresented: $showAlert,
title: "Permanently Delete?",
message: "Do you really want to permanently delete this item? This action cannot be undone and all associated data will be lost.",
icon: Image(systemName: "trash.fill"),
iconTintColor: .red,
primaryAction: CustomAlertAction(label: "Delete", style: .destructive) {
handleAction()
showAlert = false
},
secondaryAction: CustomAlertAction(label: "Cancel", style: .cancel) {
showAlert = false
}
)
}
}
#Preview {
UseCustomAlert()
}
// Enum to define the style of an alert action button
enum CustomAlertActionStyle {
case primary
case secondary
case destructive
case cancel
var foregroundStyle: Color {
switch self {
case .primary: return .accentColor
case .secondary, .cancel: return .accentColor
case .destructive: return .red
}
}
var fontWeight: Font.Weight {
switch self {
case .primary, .destructive: return .semibold
case .secondary, .cancel: return .regular
}
}
}
struct CustomAlertAction {
let id = UUID()
let label: String
let style: CustomAlertActionStyle
let action: () -> Void
}
struct CustomAlertDisplayView: View {
let title: String
let message: String?
let icon: Image?
let iconTintColor: Color
// Actions
let primaryAction: CustomAlertAction
let secondaryAction: CustomAlertAction?
private var hasOnlyOneAction: Bool { secondaryAction == nil }
var body: some View {
VStack(spacing: 0) {
if let icon = icon {
icon
.resizable()
.scaledToFit()
.frame(width: 40, height: 40)
.foregroundColor(iconTintColor)
.padding(.bottom, 10)
}
Text(title)
.font(.system(size: 18, weight: .semibold))
.foregroundColor(.primary)
.multilineTextAlignment(.center)
.padding(.bottom, message == nil && icon == nil ? 20 : 6)
if let message = message, !message.isEmpty {
Text(message)
.font(.system(size: 14, weight: .regular))
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
.lineLimit(nil)
.fixedSize(horizontal: false, vertical: true) .padding(.horizontal) .padding(.bottom, 20)
}
buttonLayout
}
.padding(.top, 20)
.frame(maxWidth: 300)
.background(Material.regular)
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
.shadow(color: .black.opacity(0.12), radius: 20, x: 0, y: 8) .overlay(
RoundedRectangle(cornerRadius: 16, style: .continuous)
.stroke(Color.primary.opacity(0.1), lineWidth: 0.5)
)
}
@ViewBuilder
private var buttonLayout: some View {
VStack(spacing: 0) {
Divider()
if let secondaryAction = secondaryAction {
HStack(spacing: 0) {
alertButton(for: secondaryAction)
Divider()
alertButton(for: primaryAction)
}
.frame(height: 48)
} else {
alertButton(for: primaryAction)
.frame(height: 48)
}
}
}
@ViewBuilder
private func alertButton(for alertAction: CustomAlertAction) -> some View {
Button {
alertAction.action()
} label: {
Text(alertAction.label)
.font(.system(size: 17, weight: alertAction.style.fontWeight))
.foregroundStyle(alertAction.style.foregroundStyle)
.frame(maxWidth: .infinity, maxHeight: .infinity) .contentShape(Rectangle())
}
}
}
struct CustomAlertModifier: ViewModifier {
@Binding var isPresented: Bool
let title: String
let message: String?
let icon: Image?
let iconTintColor: Color
let primaryAction: CustomAlertAction
let secondaryAction: CustomAlertAction?
func body(content: Content) -> some View {
ZStack {
content
if isPresented {
Color.black.opacity(0.45)
.ignoresSafeArea()
.transition(.opacity)
CustomAlertDisplayView(
title: title,
message: message,
icon: icon,
iconTintColor: iconTintColor,
primaryAction: primaryAction,
secondaryAction: secondaryAction
)
.padding(.horizontal, 28)
.transition(alertTransition)
}
}
.animation(.spring(response: 0.35, dampingFraction: 0.8, blendDuration: 0), value: isPresented)
}
private var alertTransition: AnyTransition {
.asymmetric(
insertion: .opacity.combined(with: .scale(scale: 1.1, anchor: .center)),
removal: .opacity.combined(with: .scale(scale: 0.85, anchor: .center))
)
}
}
extension View {
func customAlert(
isPresented: Binding<Bool>,
title: String,
message: String? = nil,
icon: Image? = nil,
iconTintColor: Color = .accentColor, // Default icon tint
primaryAction: CustomAlertAction,
secondaryAction: CustomAlertAction? = nil
) -> some View {
self.modifier(
CustomAlertModifier(
isPresented: isPresented,
title: title,
message: message,
icon: icon,
iconTintColor: iconTintColor,
primaryAction: primaryAction,
secondaryAction: secondaryAction
)
)
}
}Ready to copy and paste directly into any Xcode project running iOS 16.0+.
Recommended Use Cases
- Dashboard overviews, stats tracking, and metric highlights
Component Details
- Category
- Alerts & Modals
- iOS Compatibility
- iOS 16.0+
- Xcode Version
- Xcode 15+
- Platform
- iOS
- License Tier
- Pro
- Themes & Contexts
- dashboard
- Keywords
- alert modifier
C
Compot for iOS
Native SwiftUI playground app
Preview all 130+ components interactively on your device and copy code straight to Xcode with 1 tap.
Get Compot on the App StoreRelated SwiftUI Components
More components in Alerts & Modals and related app architectures.
ButtonsiOS 16.0+
Bouncy Button Icon
Interactive button featuring fluid spring animations and smooth scale feedback upon tap.
View Component & Code
ButtonsiOS 16.0+
Button with Repeat Behavior
Stepper-like button with continuous press-and-hold repeating action for counters and volume adjustment.
View Component & Code
ButtonsiOS 16.0+
Button for Copying
Smart copy button that temporarily switches icon and text to "Copied!" with tactile haptic feedback.
View Component & Code