Post

fullScreenCover in SwiftUI

fullScreenCover in SwiftUI

Similar to Sheet in SwiftUI, there are two ways to present a full screen modal view in SwiftUI as well.

To learn more about Sheet, take a look at my “Sheet in SwiftUI” post.

1. Binding to a Boolean value

In this way, SwiftUI will present a modal view that covers as much of the screen as possible 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 fullScreenCover<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")
                    }
                }
            }
            .fullScreenCover(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 full screen modal view is using the given item as a data source for the modal view’s content.

The method definition is as follows:

1
2
3
4
5
6
nonisolated
func fullScreenCover<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)
        .fullScreenCover(isPresented: $showingEventToEdit) { event in
            EventEditView(timeline: timeline, event: event)
        }
    }
}

SwiftUI will present a EventEditView when showingEventToEdit has value.

Multiple modal views

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

We can create an enum like ModalView to combine all modal views 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 ModalView: 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 showingModalView: ModalView? = nil

    var body: some View {
        List(timeline.events) { event in 
            EventRow(event: event)
                .contentShape(Rectangle())
                .onTapGesture {
                    showingModalView = .editEvent(event)
                }
        }
        .navigationTitle(timeline.title)
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button {
                    showingModalView = .createEvent
                } label: {
                    Image(systemName: "plus")
                }
            }
        }
        .fullScreenCover(item: $showingModalView) { modalView in
            switch modalView {
            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 full screen modal views together without multiple state definitions and fullScreenCover modifiers.

References

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