// // 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) } /// Used to make changes to the View based off a change events or from local properties. open func updateView() { modalDialog.surface = surface modalDialog.modalModel = modalModel // Activate constraints modalDialog.removeConstraints() let isFullScreen = UIDevice.isIPad && !modalModel.fullScreenDialog ? false : true if isFullScreen { view.backgroundColor = modalDialog.backgroundColor modalDialog .pinLeading() .pinTrailing() modalDialog.pinTop(anchor: UIDevice.isIPad ? view.safeAreaLayoutGuide.topAnchor : view.topAnchor) modalDialog.pinBottom(UIDevice.isIPad ? view.bottomAnchor : view.safeAreaLayoutGuide.bottomAnchor) } else { view.backgroundColor = backgroundColorConfiguration.getColor(self).withAlphaComponent(0.8) NSLayoutConstraint.activate([ // Constraints for the floating modal view for Tablet. modalDialog.centerXAnchor.constraint(equalTo: view.centerXAnchor), modalDialog.centerYAnchor.constraint(equalTo: view.centerYAnchor), modalDialog.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.7), modalDialog.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.7) ]) } } }