A sliding sheet from the bottom of the screen that uses UIKit to present true modal view

SwiftUIModal

A sliding sheet from the bottom of the screen that uses UIKit to present true modal view, but the whole animation and UI is driven by the SwiftUI. It uses and provides NonAnimatedUIKitModal: UIViewControllerRepresentable view that handles the modal UIKit wrapped modal presentation.


Why

There is many different SwiftUI modals available on open source. I created this one as many of the implementations do not present the bottom sheet truly as modal views, which leads to the problems like having to show the modal on the NavigationView {}.bottomSheet... to avoid problems of navigation bar being on top of the bottom sheet. Another issue that I had is that the presentation, and animation in some implementations is driven by the UIKit. I took different approach where UIKit is only used to present the SwiftUI view, but it does not drive any animation and UI content.

Installation

Swift Package Manager The preferred way of installing SwiftUIModal is via the Swift Package Manager.

In Xcode, open your project and navigate to File → Add Packages Paste the repository URL (https://github.com/nonameplum/SwiftUIModal) and click Next. For Rules, select Up to Next Major Version. Click Add Package.

Usage

Presentation

The bottom sheet usage is very similar to SwiftUI sheet or fullScreenCover by using bottomSheet method:

struct Modal: View {
    @State private var isPresenting: Bool = false

    var body: some View {
        NavigationView {
            ZStack {
                Button("Show bottom sheet") {
                    isPresenting.toggle()
                }
                .bottomSheet(isPresented: $isPresenting) {
                    ForEach(1 ..< 10) { index in
                        Text("Row \(index)")
                    }
                    .padding([.leading, .trailing])
                }
            }
            .navigationTitle("Navigation title")
        }
    }
}

In addition to that, the bottom sheet can be also used standalone, e.g. by using if statement and conditionally show the view:

struct NonModal: View {
    @State private var isPresenting: Bool = false

    var body: some View {
        NavigationView {
            ZStack {
                Button("Show bottom sheet") {
                    isPresenting.toggle()
                }
                if isPresenting {
                    BottomSheetView(onDismiss: { isPresenting = false }) {
                        ForEach(1 ..< 10) { index in
                            Text("Row \(index)")
                        }
                        .padding([.leading, .trailing])
                    }
                }
            }
            .navigationTitle("Navigation title")
        }
    }
}

The difference between the two, is that in the first case the bottom sheet will be presented modaly on top of any other view including navigation bar.

Configuration

The bottom sheet can be configured using bottomSheetConfiguration view modifier.

.bottomSheet(isPresented: $isVisible) {
    Text("Bottom Sheet Content")
}
.bottomSheetConfiguration(
    .init(
        dismissRatio: 0.5,
        maxOverDrag: 0,
        background: { Color.yellow },
        dim: { Color.blue.opacity(0.3) },
        indicator: {
            RoundedRectangle(cornerRadius: 10)
                .fill(.orange)
                .frame(width: 100, height: 10).padding()
        }
    )
)

Documentation

The documentation for the latest release is available here:

License

This library is released under the MIT license. See LICENSE for details.

GitHub

View Github