Post

Sheet in SwiftUI

Sheet in SwiftUI

There are two ways to present a sheet in SwiftUI.

1. Binding to a Boolean value

In this way, SwiftUI will present a sheet when a binding to a Boolean value that you provide is true.

The official method definition is below:

1
2
3
4
5
6
nonisolated
func sheet<Content>(
    isPresented: Binding<Bool>,
    onDismiss: (() -> Void)? = nil,
    @ViewBuilder content: @escaping () -> Content
) -> some View where Content : View

Let’s use an example to illustrate how to use it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct TimelineListView: View {
    @EnvironmentObject var timelineStore: TimelineStore
    @State private var showingSettings = false

    var body: some View {
        NavigationView {
            List(timelineStore.timelines) { timeline in 
                TimelineRow(timeline: timeline)
            }
            .navigationBarTitle("Timelines")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button(action: { showingSettings = true }) {
                        Image(systemName: "gear")
                    }
                }
            }
            .sheet(isPresented: $showingSettings, onDismiss: didDismiss) {
                SettingsView()
            }
        }
    }

    private func didDismiss() {
        // Handle the dismissing action.
    }
}
Timeline List Settings View

In the above example, SwiftUI will present SettingsView when showingSettings is true.

2. Using data source item

Another way to present a sheet is using the given item as a data source for the sheet’s content.

The method definition is as follows:

1
2
3
4
5
6
nonisolated
func sheet<Item, Content>(
    item: Binding<Item?>,
    onDismiss: (() -> Void)? = nil,
    @ViewBuilder content: @escaping (Item) -> Content
) -> some View where Item : Identifiable, Content : View

A simple example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct TimelineDetailView: View {
    let timeline: Timeline
    @State private var showingEventToEdit: Event? = nil

    var body: some View {
        List(timeline.events) { event in 
            EventRow(event: event)
                .contentShape(Rectangle())
                .onTapGesture {
                    showingEventToEdit = event
                }
        }
        .navigationTitle(timeline.title)
        .sheet(isPresented: $showingEventToEdit) { event in
            EventEditView(timeline: timeline, event: event)
        }
    }
}

SwiftUI will present a EventEditView when showingEventToEdit has value.

Multiple sheets

Sometimes, we may need to present multiple sheets in the same view. How to achieve this in a concise way?

We can create an enum like Sheet to combine all sheet cases together.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
struct TimelineDetailView: View {
    enum Sheet: Identifier {
        case createEvent
        case editEvent(Event)

        var id: String {
            switch self {
            case .createEvent: return "createEvent"
            case .editEvent(let event): return "editEvent-\(event.id)"
            }
        }
    }

    let timeline: Timeline
    @State private var showingSheet: Sheet? = nil

    var body: some View {
        List(timeline.events) { event in 
            EventRow(event: event)
                .contentShape(Rectangle())
                .onTapGesture {
                    showingSheet = .editEvent(event)
                }
        }
        .navigationTitle(timeline.title)
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button {
                    showingSheet = .createEvent
                } label: {
                    Image(systemName: "plus")
                }
            }
        }
        .sheet(item: $showingSheet) { sheet in
            switch sheet {
            case .createEvent: 
                EventCreateView(timeline: timeline)
            case .editEvent(let event): 
                EventEditView(timeline: timeline, event: event)
            }
        }
    }
}
Timeline Detail Event Create View Event Edit View

Using this way, we can simply combine all sheets together without multiple state definitions and sheet modifiers.

References

This post is licensed under CC BY 4.0 by the author.