// // CommonControllerModal.swift // Wallet // // Created by Igor on 9.02.2021. // import UIKit extension UIViewController { private struct Values { static var image = "image" static var text = "text" } @IBInspectable var image: UIImage? { get { Common.Runtime.object(self, key: &Values.image) } set { Common.Runtime.set(self, value: newValue, key: &Values.image) } } @IBInspectable var text: String? { get { Common.Runtime.object(self, key: &Values.text) } set { Common.Runtime.set(self, value: newValue, key: &Values.text) } } } class CommonControllerPopup: UIViewController { @IBOutlet weak var contentView: UIView! @IBOutlet private weak var alphaView: UIView! @IBOutlet private weak var stackView: UIStackView! @IBOutlet private weak var offset: NSLayoutConstraint! @IBOutlet weak var bottomHeight: NSLayoutConstraint! @IBOutlet private weak var titleLbl: UILabel! @IBOutlet private weak var textLbl: UILabel! @IBOutlet private weak var imgView: UIImageView! @IBOutlet private weak var imgViewWidth: NSLayoutConstraint! @IBOutlet private weak var imgViewHeight: NSLayoutConstraint! @IBOutlet private weak var contentLeft: NSLayoutConstraint! @IBOutlet private weak var contentWidth: NSLayoutConstraint! @IBOutlet private weak var contentRight: NSLayoutConstraint! @IBOutlet private weak var contentScrollVew: UIScrollView! @IBOutlet private weak var contentStackView: UIStackView! @IBOutlet private weak var contentOffsetView: UIView! private let contentViewController: UIViewController? private var willPresent = true private let completion: (() -> Void)? private let contentScrollIsEnabled: Bool init(contentViewController: UIViewController, contentScrollIsEnabled: Bool, completion: (() -> Void)? = nil) { contentViewController.view.backgroundColor = .clear self.completion = completion self.contentScrollIsEnabled = contentScrollIsEnabled self.contentViewController = contentViewController super.init(nibName: "CommonControllerPopup", bundle: nil) _ = view modalPresentationStyle = .custom transitioningDelegate = self addChild(contentViewController) alphaView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onHide))) stackView.superview?.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(onPan(_:)))) contentStackView.addArrangedSubview(contentViewController.view) update() } init( image: UIImage? = nil, title: String? = nil, text: String? = nil, views: [UIView], spacing: CGFloat, completion: (() -> Void)? = nil ) { contentViewController = nil self.contentScrollIsEnabled = true self.completion = completion super.init(nibName: "CommonControllerPopup", bundle: nil) _ = view self.title = title self.text = text self.image = image modalPresentationStyle = .custom transitioningDelegate = self alphaView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onHide))) stackView.superview?.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(onPan(_:)))) views.forEach({ contentStackView.addArrangedSubview($0) }) contentLeft.constant = 12 contentWidth.constant = -24 contentRight.constant = 12 contentStackView.spacing = spacing update() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() contentScrollVew.isScrollEnabled = self.contentScrollIsEnabled ? self.contentScrollVew.frame.height < contentStackView.frame.height : false } private func update() { if let image = contentViewController?.image ?? image { imgView.image = image imgViewWidth.constant = image.size.width imgViewHeight.constant = image.size.height imgView.superview?.isHidden = false } else { imgView.superview?.isHidden = true } if let title = contentViewController?.title ?? title { titleLbl.superview?.isHidden = false titleLbl.attributedText = title.localized.attributed(style: .bold, size: 28, color: Asset.textCoal.color) } else { titleLbl.superview?.isHidden = true } if let text = contentViewController?.text ?? text { textLbl.superview?.isHidden = false textLbl.attributedText = text.localized.attributed(style: .regular, size: 14, color: Asset.textGranite.color) } else { textLbl.superview?.isHidden = true } contentOffsetView.isHidden = (titleLbl.attributedText?.string.isEmpty ?? true) && (textLbl.attributedText?.string.isEmpty ?? true) stackView.layoutIfNeeded() view.frame = UIScreen.main.bounds view.layoutIfNeeded() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) completion?() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) Alert.refresh() } @objc private func onHide() { dismiss(animated: true) } private var point: CGPoint! @objc private func onPan(_ pan: UIPanGestureRecognizer) { switch pan.state { case .began: point = pan.location(in: view) case .changed: let new = pan.location(in: view) offset.constant += point.y - new.y if offset.constant > -bottomHeight.constant { offset.constant = -bottomHeight.constant } view.layoutIfNeeded() point = new case .ended, .cancelled: if offset.constant < -stackView.frame.height/2 { dismiss(animated: true) } else { offset.constant = -bottomHeight.constant UIView.animate(withDuration: Animation.medium) { self.view.layoutIfNeeded() } } default: break } } } extension CommonControllerPopup: UIViewControllerTransitioningDelegate { func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { willPresent = false return self } func animationController( forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController ) -> UIViewControllerAnimatedTransitioning? { self } } extension CommonControllerPopup: UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 0.2 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let presentView = willPresent ? transitionContext.viewController(forKey: .to)!.view! : transitionContext.viewController(forKey: .from)!.view! if willPresent { transitionContext.containerView.addSubview(presentView) offset.constant = -stackView.frame.height view.layoutIfNeeded() } presentView.frame = view.bounds alphaView.alpha = willPresent ? 0 : 1 offset.constant = willPresent ? -bottomHeight.constant : -stackView.frame.height UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { self.alphaView.alpha = self.willPresent ? 1 : 0 self.view.layoutIfNeeded() }, completion: { (success) in if !self.willPresent { presentView.removeFromSuperview() } transitionContext.completeTransition(success) }) } }