Last active
May 17, 2024 09:24
-
-
Save dankamel/d7d66bea734ff1a2ea2a6f6abfd538ad to your computer and use it in GitHub Desktop.
Native iOS 15 Adjustable Bottom Sheet In SwiftUI (half bottom sheet)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
} | |
} |
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?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Why on tap sheet not being dismissed?