Modal
A fluid modal dialog component with spring animations, customizable positions, overlay styles, and drag-to-dismiss support.
Import
import PrettyUI
Basic Usage
@State private var showModal = false
Button("Show Modal") {
showModal = true
}
.pModal(isPresented: $showModal) {
PModalContent("Welcome!")
.description("Thanks for using our app.")
.icon("hand.wave.fill")
}
Positions
Show modals at different screen positions:
// Center (default)
.pModal(isPresented: $show, position: .center) { ... }
// Top
.pModal(isPresented: $show, position: .top, topPadding: 64) { ... }
// Bottom
.pModal(isPresented: $show, position: .bottom, bottomPadding: 32) { ... }
Overlay Styles
Customize the backdrop:
// Dimmed (default)
.pModal(isPresented: $show, overlay: .dimmed) { ... }
// Blurred
.pModal(isPresented: $show, overlay: .blurred(radius: 10)) { ... }
// Dimmed + Blurred
.pModal(isPresented: $show, overlay: .dimmedBlur(opacity: 0.3, radius: 10)) { ... }
// Custom color
.pModal(isPresented: $show, overlay: .color(.purple, opacity: 0.3)) { ... }
// No overlay
.pModal(isPresented: $show, overlay: .none) { ... }
Variants
Set the modal variant to change icon colors:
// Standard (default)
PModalContent("Settings")
.variant(.standard)
// Destructive (red)
PModalContent("Delete Account")
.variant(.destructive)
// Success (green)
PModalContent("Success!")
.variant(.success)
// Warning (orange)
PModalContent("Warning")
.variant(.warning)
With Actions
Add action buttons to your modal:
PModalContent("Confirm Action")
.description("Are you sure you want to proceed?")
.icon("exclamationmark.circle")
.actions {
HStack(spacing: 12) {
PButton("Cancel") {
showModal = false
}
.variant(.secondary)
.fullWidth()
PButton("Confirm") {
performAction()
showModal = false
}
.variant(.primary)
.fullWidth()
}
}
Alert-Style Modal
Use the convenience modifier for common alert patterns:
.pModalAlert(
isPresented: $showAlert,
title: "Delete Account",
message: "This action cannot be undone.",
icon: "trash.circle",
variant: .destructive,
primaryButton: .destructive("Delete") {
deleteAccount()
},
secondaryButton: .cancel("Keep Account") {
showAlert = false
}
)
Alert Button Types
// Primary action
.primary("Confirm") { ... }
// Secondary action
.secondary("Maybe Later") { ... }
// Cancel action
.cancel("Cancel") { ... }
// or with custom title
.cancel("Keep Account") { ... }
// Destructive action
.destructive("Delete") { ... }
Dismiss Options
Background Tap
// Disable background tap dismiss
.pModal(
isPresented: $show,
dismissOnBackgroundTap: false
) { ... }
Close Button
PModalContent("Title")
.showCloseButton(true) // Show (default)
.showCloseButton(false) // Hide
Drag to Dismiss
Modals support drag gestures:
- Center/Bottom: Drag down to dismiss
- Top: Drag up to dismiss
Custom Content
Use completely custom content:
.pModal(isPresented: $showRating, position: .bottom) {
VStack(spacing: 20) {
Image(systemName: "star.fill")
.font(.system(size: 48))
.foregroundColor(.yellow)
Text("Rate Our App")
.font(.title2)
.fontWeight(.bold)
HStack(spacing: 8) {
ForEach(1...5, id: \.self) { _ in
Image(systemName: "star.fill")
.foregroundColor(.yellow)
}
}
PButton("Submit") {
showRating = false
}
.variant(.primary)
.fullWidth()
}
.padding(24)
.frame(maxWidth: 340)
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 28))
}
Real-World Examples
Delete Confirmation
.pModalAlert(
isPresented: $showDeleteConfirm,
title: "Remove Contact",
message: "This contact will be removed from your address book.",
icon: "exclamationmark.circle",
variant: .destructive,
primaryButton: .destructive("Remove") {
deleteContact()
showDeleteConfirm = false
},
secondaryButton: .cancel {
showDeleteConfirm = false
}
)
Success Confirmation
.pModal(isPresented: $showSuccess, position: .center) {
PModalContent("Transaction Complete")
.description("Your payment of $50.00 was successful.")
.icon("checkmark.circle")
.variant(.success)
.showCloseButton(false)
.actions {
PButton("Done") {
showSuccess = false
}
.variant(.primary)
.fullWidth()
}
}
Settings Sheet
.pModal(isPresented: $showSettings, position: .bottom, bottomPadding: 0) {
PModalContent("Notification Settings")
.description("Choose how you'd like to be notified.")
.icon("bell.badge")
.actions {
VStack(spacing: 12) {
PButton("Enable All") { ... }
.variant(.primary)
.fullWidth()
PButton("Customize") { ... }
.variant(.outline)
.fullWidth()
PButton("Not Now") { showSettings = false }
.variant(.ghost)
.fullWidth()
}
}
}
API Reference
pModal Modifier
.pModal(
isPresented: Binding<Bool>,
position: PModalPosition = .center,
overlay: PModalOverlayStyle = .dimmed,
dismissOnBackgroundTap: Bool = true,
topPadding: CGFloat? = nil,
bottomPadding: CGFloat? = nil,
horizontalPadding: CGFloat = 16,
@ViewBuilder content: () -> Content
)
pModalAlert Modifier
.pModalAlert(
isPresented: Binding<Bool>,
position: PModalPosition = .center,
overlay: PModalOverlayStyle = .dimmed,
title: String,
message: String? = nil,
icon: String? = nil,
variant: PModalVariant = .standard,
primaryButton: PModalButton,
secondaryButton: PModalButton? = nil
)
PModalContent Modifiers
| Modifier | Type | Description |
|---|---|---|
.description(_:) | String? | Description text |
.icon(_:) | String | SF Symbol name |
.iconColor(_:) | Color | Custom icon color |
.variant(_:) | PModalVariant | Visual variant |
.showCloseButton(_:) | Bool | Show/hide close button |
.radius(_:) | RadiusSize | Corner radius |
.maxWidth(_:) | CGFloat | Maximum width |
.onClose(_:) | () -> Void | Close callback |
.actions(_:) | ViewBuilder | Action buttons |
Enums
PModalPosition
.center— Centered (default).top— Top of screen.bottom— Bottom of screen
PModalVariant
.standard— Neutral.destructive— Red accent.success— Green accent.warning— Orange accent
PModalOverlayStyle
.dimmed— Black overlay.blurred(radius:)— Blur effect.dimmedBlur(opacity:radius:)— Combined.color(_:opacity:)— Custom color.none— Transparent