Skip to content

Instantly share code, notes, and snippets.

@donnywals
Last active June 30, 2021 07:17
Show Gist options
  • Save donnywals/3058ed489e35524dacfede1a3cc765ff to your computer and use it in GitHub Desktop.
Save donnywals/3058ed489e35524dacfede1a3cc765ff to your computer and use it in GitHub Desktop.
import UIKit
import SwiftUI
// Hacky workaround, use at your own risk and all that
struct BottomSheetPresenter<Content>: UIViewRepresentable where Content: View{
let label: String
let content: Content
let detents: [UISheetPresentationController.Detent]
init(_ label: String, detents: [UISheetPresentationController.Detent], @ViewBuilder content: () -> Content) {
self.label = label
self.content = content()
self.detents = detents
}
func makeUIView(context: UIViewRepresentableContext<BottomSheetPresenter>) -> UIButton {
let button = UIButton(type: .system)
button.setTitle(label, for: .normal)
button.addAction(UIAction { _ in
let hostingController = UIHostingController(rootView: content)
let viewController = UIBottomSheetWrapper(detents: detents)
viewController.addChild(hostingController)
viewController.view.addSubview(hostingController.view)
hostingController.view.pinToEdgesOf(viewController.view)
hostingController.didMove(toParent: viewController)
button.window?.rootViewController?.present(viewController, animated: true)
}, for: .touchUpInside)
print("make...")
return button
}
func updateUIView(_ uiView: UIButton, context: Context) {
// no updates
}
func makeCoordinator() -> Void {
return ()
}
}
extension BottomSheetPresenter {
class UIBottomSheetWrapper: UIViewController {
let detents: [UISheetPresentationController.Detent]
init(detents: [UISheetPresentationController.Detent]) {
self.detents = detents
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("No Storyboards")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
if let sheetController = self.presentationController as? UISheetPresentationController {
sheetController.detents = detents
}
}
}
}
extension UIView {
func pinToEdgesOf(_ other: UIView) {
translatesAutoresizingMaskIntoConstraints = false
leftAnchor.constraint(equalTo: other.leftAnchor).isActive = true
topAnchor.constraint(equalTo: other.topAnchor).isActive = true
rightAnchor.constraint(equalTo: other.rightAnchor).isActive = true
bottomAnchor.constraint(equalTo: other.bottomAnchor).isActive = true
}
}
// Usage
struct ContentView: View {
var body: some View {
VStack {
BottomSheetPresenter("Tap me for a bottom sheet!!", detents: [.medium(), .large()]) {
VStack {
Text("This is a test")
Rectangle()
.frame(width: 300, height: 300, alignment: .leading)
.foregroundColor(.blue)
}
}
}
}
}
@Snowy1803
Copy link

UIApplication.shared.windows.first?.rootViewController?.present wouldn't work as intended for apps with multiple scenes, button.window?.rootViewController?.present should work better (the button's window will be set at the time the button is tapped)
That's the only "hacky" thing I see here, the rest looks good to me!

@donnywals
Copy link
Author

Thanks! 😁 I'll modify the gist to use your suggestion

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment