From 32a44a51b5dbd967f80d0584a1e88b56aa4874f0 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 24 Jun 2024 15:34:02 -0500 Subject: [PATCH] updated date picker Signed-off-by: Matt Bruce --- VDS/Components/DatePicker/DatePicker.swift | 106 ++++++++++++--------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index e44c2a76..991167e1 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -30,8 +30,10 @@ open class DatePicker: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + internal override var responder: UIResponder? { hiddenView } + internal var hiddenView = UITextView().with { $0.width(0) } internal var minWidthDefault = 186.0 - internal var popoverView: UIView! + internal var popoverView: UIScrollView! internal var popoverVisible = false internal var outsideTapGesture: UITapGestureRecognizer? internal var outsidePanGesture: UIPanGestureRecognizer? @@ -141,6 +143,7 @@ open class DatePicker: EntryFieldBase { } controlStackView.addArrangedSubview(calendarIcon) controlStackView.addArrangedSubview(selectedDateLabel) + controlStackView.addArrangedSubview(hiddenView) return controlStackView } @@ -168,25 +171,11 @@ open class DatePicker: EntryFieldBase { formatter.dateFormat = dateFormat.format selectedDateLabel.text = formatter.string(from: date) } - - internal var popoverController: ClearPopoverViewController? - - func didSelect(_ date: Date) { - selectedDate = date - sendActions(for: .valueChanged) - UIAccessibility.post(notification: .layoutChanged, argument: self.containerView) - popoverController?.dismiss(animated: true){ [weak self] in - guard let self else { return } - popoverController = nil - } - } } extension DatePicker { - private func showPopover() { guard let viewController = UIApplication.topViewController(), let parentView = viewController.view else { return } - if popoverVisible { hidePopoverView() } else { @@ -226,34 +215,33 @@ extension DatePicker { parentView.addSubview(overlayView) - popoverView = UIView() - popoverView.backgroundColor = .white - popoverView.layer.cornerRadius = 10 - popoverView.layer.shadowColor = UIColor.black.cgColor - popoverView.layer.shadowOpacity = 0.2 - popoverView.layer.shadowOffset = CGSize(width: 0, height: 5) - popoverView.layer.shadowRadius = 10 + popoverView = UIScrollView() + popoverView.backgroundColor = .green + popoverView.clipsToBounds = true + popoverView.backgroundColor = .clear popoverView.isHidden = true popoverView.addSubview(calendar) calendar.pinToSuperView() - popoverView.translatesAutoresizingMaskIntoConstraints = false parentView.addSubview(popoverView) - popoverView.width(calendar.frame.width) - popoverView.height(calendar.frame.height) let spacing: CGFloat = 4 - let (popoverX, popoverY) = calculatePopoverPosition(relativeTo: containerView, in: parentView, size: calendar.frame.size, with: spacing) - popoverView.leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: popoverX).isActive = true - popoverView.topAnchor.constraint(equalTo: parentView.topAnchor, constant: popoverY).isActive = true + let popoverSize: CGSize = .init(width: calendar.frame.width, height: calendar.frame.height) + popoverView.contentSize = CGSize(width: popoverSize.width, height: popoverSize.height) + + let (popoverX, popoverY, adjustedHeight) = calculatePopoverPosition(relativeTo: containerView, in: parentView, size: popoverSize, with: spacing) + //let adjustedX = adjustedHeight != popoverSize.height ? popoverX - 10 : popoverX + let adjustedWidth = adjustedHeight != popoverSize.height ? popoverSize.width + 10 : popoverSize.width + popoverView.frame = CGRect(x: popoverX, y: popoverY, width: adjustedWidth, height: adjustedHeight) parentView.layoutIfNeeded() popoverView.alpha = 0 popoverView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9) popoverView.isHidden = false popoverVisible = true - - UIView.animate(withDuration: 0.3, + _ = responder?.becomeFirstResponder() + updateContainerView() + UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.2, @@ -262,6 +250,10 @@ extension DatePicker { guard let self else { return } popoverView.alpha = 1 popoverView.transform = CGAffineTransform.identity + if popoverSize.height > adjustedHeight { + popoverView.flashScrollIndicators() + } + UIAccessibility.post(notification: .layoutChanged, argument: calendar) parentView.layoutIfNeeded() }) } @@ -270,10 +262,9 @@ extension DatePicker { private func hidePopoverView() { overlayView.isHidden = true overlayView.removeFromSuperview() - outsideTapGesture = nil outsidePanGesture = nil - UIView.animate(withDuration: 0.2, + UIView.animate(withDuration: 0.2, animations: {[weak self] in guard let self else { return } popoverView.alpha = 0 @@ -283,17 +274,23 @@ extension DatePicker { popoverView.isHidden = true popoverView.removeFromSuperview() popoverVisible = false + responder?.resignFirstResponder() + setNeedsUpdate() + UIAccessibility.post(notification: .layoutChanged, argument: containerView) } + } - - private func calculatePopoverPosition(relativeTo sourceView: UIView, in parentView: UIView, size: CGSize, with spacing: CGFloat) -> (CGFloat, CGFloat) { + + private func calculatePopoverPosition(relativeTo sourceView: UIView, in parentView: UIView, size: CGSize, with spacing: CGFloat) -> (CGFloat, CGFloat, CGFloat) { let sourceFrameInParent = sourceView.convert(sourceView.bounds, to: parentView) let parentBounds = parentView.bounds - let popoverWidth: CGFloat = size.width - let popoverHeight: CGFloat = size.height + let safeAreaInsets = parentView.safeAreaInsets + let popoverWidth = size.width + let popoverHeight = size.height var popoverX: CGFloat = 0 var popoverY: CGFloat = 0 + var adjustedHeight = popoverHeight // Calculate horizontal position if sourceFrameInParent.width < popoverWidth { @@ -311,19 +308,34 @@ extension DatePicker { popoverX = sourceFrameInParent.midX - popoverWidth / 2 } - // Calculate vertical position - if sourceFrameInParent.origin.y > parentBounds.height / 2 { - // Show above - popoverY = sourceFrameInParent.minY - popoverHeight - spacing - } else { - // Show below - popoverY = sourceFrameInParent.maxY + spacing - } - - // Ensure the popover is within the parent's bounds + // Ensure the popover is within the parent's bounds horizontally popoverX = max(0, min(popoverX, parentBounds.width - popoverWidth)) - return (popoverX, popoverY) + // Calculate vertical position and height + let availableSpaceAbove = sourceFrameInParent.minY - safeAreaInsets.top - spacing + let availableSpaceBelow = parentBounds.height - sourceFrameInParent.maxY - safeAreaInsets.bottom - spacing + let totalAvailableHeight = parentBounds.height - safeAreaInsets.top - safeAreaInsets.bottom + + if availableSpaceAbove >= popoverHeight { + // Show above without adjusting height + popoverY = sourceFrameInParent.minY - popoverHeight - spacing + } else if availableSpaceBelow >= popoverHeight { + // Show below without adjusting height + popoverY = sourceFrameInParent.maxY + spacing + + } else if totalAvailableHeight >= popoverHeight { + // check if the total + if availableSpaceAbove > availableSpaceBelow { + popoverY = safeAreaInsets.top + } else { + popoverY = parentBounds.height - safeAreaInsets.bottom - popoverHeight + } + } else { + popoverY = safeAreaInsets.top + adjustedHeight = totalAvailableHeight + } + + return (popoverX, popoverY, adjustedHeight) } }