// // ModalDialogViewController.swift // VDS // // Created by Kanamarlapudi, Vasavi on 09/09/24. // import Foundation import UIKit import Combine import VDSCoreTokens @objcMembers @objc(VDSModalDialogViewController) open class ModalDialogViewController: UIViewController, Surfaceable { /// Set of Subscribers for any Publishers for this Control. open var subscribers = Set() //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- private var onClickSubscriber: AnyCancellable? { willSet { if let onClickSubscriber { onClickSubscriber.cancel() } } } private let modalDialog = ModalDialog() //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- /// Current Surface and this is used to pass down to child objects that implement Surfacable open var surface: Surface = .light { didSet { updateView() }} open var modalModel = Modal.ModalModel() { didSet { updateView() }} open var presenter: UIView? { didSet { updateView() }} //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- open override func viewDidLoad() { super.viewDidLoad() isModalInPresentation = true setup() } open override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) UIAccessibility.post(notification: .screenChanged, argument: modalDialog) } private func dismiss() { dismiss(animated: true) { [weak self] in guard let self, let presenter else { return } UIAccessibility.post(notification: .layoutChanged, argument: presenter) } } open func setup() { view.accessibilityElements = [modalDialog] //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) //clicking button onClickSubscriber = modalDialog.closeCrossButton.publisher(for: .touchUpInside) .sink {[weak self] button in guard let self else { return } self.dismiss() } view.addSubview(modalDialog) // Activate constraints UIDevice.isIPad ? NSLayoutConstraint.activate([ // Constraints for the floating modal view modalDialog.centerXAnchor.constraint(equalTo: view.centerXAnchor), modalDialog.centerYAnchor.constraint(equalTo: view.centerYAnchor), modalDialog.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor), modalDialog.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor), modalDialog.topAnchor.constraint(greaterThanOrEqualTo: view.topAnchor), modalDialog.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor) ]) : NSLayoutConstraint.activate([ // Constraints for the floating modal view modalDialog.leadingAnchor.constraint(equalTo: view.leadingAnchor), modalDialog.trailingAnchor.constraint(equalTo: view.trailingAnchor), modalDialog.topAnchor.constraint(equalTo: view.topAnchor), modalDialog.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) } /// Used to make changes to the View based off a change events or from local properties. open func updateView() { view.backgroundColor = backgroundColorConfiguration.getColor(self).withAlphaComponent(0.8) modalDialog.surface = surface modalDialog.modalModel = modalModel } }