Skip to content

Latest commit

 

History

History
180 lines (139 loc) · 5.79 KB

bottom-sheet.md

File metadata and controls

180 lines (139 loc) · 5.79 KB

Presenting a bottom sheet in UIKit using UISheetPresentationController

Available in iOS 15+

Files

  • BottomSheetViewController: the main view controller responsible for presenting the sheet
  • BottomSheetView: Presented bottom sheet written in SwiftUI
  • BottomSheetDetailController: hosts the SwiftUI View

Screen Shot 2023-01-27 at 4 23 21 PM

Screen Shot 2023-01-27 at 4 20 43 PM

Screen Shot 2023-01-27 at 4 20 57 PM

Simulator Screen Shot - iPad Pro (11-inch) (4th generation) - 2023-01-27 at 16 16 14

Simulator Screen Shot - iPad Pro (11-inch) (4th generation) - 2023-01-27 at 16 17 43

try? it out

BottomSheetViewController.swift

import UIKit

final class BottomSheetViewController: UIViewController {
    private lazy var presentationButton: UIButton = {
        let button = UIButton()
        button.addTarget(self, action: #selector(presentSheet), for: .touchUpInside)
        button.setTitle("Preset Bottom Sheet using UIKit", for: .normal)
        button.setTitleColor(.white, for: .normal)
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .orange
        view.addSubview(presentationButton)
    }

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        presentationButton.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            presentationButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            presentationButton.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }

    @objc func presentSheet() {
        // 1
        // Create an instance of the view controller that will be presented
        // in this case it host a SwiftUI View
        let detailVC = BottomSheetDetailController(rootView: BottomSheetView())

        // 2
        // Support `popover` for iPad
        detailVC.modalPresentationStyle = .popover

        // 3
        // Get the `popoverPresentationController` from the view controller
        guard let popover = detailVC.popoverPresentationController else {
            return
        }

        // 4
        // Configure the `popover` instance
        popover.permittedArrowDirections = [.up]
        popover.sourceView = presentationButton

        // 5
        // `adaptiveSheetPresentationController` is available as of iOS 15
        let sheet = popover.adaptiveSheetPresentationController

        // 6
        // Configure the `UISheetPresentationController` here
        sheet.prefersEdgeAttachedInCompactHeight = true
        sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
        sheet.preferredCornerRadius = 24.0
        sheet.prefersGrabberVisible = true

        // 7
        // [UISheetPresentationController.Detents], e.g .medium(), .large()
        // if `.detents` is not set a full sheet will be presented
        sheet.detents = [.medium(), .large()]

        // 8
        // Present the sheet
        present(detailVC, animated: true)
    }
}

BottomSheetDetailController.swift

import SwiftUI

final class BottomSheetDetailController: UIHostingController<BottomSheetView> {}

BottomSheetView.swift

import SwiftUI

struct BottomSheetView: View {
    @Environment(\.dismiss) var dismiss
    @Environment(\.verticalSizeClass) var verticalSizeClass

    private let names = [
        "Combine", "SwiftUI", "Testing", "UIKit",
        "Swift Package", "Swift Concurrency", "Accessibility", "Xcode"
    ]

    var body: some View {
        VStack {
            ScrollView(showsIndicators: false) {
                VStack(alignment: .center, spacing: 20) {
                    Text("SwiftUI View hosted by UIKit **`UISheetPresentationController`**")
                        .font(verticalSizeClass == .regular ? .headline : .title3)
                        .multilineTextAlignment(.center)
                    Image("swiftui-logo")
                        .resizable()
                        .frame(width: 140, height: 140)
                        .aspectRatio(contentMode: .fit)
                    ForEach(names, id: \.self) { name in
                        Text(name)
                    }
                }
                .padding(.top, 40)
            }
            Divider()
                .frame(height: 0.5)
                .background(.gray)
                .shadow(color: .gray, radius: 12)
                .padding(.bottom, 2)
            Button(action: dismissView) {
                Text("Dismiss")
                    .font(.title3)
                    .foregroundColor(.white)
                    .frame(maxWidth: .infinity)
                    .padding(10)
            }
            .background(.blue)
            .cornerRadius(10)
            .padding(.horizontal, 20)
            .padding(.bottom, 10)
        }
    }

    private func dismissView() { dismiss() }
}

struct BottomSheetView_Previews: PreviewProvider {
    static var previews: some View {
        BottomSheetView()
    }
}

Resources