Available in iOS 15+
BottomSheetViewController
: the main view controller responsible for presenting the sheetBottomSheetView
: Presented bottom sheet written in SwiftUIBottomSheetDetailController
: hosts the SwiftUI View
try? it out
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)
}
}
import SwiftUI
final class BottomSheetDetailController: UIHostingController<BottomSheetView> {}
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()
}
}