mirror of
https://github.com/logseq/logseq.git
synced 2026-04-24 14:14:55 +00:00
feat(mobile): add native inline calendar for ios
This commit is contained in:
@@ -5,80 +5,131 @@
|
||||
// Created by Charlie on 2025/5/29.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Capacitor
|
||||
import Foundation
|
||||
|
||||
class DatePickerDialogViewController: UIViewController {
|
||||
private let datePicker = UIDatePicker()
|
||||
private let dialogView = UIView()
|
||||
|
||||
var onDateSelected: ((Date?) -> Void)?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupImplView()
|
||||
}
|
||||
|
||||
@objc private func confirmDate() {
|
||||
onDateSelected?(datePicker.date)
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func dismissDialog() {
|
||||
onDateSelected?(nil)
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func setupImplView() {
|
||||
datePicker.datePickerMode = .date
|
||||
datePicker.preferredDatePickerStyle = .inline
|
||||
datePicker.addTarget(
|
||||
self, action: #selector(confirmDate), for: .valueChanged)
|
||||
|
||||
// Create hosting view controller
|
||||
let view = self.view!
|
||||
|
||||
view.backgroundColor = .black.withAlphaComponent(0.4)
|
||||
|
||||
dialogView.backgroundColor = .white
|
||||
dialogView.layer.cornerRadius = 10
|
||||
dialogView.clipsToBounds = true
|
||||
view.addSubview(dialogView)
|
||||
|
||||
dialogView.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
dialogView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
|
||||
dialogView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
|
||||
])
|
||||
|
||||
// Add sub views
|
||||
dialogView.addSubview(datePicker)
|
||||
|
||||
// Add date selector and toolbar to the view
|
||||
datePicker.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
datePicker.topAnchor.constraint(equalTo: dialogView.topAnchor),
|
||||
datePicker.bottomAnchor.constraint(equalTo: dialogView.bottomAnchor, constant: -8),
|
||||
datePicker.leadingAnchor.constraint(equalTo: dialogView.leadingAnchor, constant: 16),
|
||||
datePicker.trailingAnchor.constraint(equalTo: dialogView.trailingAnchor, constant: -16),
|
||||
])
|
||||
|
||||
datePicker.setContentHuggingPriority(.required, for: .horizontal)
|
||||
datePicker.setContentHuggingPriority(.required, for: .vertical)
|
||||
datePicker.setContentCompressionResistancePriority(
|
||||
.required, for: .horizontal)
|
||||
datePicker.setContentCompressionResistancePriority(
|
||||
.required, for: .vertical)
|
||||
}
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
if let touch = touches.first {
|
||||
let location = touch.location(in: view)
|
||||
if !dialogView.frame.contains(location) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
super.touchesBegan(touches, with: event)
|
||||
}
|
||||
}
|
||||
|
||||
@objc(UILocalPlugin)
|
||||
public class UILocalPlugin: CAPPlugin, CAPBridgedPlugin {
|
||||
|
||||
|
||||
public let identifier = "UILocalPlugin"
|
||||
public let jsName = "UILocal"
|
||||
|
||||
|
||||
private var call: CAPPluginCall?
|
||||
private var selectedDate: Date?
|
||||
|
||||
private var datepickerViewController: UIViewController?
|
||||
private var datepickerDialogView: UIView?
|
||||
|
||||
public let pluginMethods: [CAPPluginMethod] = [
|
||||
CAPPluginMethod(name: "showDatePicker", returnType: CAPPluginReturnPromise)
|
||||
]
|
||||
|
||||
|
||||
@objc func showDatePicker(_ call: CAPPluginCall) {
|
||||
self.call = call
|
||||
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
let datePicker = UIDatePicker()
|
||||
datePicker.datePickerMode = .date
|
||||
datePicker.preferredDatePickerStyle = .wheels
|
||||
datePicker.addTarget(self, action: #selector(self?.dateChanged(_:)), for: .valueChanged)
|
||||
|
||||
// Create hosting view controller
|
||||
let viewController = UIViewController()
|
||||
viewController.view.backgroundColor = .white
|
||||
|
||||
// Create the top toolbar (including cancel and confirm buttons)
|
||||
let toolbar = UIToolbar()
|
||||
toolbar.translatesAutoresizingMaskIntoConstraints = false
|
||||
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(self?.cancelTapped))
|
||||
let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self?.confirmTapped))
|
||||
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
||||
toolbar.setItems([cancelButton, flexibleSpace, doneButton], animated: false)
|
||||
toolbar.sizeToFit()
|
||||
|
||||
// Add date selector and toolbar to the view
|
||||
datePicker.translatesAutoresizingMaskIntoConstraints = false
|
||||
viewController.view.addSubview(datePicker)
|
||||
viewController.view.addSubview(toolbar)
|
||||
|
||||
// Set layout constraints
|
||||
NSLayoutConstraint.activate([
|
||||
toolbar.topAnchor.constraint(equalTo: viewController.view.safeAreaLayoutGuide.topAnchor),
|
||||
toolbar.leadingAnchor.constraint(equalTo: viewController.view.leadingAnchor),
|
||||
toolbar.trailingAnchor.constraint(equalTo: viewController.view.trailingAnchor),
|
||||
|
||||
datePicker.topAnchor.constraint(equalTo: toolbar.bottomAnchor, constant: 10),
|
||||
datePicker.leadingAnchor.constraint(equalTo: viewController.view.leadingAnchor),
|
||||
datePicker.trailingAnchor.constraint(equalTo: viewController.view.trailingAnchor),
|
||||
datePicker.bottomAnchor.constraint(lessThanOrEqualTo: viewController.view.safeAreaLayoutGuide.bottomAnchor, constant: -10)
|
||||
])
|
||||
|
||||
// Set bottom sheet style (compatible with iOS 13+)
|
||||
viewController.modalPresentationStyle = .formSheet
|
||||
viewController.isModalInPresentation = true
|
||||
|
||||
let viewController = DatePickerDialogViewController()
|
||||
|
||||
// Set view controller presentation
|
||||
viewController.modalPresentationStyle = .overFullScreen
|
||||
viewController.modalTransitionStyle = .crossDissolve
|
||||
viewController.isModalInPresentation = true // 禁止非按钮交互关闭
|
||||
|
||||
viewController.onDateSelected = self?.dateChanged
|
||||
|
||||
// Present View Controller
|
||||
guard let presentingViewController = self?.bridge?.viewController else {
|
||||
call.reject("Unable to present date picker")
|
||||
return
|
||||
}
|
||||
presentingViewController.present(viewController, animated: true, completion: nil)
|
||||
|
||||
presentingViewController.present(
|
||||
viewController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func dateChanged(_ datePicker: UIDatePicker) {
|
||||
self.selectedDate = datePicker.date
|
||||
self.call?.keepAlive = true // Keep calling until confirmed or canceled
|
||||
|
||||
private func dateChanged(_ date: Date?) {
|
||||
self.selectedDate = date
|
||||
self.call?.keepAlive = true // Keep calling until confirmed or canceled
|
||||
onDateSelected()
|
||||
}
|
||||
|
||||
@objc private func confirmTapped() {
|
||||
|
||||
private func onDateSelected() {
|
||||
if let date = self.selectedDate {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "yyyy-MM-dd"
|
||||
@@ -92,16 +143,11 @@ public class UILocalPlugin: CAPPlugin, CAPBridgedPlugin {
|
||||
let result: PluginCallResultData = ["value": dateString]
|
||||
self.call?.resolve(result)
|
||||
}
|
||||
|
||||
|
||||
self.bridge?.viewController?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func cancelTapped() {
|
||||
self.call?.reject("Date selection cancelled")
|
||||
self.bridge?.viewController?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override public func load () {
|
||||
|
||||
override public func load() {
|
||||
print("🔅 UILocalPlugin loaded")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user