// // 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 contentTop: NSLayoutConstraint! @IBOutlet private weak var contentHeight: NSLayoutConstraint! @IBOutlet private weak var contentBottom: NSLayoutConstraint! @IBOutlet private weak var contentScrollVew: UIScrollView! @IBOutlet private weak var contentStackView: UIStackView! @IBOutlet private weak var contentOffsetView: UIView! @IBOutlet private weak var panIndicator: UIView! private let contentViewController: UIViewController? private var willPresent = true private let completion: (() -> Void)? private let contentScrollIsEnabled: Bool var muteCompletion = false 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) self.loadViewIfNeeded() self.modalPresentationStyle = .custom self.transitioningDelegate = self self.addChild(contentViewController) self.alphaView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onHide))) self.stackView.superview?.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(onPan(_:)))) self.contentStackView.addArrangedSubview(contentViewController.view) self.update() } init( image: UIImage? = nil, title: String? = nil, text: String? = nil, views: [UIView], spacing: CGFloat, completion: (() -> Void)? = nil ) { self.contentViewController = nil self.contentScrollIsEnabled = true self.completion = completion super.init(nibName: "CommonControllerPopup", bundle: nil) self.loadViewIfNeeded() self.title = title self.text = text self.image = image self.modalPresentationStyle = .custom self.transitioningDelegate = self self.alphaView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onHide))) self.stackView.superview?.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(onPan(_:)))) views.forEach { contentStackView.addArrangedSubview($0) } self.contentLeft.constant = 12 self.contentWidth.constant = -24 self.contentRight.constant = 12 self.contentStackView.spacing = spacing self.update() } init( views: [UIView], spacing: CGFloat = 0, leftSpace: CGFloat? = nil, rightSpace: CGFloat? = nil, topSpace: CGFloat? = nil, bottomSpace: CGFloat? = nil ) { self.contentViewController = nil self.contentScrollIsEnabled = false self.completion = nil super.init(nibName: "CommonControllerPopup", bundle: nil) self.loadViewIfNeeded() self.modalPresentationStyle = .custom self.transitioningDelegate = self self.panIndicator.isHidden = true views.forEach { contentStackView.addArrangedSubview($0) } if let leftSpace = leftSpace, let rightSpace = rightSpace { self.contentLeft.constant = leftSpace self.contentWidth.constant = -(leftSpace + rightSpace) self.contentRight.constant = rightSpace } if let topSpace = topSpace, let bottomSpace = bottomSpace { self.contentTop.constant = topSpace - spacing self.contentHeight.constant = topSpace + bottomSpace - spacing self.contentBottom.constant = bottomSpace - spacing } self.contentStackView.spacing = spacing self.update() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() self.contentScrollVew.isScrollEnabled = self.contentScrollIsEnabled ? self.contentScrollVew.frame.height < contentStackView.frame.height : false } private func update() { if let image = contentViewController?.image ?? image { self.imgView.image = image self.imgViewWidth.constant = image.size.width self.imgViewHeight.constant = image.size.height self.imgView.superview?.isHidden = false } else { self.imgView.superview?.isHidden = true } if let title = contentViewController?.title ?? title { self.titleLbl.superview?.isHidden = false self.titleLbl.attributedText = title.localized.attributed(style: .bold, size: 28, color: Asset.textCoal.color) } else { self.titleLbl.superview?.isHidden = true } if let text = contentViewController?.text ?? text { self.textLbl.superview?.isHidden = false self.textLbl.attributedText = text.localized.attributed(style: .regular, size: 14, color: Asset.textGranite.color) } else { self.textLbl.superview?.isHidden = true } self.contentOffsetView.isHidden = (titleLbl.attributedText?.string.isEmpty ?? true) && (textLbl.attributedText?.string.isEmpty ?? true) self.stackView.layoutIfNeeded() self.view.frame = UIScreen.main.bounds self.view.layoutIfNeeded() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if self.muteCompletion { return } self.completion?() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) Alert.refresh() } @objc private func onHide() { self.dismiss(animated: true) } private var point: CGPoint! @objc private func onPan(_ pan: UIPanGestureRecognizer) { switch pan.state { case .began: self.point = pan.location(in: view) case .changed: let new = pan.location(in: view) self.offset.constant += self.point.y - new.y if self.offset.constant > -self.bottomHeight.constant { self.offset.constant = -self.bottomHeight.constant } self.view.layoutIfNeeded() self.point = new case .ended, .cancelled: if self.offset.constant < -self.stackView.frame.height/2 { self.dismiss(animated: true) } else { self.offset.constant = -self.bottomHeight.constant UIView.animate(withDuration: Animation.medium) { self.view.layoutIfNeeded() } } default: break } } } extension CommonControllerPopup: UIViewControllerTransitioningDelegate { func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { self.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 = self.willPresent ? transitionContext.viewController(forKey: .to)!.view! : transitionContext.viewController(forKey: .from)!.view! if self.willPresent { transitionContext.containerView.addSubview(presentView) self.offset.constant = -self.stackView.frame.height self.view.layoutIfNeeded() } presentView.frame = view.bounds self.alphaView.alpha = self.willPresent ? 0 : 1 self.offset.constant = self.willPresent ? -self.bottomHeight.constant : -self.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) }) } }