-
-
Save dankamel/d7d66bea734ff1a2ea2a6f6abfd538ad to your computer and use it in GitHub Desktop.
import SwiftUI | |
struct BottomSheetDesign: View { | |
@State var showSheet: Bool? = nil | |
var body: some View { | |
Button(action: { showSheet = true }) { | |
HStack (spacing: 5) { | |
Image(systemName: "hand.tap.fill") | |
.font(.system(size: 20, weight: .regular, design: .rounded)) | |
Text("me to bring up adjustable sheet") | |
.font(.system(size: 20, weight: .regular, design: .rounded)) | |
} | |
} | |
.halfSheet(showSheet: $showSheet) { | |
ZStack { | |
Color.blue | |
ScrollView(.vertical, showsIndicators: false) { | |
VStack { | |
RoundedRectangle(cornerRadius: 12, style: .continuous) | |
.frame(width: 50, height: 8) | |
.padding(.top, -20) | |
.foregroundColor(.black.opacity(0.2)) | |
Text("Hello half sheet 🥹") | |
.font(.system(size: 25, weight: .regular, design: .rounded)) | |
.foregroundColor(.white) | |
.padding(.top, 70) | |
.padding(.bottom, 10) | |
Button(action: { showSheet = false }) { | |
HStack(spacing: 5) { | |
Image(systemName: "hand.tap.fill") | |
.font(.system(size: 20, weight: .bold, design: .rounded)) | |
.foregroundColor(.white) | |
Text("me to close sheet") | |
.font(.system(size: 20, weight: .bold, design: .rounded)) | |
.foregroundColor(.white) | |
} | |
} | |
} | |
.padding(.top, 30) | |
} | |
} | |
.edgesIgnoringSafeArea(.bottom) | |
} onDismiss: { | |
print("sheet dismissed") | |
} | |
} | |
} | |
struct BottomSheetDesign_Previews: PreviewProvider { | |
static var previews: some View { | |
BottomSheetDesign() | |
} | |
} |
import SwiftUI | |
// Custom Half Sheet Modifier.... | |
extension View { | |
//binding show bariable... | |
func halfSheet<Content: View>( | |
showSheet: Binding<Bool?>, | |
@ViewBuilder content: @escaping () -> Content, | |
onDismiss: @escaping () -> Void | |
) -> some View { | |
return self | |
.background( | |
HalfSheetHelper(sheetView: content(), showSheet: showSheet, onDismiss: onDismiss) | |
) | |
} | |
} | |
// UIKit integration | |
struct HalfSheetHelper<Content: View>: UIViewControllerRepresentable { | |
var sheetView: Content | |
let controller: UIViewController = UIViewController() | |
@Binding var showSheet: Bool? | |
var onDismiss: () -> Void | |
func makeCoordinator() -> Coordinator { | |
Coordinator(parent: self) | |
} | |
func makeUIViewController(context: Context) -> UIViewController { | |
controller.view.backgroundColor = .clear | |
return controller | |
} | |
func updateUIViewController(_ uiViewController: UIViewController, context: Context) { | |
if let showSheet: Bool = showSheet { | |
if showSheet { | |
let sheetController = CustomHostingController(rootView: sheetView) | |
sheetController.presentationController?.delegate = context.coordinator | |
uiViewController.present(sheetController, animated: true) | |
} | |
} | |
} | |
//on dismiss... | |
final class Coordinator: NSObject, UISheetPresentationControllerDelegate { | |
var parent: HalfSheetHelper | |
init(parent: HalfSheetHelper) { | |
self.parent = parent | |
} | |
func presentationControllerWillDismiss(_ presentationController: UIPresentationController) { | |
parent.showSheet = false | |
} | |
} | |
} | |
// Custom UIHostingController for halfSheet... | |
final class CustomHostingController<Content: View>: UIHostingController<Content> { | |
override func viewDidLoad() { | |
view.backgroundColor = .clear | |
if let presentationController = presentationController as? UISheetPresentationController { | |
presentationController.detents = [ | |
.medium(), | |
.large() | |
] | |
//MARK: - sheet grabber visbility | |
presentationController.prefersGrabberVisible = false // i wanted to design my own grabber hehehe | |
// this allows you to scroll even during medium detent | |
presentationController.prefersScrollingExpandsWhenScrolledToEdge = false | |
//MARK: - sheet corner radius | |
presentationController.preferredCornerRadius = 30 | |
// for more sheet customisation check out this great article https://sarunw.com/posts/bottom-sheet-in-ios-15-with-uisheetpresentationcontroller/#scrolling | |
} | |
} | |
} | |
public struct LazyView<Content: View>: View { | |
private let build: () -> Content | |
public init(_ build: @autoclosure @escaping () -> Content) { | |
self.build = build | |
} | |
public var body: Content { | |
build() | |
} | |
} |
Why on tap sheet not being dismissed?
For DISMISS use the suggestion above and add
else {
uiViewController.dismiss(animated: true)
}
And of course in your own 'sheet' (View) you have to set showSheet = false to trigger the update
Worked for me
aww , good
Excellent, but it doesn't work with EnvironmentObjects.
when I dismiss the sheet using @Environment(\.dismiss) var dismiss
from the sheet content, it presents the sheet again immediately .
is there a way to fix this?
Nice one @dankamel!
I got the onDismiss
to work by doing this:
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
if let showSheet: Bool = showSheet {
if showSheet {
let sheetController = CustomHostingController(rootView: sheetView)
sheetController.presentationController?.delegate = context.coordinator
uiViewController.present(sheetController, animated: true)
} else {
uiViewController.dismiss(animated: true)
onDismiss()
self.showSheet = nil
}
}
}
Thanks to hints from @Frodothedwarf & @bruijnesteijn
Based on issues that I had, simply closing uiViewController, dismissed whole screen because nature of my screen was pretty complex and nested, what worked for me was, dismissing presented controller
if showSheet {
uiViewController.present(sheetController, animated: true)
} else {
uiViewController.presentedViewController?.dismiss(animated: true)
onDismiss()
DispatchQueue.main.async { //added because of issue on changing showSheet on none supported thread
self.showSheet = nil
}
}
But its not properly working in ipad.It does not completely occupy the whole width.So If you have any solution for this,kindly let me know.
Going to try this out! Btw, the
onDismiss
block gets passed around a lot, but doesn't seem to actually get called in this jist.