Action sheets in SwiftUI provide a way to present a selection of options or actions to the user in a visually appealing and interactive manner. They are particularly useful when you want to offer choices or gather input in response to a specific user action. With SwiftUI’s confirmationDialog() modifier in iOS 15 and later, creating action sheets is straightforward, allowing you to provide a title, control their visibility using a binding, and define the available options as buttons. For iOS 14 and earlier, the actionSheet() modifier offers similar functionality.
I am going to use a Shopping app as an example that I started developing for other tutorials about SwiftUI presentations like sheets and popovers.
Download the project files here: https://github.com/gahntpo/Sho…
In the following, I am going to show an action sheet when the user taps on the toolbar person button.
Understanding SwiftUI Action Sheets
An Action Sheet in SwiftUI is a specific kind of alert that presents a set of options to the user. It’s generally used when you want to offer multiple choices that dictate the app’s next action. In SwiftUI, you can present an Action Sheet by attaching the .actionSheet(isPresented:content:)
modifier to a view. The isPresented
parameter is a binding to a Boolean that determines whether the sheet is shown. The content
parameter is a closure returning the Action Sheet to display.
For the above example, I am creating a separate view `UserProfileButtonView` that I add to the toolbar:
struct ContentView: View {
@StateObject var fetcher = AutomaticProductFetcher()
var body: some View {
NavigationStack {
ProductListView(products: fetcher.products,
state: fetcher.state)
.toolbar {
ToolbarItemGroup(placement: .navigation) {
UserProfileButtonView()
}
ToolbarItem {
Button {
showCategorySelector.toggle()
} label: {
Label("Choose Category", systemImage: "line.3.horizontal.decrease.circle")
}
}
}
.navigationDestination(for: Product.self, destination: { product in
ProductDetailView(product: product)
})
}
.sheet(isPresented: $showCategorySelector) {
...
}
}
}
For the `UserProfileButtonView`, I am adding a button which toggles the `showActionSheet` state property. This state is used to show/ hide the action sheet. You have to define an ActionSheet which defines what is displayed inside the action sheet.
struct UserProfileButtonView: View {
@State private var showActionSheet = false
var body: some View {
Button {
showActionSheet.toggle()
} label: {
Image(systemName: "person.fill")
}
.actionSheet(isPresented: $showActionSheet) {
userSheet()
}
}
func userSheet() -> ActionSheet {
let picturButton = ActionSheet.Button.default(Text("Change Profile Picture")) { }
let profileButton = ActionSheet.Button.default(Text("Edit Profile Information")) { }
let logOutButton = ActionSheet.Button.destructive(Text("Log Out")) { }
let cancelButton = ActionSheet.Button.cancel(Text("Cancel")) { }
let buttons: [ActionSheet.Button] = [picturButton, profileButton, logOutButton, cancelButton]
return ActionSheet(title: Text("Change Your Profile"),
message: Text(""),
buttons: buttons)
}
}
In this example I am showing 4 buttons:
- `Change Profile`, and `Edit Profile Information` are of type default and use the apps accent color.
- The `Log Out` button is a destructive actionsheet button and has a red foreground color.
- The `Cancel` button is a cancel actionsheet button and displayed at the very bottom of the action list.
How to dismiss an action sheet?
In order to dismiss an action sheet, you can either tap on the area outside the action sheet or on one of the button options. You don’t have to call dismiss() inside the buttons, this is automatically done for you.
SwiftUI ConfirmationDialog for iOS 15 and macOS 13
ActionSheet was soft deprecated with the introduction iOS 15 and macOS 12. You can still use it, but I am going to show you the newer and easier-to-use alternative which is confirmationDialog.
struct UserProfileButtonView: View {
@State private var showDialog = false
var body: some View {
Button {
showDialog.toggle()
} label: {
Image(systemName: "person.fill")
}
.confirmationDialog("Change Your Profile",
isPresented: $showDialog) {
Button("Change Profile Picture") { }
Button("Edit Profile Information") { }
Button("Log Out", role: .destructive) { }
Button("Cancel", role: .cancel) { }
}
}
}
You can simply work with SwiftUI buttons, instead of the special type `ActionSheet.Button`. I used different button roles to achieve the same foreground color styling. You can learn more about button styling in this post SwiftUI Button Stylings.
Customizing the Appearance of Confirmation Dialog in SwiftUI
You can further change the appearance of the confirmation dialog. For example, you can add a message text that is shown below the title. Additionally, you can now use the titleVisibility parameter to hide the title.
import SwiftUI
struct ConfirmationDialogExampleView: View {
@State var show = false
var body: some View {
Button("Open Confirmation Dialog") {
show = true
}
.confirmationDialog(String("Are you sure?"),
isPresented: $show,
titleVisibility: .hidden) {
Button("Yes") { }
Button("No", role: .cancel) { }
} message: {
Text("This action cannot be undone. Would you like to proceed?")
}
}
}
Conclusion
That’s it! You’ve now taken a comprehensive tour through SwiftUI Action Sheets and Confirmation Dialogs, their structure, and their use in your applications. Remember, these elements are vital tools to create an interactive, user-friendly interface. With just a few lines of code, you can greatly enhance your app’s user experience. So don’t hesitate to dive in, experiment, and see how these features can bring your apps to life. Keep exploring, keep learning, and most importantly, keep coding!
Further Reading:
- Instead of action sheets, you can also show alerts, which are more appropriate for errors and when something goes wrong. See how they work in this post about SwiftUI Alerts
- learn about sheet in this blog post SwiftUI Sheet: Modal, Bottom, and full screen presentation in iOS
- Working with popovers in SwiftUI