Created
August 12, 2024 03:26
-
-
Save luthviar/80cdff75529f5359d48eb33b5b894936 to your computer and use it in GitHub Desktop.
Create a Bottom Sheet SwiftUI View and present from UIKit UIViewController
This file contains hidden or 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
| class ViewController: UIViewController, BottomSheetPeopleDetectedInputDelegate { | |
| private var overlayView: UIView? | |
| override func viewDidLoad() { | |
| super.viewDidLoad() | |
| view.backgroundColor = .white | |
| let showButton = UIButton(type: .system) | |
| showButton.setTitle("Show Bottom Sheet", for: .normal) | |
| showButton.addTarget(self, action: #selector(showBottomSheet), for: .touchUpInside) | |
| showButton.translatesAutoresizingMaskIntoConstraints = false | |
| view.addSubview(showButton) | |
| NSLayoutConstraint.activate([ | |
| showButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), | |
| showButton.centerYAnchor.constraint(equalTo: view.centerYAnchor) | |
| ]) | |
| } | |
| @objc func showBottomSheet() { | |
| let bottomSheetView = BottomSheetPeopleDetectedInputView(delegate: self) | |
| let hostingController = UIHostingController(rootView: bottomSheetView) | |
| let targetSize = hostingController.view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) | |
| hostingController.preferredContentSize = targetSize | |
| // Set the corner radius for the hosting controller's view | |
| hostingController.view.layer.cornerRadius = 16 | |
| hostingController.view.layer.masksToBounds = true | |
| if let sheet = hostingController.sheetPresentationController { | |
| sheet.detents = [.custom(resolver: { _ in (targetSize.height + 80) })] | |
| //sheet.detents = [.medium()] | |
| sheet.prefersGrabberVisible = true | |
| } | |
| present(hostingController, animated: true, completion: nil) | |
| // Add the gray overlay | |
| // let overlay = UIView(frame: self.view.bounds) | |
| // overlay.backgroundColor = UIColor.black.withAlphaComponent(0.5) | |
| // view.addSubview(overlay) | |
| // self.overlayView = overlay | |
| // | |
| // let bottomSheetView = BottomSheetPeopleDetectedInputView(delegate: self) | |
| // let hostingController = UIHostingController(rootView: bottomSheetView) | |
| // | |
| // // Set the corner radius for the hosting controller's view | |
| // hostingController.view.layer.cornerRadius = 16 | |
| // hostingController.view.layer.masksToBounds = true | |
| // | |
| // // Calculate the height of the bottom sheet based on the content | |
| // let targetSize = hostingController.view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) | |
| // | |
| // // Set up modal presentation style | |
| // hostingController.modalPresentationStyle = .overCurrentContext | |
| // hostingController.view.backgroundColor = .clear | |
| // | |
| // // Present the view controller | |
| // present(hostingController, animated: true) { | |
| // // Adjust the frame to appear as a bottom sheet | |
| // hostingController.view.frame = CGRect( | |
| // x: 0, | |
| // y: (self.view.frame.height - targetSize.height) - 80, | |
| // width: self.view.frame.width, | |
| // height: targetSize.height + 80 | |
| // ) | |
| // } | |
| } | |
| func didSubmitText(_ text: String) { | |
| // Handle the text received from the BottomSheetView | |
| print("Received text: \(text)") | |
| } | |
| func dismissSheet() { | |
| dismiss(animated: true) { | |
| // Remove the overlay with animation when dismissing the bottom sheet | |
| UIView.animate(withDuration: 0.3, animations: { | |
| self.overlayView?.alpha = 0.0 | |
| }) { _ in | |
| self.overlayView?.removeFromSuperview() | |
| self.overlayView = nil | |
| } | |
| } | |
| } | |
| } | |
| import SwiftUI | |
| struct BottomSheetPeopleDetectedInputView: View { | |
| @State private var detectedPeopleCount: Int = 1 | |
| weak var delegate: BottomSheetPeopleDetectedInputDelegate? | |
| init(delegate: BottomSheetPeopleDetectedInputDelegate? = nil) { | |
| self.delegate = delegate | |
| } | |
| var body: some View { | |
| VStack(spacing: 20) { | |
| // MARK: A small drag handle at the top if needed | |
| // RoundedRectangle(cornerRadius: 3) | |
| // .frame(width: 40, height: 5) | |
| // .foregroundColor(Color.gray.opacity(0.5)) | |
| // .padding(.top, 8) | |
| Text("Orang Terdeteksi") | |
| .font(.system(size: 16, weight: .bold)) // Body/Large/Bold | |
| .padding(.top, 8) | |
| .padding(.bottom, 32) | |
| VStack(alignment: .leading) { | |
| Text("Atur jumlah orang yang harus ada di area deteksi") | |
| .font(.system(size: 12, weight: .regular)) // Body/Small/Regular | |
| .foregroundColor(.gray) | |
| .multilineTextAlignment(.leading) | |
| .padding(.bottom, 2.5) | |
| HStack { | |
| // Minus button | |
| Button(action: { | |
| if detectedPeopleCount > 0 { | |
| detectedPeopleCount -= 1 | |
| } | |
| }) { | |
| Image(systemName: "minus") | |
| .resizable() | |
| .aspectRatio(contentMode: .fit) | |
| .frame(width: 20, height: 20) | |
| .foregroundColor(.blue) | |
| } | |
| Spacer() | |
| // Displaying the count in the center | |
| TextField("", value: $detectedPeopleCount, formatter: NumberFormatter()) | |
| .font(.title2) | |
| .multilineTextAlignment(.center) | |
| .keyboardType(.numberPad) | |
| .frame(width: 60, height: 70) | |
| Spacer() | |
| // Plus button | |
| Button(action: { | |
| detectedPeopleCount += 1 | |
| }) { | |
| Image(systemName: "plus") | |
| .resizable() | |
| .aspectRatio(contentMode: .fit) | |
| .frame(width: 20, height: 20) | |
| .foregroundColor(.blue) | |
| } | |
| } | |
| .padding(.horizontal, 16) | |
| //.padding(.vertical, 35) | |
| .background(Color.white) // White background | |
| .cornerRadius(8) | |
| .overlay( | |
| RoundedRectangle(cornerRadius: 8) | |
| .stroke(.gray, lineWidth: 1) | |
| ) | |
| } | |
| .padding(.horizontal, 16) | |
| .padding(.bottom, 26) | |
| // Apply button | |
| Button(action: { | |
| // Action for button | |
| delegate?.didSubmitText("\(detectedPeopleCount)") | |
| delegate?.dismissSheet() | |
| }) { | |
| Text("Terapkan") | |
| .font(.system(size: 16, weight: .bold)) | |
| .foregroundColor(.white) | |
| .frame(maxWidth: .infinity) | |
| .padding() | |
| .background(Color.blue) | |
| .cornerRadius(10) | |
| } | |
| .cornerRadius(.infinity) | |
| .padding(.horizontal, 16) | |
| .padding(.bottom, 20) | |
| } | |
| .padding() | |
| .background(Color.white) | |
| .cornerRadius(20) | |
| .frame(maxWidth: .infinity, alignment: .bottom) | |
| } | |
| } | |
| protocol BottomSheetPeopleDetectedInputDelegate: AnyObject { | |
| func didSubmitText(_ text: String) | |
| func dismissSheet() | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
👍🏻