From c02fd2241e1b11b41deac392c23162155919b6f7 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 30 Jun 2023 15:21:20 -0500 Subject: [PATCH] updated tooltip to accept a view or title or content text Signed-off-by: Matt Bruce --- .../Attributes/TooltipLabelAttribute.swift | 10 ++- .../TextFields/EntryField/EntryField.swift | 4 +- VDS/Components/Tooltip/Tooltip.swift | 18 ++-- .../Tooltip/TooltipAlertViewController.swift | 88 +++++++++++++------ .../Tooltip/TooltipLaunchable.swift | 5 +- .../Tooltip/TrailingTooltipLabel.swift | 22 +++-- 6 files changed, 101 insertions(+), 46 deletions(-) diff --git a/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift b/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift index b061d747..067d70ed 100644 --- a/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift @@ -20,8 +20,9 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable public var surface: Surface = .light public var accessibleText: String? = "Tool Tip" public var closeButtonText: String = "Close" - public var title: String - public var content: String + public var title: String? + public var content: String? + public var contentView: UIView? public func setAttribute(on attributedString: NSMutableAttributedString) { //update the location @@ -65,7 +66,7 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable addHandler(on: attributedString) } - public init(id: UUID = UUID(), action: PassthroughSubject = PassthroughSubject(), subscriber: AnyCancellable? = nil, surface: Surface, accessibleText: String? = nil, closeButtonText: String = "Close", title: String, content: String) { + public init(id: UUID = UUID(), action: PassthroughSubject = PassthroughSubject(), subscriber: AnyCancellable? = nil, surface: Surface, accessibleText: String? = nil, closeButtonText: String = "Close", title: String? = nil, content: String? = nil, contentView: UIView? = nil) { self.id = id self.action = action self.subscriber = subscriber @@ -74,13 +75,14 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable self.closeButtonText = closeButtonText self.title = title self.content = content - + self.contentView = contentView //create the tooltip click event self.subscriber = action.sink { [weak self] in guard let self else { return } self.presentTooltip(surface: self.surface, title: self.title, content: self.content, + contentView: contentView, closeButtonText: self.closeButtonText) } } diff --git a/VDS/Components/TextFields/EntryField/EntryField.swift b/VDS/Components/TextFields/EntryField/EntryField.swift index cbdc65fe..6a32bae1 100644 --- a/VDS/Components/TextFields/EntryField/EntryField.swift +++ b/VDS/Components/TextFields/EntryField/EntryField.swift @@ -151,6 +151,8 @@ open class EntryField: Control, Changeable { open var tooltipContent: String? { didSet { setNeedsUpdate() }} + open var tooltipContentView: UIView? { didSet { setNeedsUpdate() }} + open var transparentBackground: Bool = false { didSet { setNeedsUpdate() }} open var width: CGFloat? { didSet { setNeedsUpdate() }} @@ -293,7 +295,7 @@ open class EntryField: Control, Changeable { } if let tooltipTitle, let tooltipContent { - attributes.append(TooltipLabelAttribute(surface: surface, title: tooltipTitle, content: tooltipContent)) + attributes.append(TooltipLabelAttribute(surface: surface, title: tooltipTitle, content: tooltipContent, contentView: tooltipContentView)) } //set the titleLabel diff --git a/VDS/Components/Tooltip/Tooltip.swift b/VDS/Components/Tooltip/Tooltip.swift index c4a98636..f51cd36c 100644 --- a/VDS/Components/Tooltip/Tooltip.swift +++ b/VDS/Components/Tooltip/Tooltip.swift @@ -50,10 +50,12 @@ open class Tooltip: Control, TooltipLaunchable { open var size: Size = .medium { didSet { setNeedsUpdate() }} - open var title: String = "" { didSet { setNeedsUpdate() }} - - open var content: String = "" { didSet { setNeedsUpdate() }} + open var title: String? { didSet { setNeedsUpdate() }} + open var content: String? { didSet { setNeedsUpdate() }} + + open var contentView: UIView? { didSet { setNeedsUpdate() }} + //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- @@ -132,6 +134,7 @@ open class Tooltip: Control, TooltipLaunchable { self.presentTooltip(surface: tooltip.surface, title: tooltip.title, content: tooltip.content, + contentView: tooltip.contentView, closeButtonText: tooltip.closeButtonText) }) } @@ -160,8 +163,13 @@ open class Tooltip: Control, TooltipLaunchable { //get the color for the image let imageColor = iconColorConfiguration.getColor(self) imageView.image = infoImage.withTintColor(imageColor) - - accessibilityLabel = "Tooltip: \(title)" + var label = title + if label == nil { + label = content + } + if let label { + accessibilityLabel = "Tooltip: \(label)" + } } } diff --git a/VDS/Components/Tooltip/TooltipAlertViewController.swift b/VDS/Components/Tooltip/TooltipAlertViewController.swift index 0a994bde..b645c424 100644 --- a/VDS/Components/Tooltip/TooltipAlertViewController.swift +++ b/VDS/Components/Tooltip/TooltipAlertViewController.swift @@ -10,7 +10,7 @@ import UIKit import Combine import VDSColorTokens -open class TooltipAlertViewController: UIViewController, Surfaceable { +open class TooltipAlertViewController: UIViewController, Surfaceable, UIScrollViewDelegate { /// Set of Subscribers for any Publishers for this Control public var subscribers = Set() @@ -35,8 +35,15 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { $0.layer.cornerRadius = 8 } - private let containerView = View() + public var contentView: UIView? = nil + private let contentStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .vertical + $0.distribution = .fillProportionally + $0.spacing = 0 + } + private var line = Line().with { instance in instance.lineViewColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsLowcontrastOnlight, VDSColor.elementsLowcontrastOndark).eraseToAnyColorable() } @@ -45,12 +52,12 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { // MARK: - Public Properties //-------------------------------------------------- open var surface: Surface = .light { didSet { updateView() }} - open var titleText: String = "" { didSet { updateView() }} + open var titleText: String? { didSet { updateView() }} open var titleLabel = Label().with { label in label.textStyle = .boldTitleMedium } - open var contentText: String = "" { didSet { updateView() }} + open var contentText: String? { didSet { updateView() }} open var contentLabel = Label().with { label in label.textStyle = .bodyLarge } @@ -79,6 +86,9 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { private let closeButtonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) private let containerViewInset = VDSLayout.Spacing.space4X.value + private var containerBottomConstraint: NSLayoutConstraint? + private var containerHeightConstraint: NSLayoutConstraint? + private var contentStackViewBottomConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -89,7 +99,6 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { } open func setup() { - //left-right swipe view.publisher(for: UISwipeGestureRecognizer().with{ $0.direction = .right }) .sink { [weak self] swipe in @@ -111,9 +120,9 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { self.dismiss(animated: true, completion: nil) } - containerView.addSubview(titleLabel) - containerView.addSubview(contentLabel) - scrollView.addSubview(containerView) + contentStackView.addArrangedSubview(titleLabel) + contentStackView.addArrangedSubview(contentLabel) + scrollView.addSubview(contentStackView) modalView.addSubview(scrollView) modalView.addSubview(line) modalView.addSubview(closeButton) @@ -134,14 +143,6 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { scrollView.trailingAnchor.constraint(equalTo: modalView.trailingAnchor), scrollView.bottomAnchor.constraint(equalTo: line.topAnchor), - // Constraints for the container view - containerView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0), - containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -containerViewInset), - containerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: containerViewInset), - containerView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -containerViewInset), - containerView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -(containerViewInset * 2)), - containerView.heightAnchor.constraint(greaterThanOrEqualTo: scrollView.heightAnchor, constant: -(containerViewInset * 2)), - line.leadingAnchor.constraint(equalTo: modalView.leadingAnchor), line.trailingAnchor.constraint(equalTo: modalView.trailingAnchor), @@ -151,14 +152,12 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { closeButton.bottomAnchor.constraint(equalTo: modalView.bottomAnchor), closeButton.heightAnchor.constraint(equalToConstant: 44.0), - // Constraints on labels - titleLabel.topAnchor.constraint(equalTo: containerView.topAnchor), - titleLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), - titleLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), - contentLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: VDSLayout.Spacing.space1X.value), - contentLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), - contentLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), - contentLabel.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), + contentStackView.topAnchor.constraint(equalTo: scrollView.topAnchor), + contentStackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: containerViewInset), + contentStackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -containerViewInset), + contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -containerViewInset), + contentStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -(containerViewInset * 2)), + contentStackView.heightAnchor.constraint(greaterThanOrEqualTo: scrollView.heightAnchor, constant: -(containerViewInset * 2)) ]) } @@ -168,18 +167,57 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { modalView.backgroundColor = containerViewBackgroundColorConfiguration.getColor(self) scrollView.indicatorStyle = surface == .light ? .black : .white + titleLabel.removeFromSuperview() + contentLabel.removeFromSuperview() + contentView?.removeFromSuperview() + titleLabel.surface = surface contentLabel.surface = surface line.surface = surface titleLabel.text = titleText contentLabel.text = contentText + titleLabel.sizeToFit() contentLabel.sizeToFit() - + + var addedTitle = false + + if let titleText, !titleText.isEmpty { + contentStackView.addArrangedSubview(titleLabel) + addedTitle = true + } + + var addedContent = false + if let contentText, !contentText.isEmpty { + contentStackView.addArrangedSubview(contentLabel) + addedContent = true + } else if let contentView { + contentView.translatesAutoresizingMaskIntoConstraints = false + if var surfaceable = contentView as? Surfaceable { + surfaceable.surface = surface + } + let wrapper = View() + wrapper.addSubview(contentView) + contentView.pinTop() + contentView.pinLeading() + contentView.pinBottom() + contentView.pinTrailingLessThanOrEqualTo() + contentView.setNeedsLayout() + contentStackView.addArrangedSubview(wrapper) + addedContent = true + } + + if addedTitle && addedContent { + contentStackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: titleLabel) + } + let closeButtonTextColor = closeButtonTextColorConfiguration.getColor(self) closeButton.setTitleColor(closeButtonTextColor, for: .normal) closeButton.setTitleColor(closeButtonTextColor, for: .highlighted) closeButton.setTitle(closeButtonText, for: .normal) + + scrollView.layoutIfNeeded() + } } diff --git a/VDS/Components/Tooltip/TooltipLaunchable.swift b/VDS/Components/Tooltip/TooltipLaunchable.swift index 5f8219b7..17c3f680 100644 --- a/VDS/Components/Tooltip/TooltipLaunchable.swift +++ b/VDS/Components/Tooltip/TooltipLaunchable.swift @@ -9,16 +9,17 @@ import Foundation import UIKit public protocol TooltipLaunchable { - func presentTooltip(surface: Surface, title: String, content: String, closeButtonText: String) + func presentTooltip(surface: Surface, title: String?, content: String?, contentView: UIView?, closeButtonText: String) } extension TooltipLaunchable { - public func presentTooltip(surface: Surface, title: String, content: String, closeButtonText: String = "Close") { + public func presentTooltip(surface: Surface, title: String?, content: String?, contentView: UIView? = nil, closeButtonText: String = "Close") { if let presenting = UIApplication.topViewController() { let tooltipViewController = TooltipAlertViewController(nibName: nil, bundle: nil).with { $0.surface = surface $0.titleText = title $0.contentText = content + $0.contentView = contentView $0.closeButtonText = closeButtonText $0.modalPresentationStyle = .overCurrentContext $0.modalTransitionStyle = .crossDissolve diff --git a/VDS/Components/Tooltip/TrailingTooltipLabel.swift b/VDS/Components/Tooltip/TrailingTooltipLabel.swift index 9c7fc628..220cb2f3 100644 --- a/VDS/Components/Tooltip/TrailingTooltipLabel.swift +++ b/VDS/Components/Tooltip/TrailingTooltipLabel.swift @@ -36,10 +36,12 @@ open class TrailingTooltipLabel: View, TooltipLaunchable { open var tooltipCloseButtonText: String = "Close" { didSet { setNeedsUpdate() } } - open var tooltipTitle: String = "" { didSet { setNeedsUpdate() } } + open var tooltipTitle: String? { didSet { setNeedsUpdate() } } - open var tooltipContent: String = "" { didSet { setNeedsUpdate() } } + open var tooltipContent: String? { didSet { setNeedsUpdate() } } + open var tooltipContentView: UIView? { didSet { setNeedsUpdate() } } + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -70,8 +72,8 @@ open class TrailingTooltipLabel: View, TooltipLaunchable { label.disabled = disabled //add tooltip - if let labelText, !labelText.isEmpty, !tooltipTitle.isEmpty, !tooltipContent.isEmpty { - label.addTooltip(model: .init(surface: surface, closeButtonText: tooltipCloseButtonText, title: tooltipTitle, content: tooltipContent)) + if let labelText, !labelText.isEmpty { + label.addTooltip(model: .init(surface: surface, closeButtonText: tooltipCloseButtonText, title: tooltipTitle, content: tooltipContent, contentView: tooltipContentView)) } } @@ -95,14 +97,15 @@ extension Label { public struct TooltipModel { public var surface: Surface public var closeButtonText: String - public var title: String - public var content: String - - public init(surface: Surface = .light, closeButtonText: String = "Close", title: String, content: String) { + public var title: String? + public var content: String? + public var contentView: UIView? + public init(surface: Surface = .light, closeButtonText: String = "Close", title: String?, content: String?, contentView: UIView?) { self.surface = surface self.closeButtonText = closeButtonText self.title = title self.content = content + self.contentView = contentView } } @@ -121,7 +124,8 @@ extension Label { let tooltip = TooltipLabelAttribute(surface: surface, closeButtonText: model.closeButtonText, title: model.title, - content: model.content) + content: model.content, + contentView: model.contentView) newAttributes.append(tooltip) }