'How to present a modal view controller like with UIKit's UIViewControllerTransitioningDelegate?

I am switching from UIKit to SwiftUI and still struggle to understand some parts of it, especially the transitions between views.

I have a situation where is displayed on screen a view controller with a list, say ElementsListViewController(), and when tapping an element I want to display a modal with a custom UI/animation: the opaque overlay would appear with an animation of the alpha value while the white modal "sheet" would appear from bottom to top.

Here is what it looks like:

enter image description here

With UIKit, I would use UIViewControllerAnimatedTransitioning to do that.

Now I would like to do the same with SwiftUI, but I am lost with what to do exactly here.

I have this so far:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ElementsList()
        }
    }
}

struct ElementsList: View {
    @State var elements: [String] = (0..<100).map { "Element #\($0)" }

    var body: some View {
        List(elements, id: \.self) {
            Text($0)
                .onTapGesture {
                    // what to do here to display ModalView as I want to?
                }
        }
        .listStyle(PlainListStyle())
    }
}

struct ModalView: View {
    var body: some View {
        ZStack {
            Color.black.opacity(0.8)
            Color.white
                .cornerRadius(20.0)
                .padding(.top, 150)
        }
        .ignoresSafeArea()
    }
}

What is my best option here? Thank you for your help



Solution 1:[1]

Well there is no built-in such flexibility say with standard .sheet, but it can be implemented custom very fast.

Here is simple demo (Xcode 13.3 / iOS 15.4)

demo

Main part:

struct ElementsList: View {
// ...
        ModalView(isPresented: $isModal) {
            List(elements, id: \.self) {


struct ModalView<V: View>: View {
    @Binding var isPresented: Bool
// ...
        ZStack {
            content()
            ZStack {
                VStack {
                    if isPresented {
                        Color.black.opacity(0.8)
                            .transition(.opacity)
                    }
                }.animation(.easeInOut(duration: 0.25), value: isPresented)


Complete test code in project is here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Asperi