From b302077f994fe98539a389dd5471ca82783ded4e Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 20 Jun 2024 18:23:54 -0500 Subject: [PATCH] added popover Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 8 +- VDS/Classes/ClearPopoverViewController.swift | 151 +++++++++++++++++++ 2 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 VDS/Classes/ClearPopoverViewController.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index ff8a6735..7c9565f7 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -152,7 +152,7 @@ EAC58C182BED0E2300BA39FA /* SecurityCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58C172BED0E2300BA39FA /* SecurityCode.swift */; }; EAC58C232BF2824200BA39FA /* DatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58C222BF2824200BA39FA /* DatePicker.swift */; }; EAC58C272BF4116200BA39FA /* DatePickerCalendarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58C262BF4116200BA39FA /* DatePickerCalendarModel.swift */; }; - EAC58C292BF4118C00BA39FA /* DatePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58C282BF4118C00BA39FA /* DatePickerViewController.swift */; }; + EAC58C292BF4118C00BA39FA /* ClearPopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58C282BF4118C00BA39FA /* ClearPopoverViewController.swift */; }; EAC71A1D2A2E155A00E47A9F /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC71A1C2A2E155A00E47A9F /* Checkbox.swift */; }; EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC71A1E2A2E173D00E47A9F /* RadioButton.swift */; }; EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */; }; @@ -368,7 +368,7 @@ EAC58C222BF2824200BA39FA /* DatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePicker.swift; sourceTree = ""; }; EAC58C242BF2A7FB00BA39FA /* DatePickerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = DatePickerChangeLog.txt; sourceTree = ""; }; EAC58C262BF4116200BA39FA /* DatePickerCalendarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerCalendarModel.swift; sourceTree = ""; }; - EAC58C282BF4118C00BA39FA /* DatePickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerViewController.swift; sourceTree = ""; }; + EAC58C282BF4118C00BA39FA /* ClearPopoverViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearPopoverViewController.swift; sourceTree = ""; }; EAC71A1C2A2E155A00E47A9F /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; EAC71A1E2A2E173D00E47A9F /* RadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = ""; }; EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupCollectionViewCell.swift; sourceTree = ""; }; @@ -746,6 +746,7 @@ isa = PBXGroup; children = ( EA985C1C296CD13600F2FF2E /* BundleManager.swift */, + EAC58C282BF4118C00BA39FA /* ClearPopoverViewController.swift */, EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */, EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */, EAF2F4752C231EAA007BFEDC /* AccessibilityActionElement.swift */, @@ -966,7 +967,6 @@ children = ( EAC58C222BF2824200BA39FA /* DatePicker.swift */, EAC58C262BF4116200BA39FA /* DatePickerCalendarModel.swift */, - EAC58C282BF4118C00BA39FA /* DatePickerViewController.swift */, EAC58C242BF2A7FB00BA39FA /* DatePickerChangeLog.txt */, ); path = DatePicker; @@ -1304,7 +1304,7 @@ EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */, EAC58C0A2BED004E00BA39FA /* FieldType.swift in Sources */, EA471F3A2A95587500CE9E58 /* LayoutConstraintable.swift in Sources */, - EAC58C292BF4118C00BA39FA /* DatePickerViewController.swift in Sources */, + EAC58C292BF4118C00BA39FA /* ClearPopoverViewController.swift in Sources */, EAF193432C134F3800C68D18 /* TableCellItem.swift in Sources */, EAB1D2CF28ABEF2B00DAE764 /* Typography+Base.swift in Sources */, EA0D1C3B2A6AD51B00E5C127 /* Typogprahy+Styles.swift in Sources */, diff --git a/VDS/Classes/ClearPopoverViewController.swift b/VDS/Classes/ClearPopoverViewController.swift new file mode 100644 index 00000000..33531a75 --- /dev/null +++ b/VDS/Classes/ClearPopoverViewController.swift @@ -0,0 +1,151 @@ +// +// DatePickerPopoverViewController.swift +// VDS +// +// Created by Matt Bruce on 5/14/24. +// + +import Foundation +import UIKit + +open class ClearPopoverViewController: UIViewController, UIPopoverPresentationControllerDelegate { + + /// The view to be inserted inside the popover + private var contentView: UIView! + + /// An object representing the arrow of the popover. + private var arrow: UIPopoverArrowDirection + + /// Popover presentation controller of the popover + private var popOver: UIPopoverPresentationController! + + open var spacing: CGFloat = 0 + /** + A controller that manages the popover. + - Parameter contentView: The view to be inserted inside the popover. + - Parameter design: An object used for defining visual attributes of the popover. + - Parameter arrow: An object representing the arrow in popover. + - Parameter sourceView: The view containing the anchor rectangle for the popover. + - Parameter sourceRect: The rectangle in the specified view in which to anchor the popover. + - Parameter barButtonItem: The bar button item on which to anchor the popover. + + Assign a value to `barButton` to anchor the popover to the specified bar button item. When presented, the popover’s arrow points to the specified item. Alternatively, you may specify the anchor location for the popover using the `sourceView` and `sourceRect` properties. + */ + public init(contentView: UIView, arrow: UIPopoverArrowDirection, sourceView: UIView? = nil, sourceRect: CGRect? = nil, spacing: CGFloat = 0, barButtonItem: UIBarButtonItem? = nil) { + self.contentView = contentView + self.spacing = spacing + self.arrow = arrow + super.init(nibName: nil, bundle: nil) + setupPopover(sourceView, sourceRect, barButtonItem) + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + open override func viewIsAppearing(_ animated: Bool) { + super.viewIsAppearing(animated) + view.superview?.accessibilityIdentifier = "HadCornerRadius" + view.accessibilityIdentifier = "PopoverViewController.View" + contentView.accessibilityIdentifier = "PopoverViewController.ContentView" + view.superview?.layer.cornerRadius = 0 + } + + open override func viewDidLayoutSubviews() { + contentView.frame.origin = CGPoint(x: 0, y: 0) + } + + ///Sets up the Popover and starts the timer for its closing. + private func setupPopover(_ sourceView: UIView?, _ sourceRect: CGRect?, _ barButtonItem: UIBarButtonItem?) { + modalPresentationStyle = .popover + view.addSubview(contentView) + + popOver = self.popoverPresentationController! + popOver.popoverLayoutMargins = .zero + popOver.popoverBackgroundViewClass = ClearPopoverBackgroundView.self + popOver.sourceView = sourceView + popOver.popoverLayoutMargins = .zero + + if let sourceRect = sourceRect { + popOver.sourceRect = sourceRect + } + + popOver.barButtonItem = barButtonItem + popOver.delegate = self + popOver.permittedArrowDirections = arrow + popOver.backgroundColor = .clear + + } + + open func popoverPresentationController(_ popoverPresentationController: UIPopoverPresentationController, willRepositionPopoverTo rect: UnsafeMutablePointer, in view: AutoreleasingUnsafeMutablePointer) { + if let presentedView = popoverPresentationController.presentedViewController.view.superview { + presentedView.layer.cornerRadius = 0 + } + } + + private func updatePopoverPosition() { + guard let popoverPresentationController = popoverPresentationController else { return } + if let sourceView = popoverPresentationController.sourceView { + popoverPresentationController.sourceRect = .init(x: sourceView.bounds.origin.x, + y: sourceView.bounds.origin.y, + width: sourceView.bounds.width, + height: sourceView.bounds.height + spacing) + } + } + + // Ensure to handle rotations + open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + coordinator.animate(alongsideTransition: { [weak self] _ in + self?.updatePopoverPosition() + }) + } + + open func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle { + return .none + } + + // Returns presentation controller of the popover + open func getPopoverPresentationController() -> UIPopoverPresentationController { + return popOver + } +} + +open class ClearPopoverBackgroundView: UIPopoverBackgroundView { + open override var arrowOffset: CGFloat { + get { 0 } + set { } + } + + open override var arrowDirection: UIPopoverArrowDirection { + get { .any } + set { } + } + + open override class var wantsDefaultContentAppearance: Bool { + false + } + + open override class func contentViewInsets() -> UIEdgeInsets{ + .zero + } + + open override class func arrowHeight() -> CGFloat { + 0 + } + + open override class func arrowBase() -> CGFloat{ + 0 + } + + open override func layoutSubviews() { + super.layoutSubviews() + layer.shadowOpacity = 0 + layer.shadowRadius = 0 + layer.cornerRadius = 0 + } + + open override func draw(_ rect: CGRect) { + + } +}