diff --git a/VDS/Classes/AlertViewController.swift b/VDS/Classes/AlertViewController.swift index 71a1c6d9..30a5d5c6 100644 --- a/VDS/Classes/AlertViewController.swift +++ b/VDS/Classes/AlertViewController.swift @@ -62,21 +62,6 @@ open class AlertViewController: UIViewController, Surfaceable { open func setup() { guard let dialog else { return } view.accessibilityElements = [dialog] - - //left-right swipe - view.publisher(for: UISwipeGestureRecognizer().with{ $0.direction = .right }) - .sink { [weak self] swipe in - guard let self, !UIAccessibility.isVoiceOverRunning else { return } - self.dismiss() - }.store(in: &subscribers) - - //tapping in background - view.publisher(for: UITapGestureRecognizer().with{ $0.numberOfTapsRequired = 1 }) - .sink { [weak self] swipe in - guard let self, !UIAccessibility.isVoiceOverRunning else { return } - self.dismiss() - }.store(in: &subscribers) - view.addSubview(dialog) // Activate constraints @@ -90,6 +75,16 @@ open class AlertViewController: UIViewController, Surfaceable { dialog.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: -10) ]) } + + open override func touchesBegan(_ touches: Set, with event: UIEvent?) { + guard let touch = touches.first else { return } + let location = touch.location(in: view) + if dialog.frame.contains(location) { + super.touchesBegan(touches, with: event) + } else { + dismiss() + } + } /// Used to make changes to the View based off a change events or from local properties. open func updateView() { diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index b5231154..cb2b5b88 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -52,6 +52,15 @@ open class DatePicker: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Popover/Alert Properties //-------------------------------------------------- + /// View shown inline + internal var popoverOverlayView = UIView().with { + $0.backgroundColor = .clear + $0.translatesAutoresizingMaskIntoConstraints = false + } + + /// use this to track touch events outside of the popover in the overlay + internal var popupOverlayTapGesture: AnyCancellable? + /// View shown inline internal var popoverView: UIView! /// Size used for the popover @@ -147,6 +156,7 @@ open class DatePicker: EntryFieldBase { } .store(in: &subscribers) + popoverOverlayView.isHidden = true } open override func getFieldContainer() -> UIView { @@ -189,7 +199,7 @@ open class DatePicker: EntryFieldBase { } extension DatePicker { - + private func showPopover() { guard let viewController = UIApplication.topViewController(), var parentView = viewController.view, !popoverVisible else { hidePopoverView() @@ -220,12 +230,12 @@ extension DatePicker { if let scrollView { parentView = scrollView } - + // see if you should use the popover or show an alert - if let popoverOrigin = try? calculatePopoverPosition(relativeTo: containerView, - in: parentView, - size: popoverViewSize, - with: popoverSpacing) { + if let popoverOrigin = calculatePopoverPosition(relativeTo: containerView, + in: parentView, + size: popoverViewSize, + with: popoverSpacing) { calendar.onChange = { [weak self] control in guard let self else { return } selectedDate = control.selectedDate @@ -246,6 +256,16 @@ extension DatePicker { calendar.pinToSuperView() // add views + popoverOverlayView.isHidden = false + popupOverlayTapGesture = popoverOverlayView + .publisher(for: UITapGestureRecognizer()) + .sink(receiveValue: { [weak self] gesture in + guard let self else { return } + gestureEventOccured(gesture, parentView: parentView) + }) + + parentView.addSubview(popoverOverlayView) + popoverOverlayView.pinToSuperView() parentView.addSubview(popoverView) parentView.layoutIfNeeded() @@ -302,6 +322,11 @@ extension DatePicker { topViewController?.dismiss(animated: true) topViewController = nil } else { + popoverOverlayView.isHidden = true + popoverOverlayView.removeFromSuperview() + popupOverlayTapGesture?.cancel() + popupOverlayTapGesture = nil + UIView.animate(withDuration: 0.2, animations: {[weak self] in guard let self, let popoverView else { return } @@ -367,7 +392,7 @@ extension DatePicker { var availableSpaceBelow: CGFloat = 0.0 /// if the scrollView is set we want to change how we calculate the containerView's position - if var scrollView = parentView as? UIScrollView { + if let scrollView = parentView as? UIScrollView { // Calculate vertical position and height availableSpaceAbove = sourceFrameInParent.minY - scrollView.bounds.minY - spacing availableSpaceBelow = scrollView.bounds.maxY - sourceFrameInParent.maxY - spacing @@ -408,4 +433,12 @@ extension DatePicker { return .init(x: popoverX, y: popoverY) } + + private func gestureEventOccured(_ gesture: UIGestureRecognizer, parentView: UIView) { + guard let popoverView, popoverVisible else { return } + let location = gesture.location(in: parentView) + if !popoverView.frame.contains(location) { + hidePopoverView() + } + } }