Представление представлений с пользовательской анимацией высоты/ скольжения вверх

#ios #swift #presentviewcontroller

Вопрос:

У меня есть CheckoutViewController с коллекционным представлением. В одной из этих ячеек отображается представление для ввода данных кредитной карты.

Платежная ячейка, в которой представлен вид

 class PaymentCell_Checkout: UICollectionViewCell {
    
    static let cellID = "PaymentCell_Checkout"
    
    var delegate: Checkout_VC!
    
    
    @objc func showPayWithView() {
       let vc = PayWithView()
       // let vc = CreditCardEntryView()
        vc.delegate = delegate
        vc.modalPresentationStyle = .custom
        vc.transitioningDelegate = self
        delegate.navigationController?.present(vc, animated: false, completion: nil)
    }
}


extension PaymentCell_Checkout: UIViewControllerTransitioningDelegate {
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        PaymentPresentationController(presentedViewController: presented, presenting: presenting)
    }
}
 

Представленное представление имеет 4 различных варианта для выбора (табличное представление) и представлено пользовательским переходом с использованием UIViewControllerTransitioningDelegate и UIPresentationController. Все это отлично работает.

Просмотр, чтобы выбрать способ оплаты

 class PayWithView: UIViewController {
    
    var hasSetPointOrigin = false
    var pointOrigin: CGPoint?
    
    var delegate: Checkout_VC!
    
    var payWithOptions: [PayWith] = []
    
    struct Cells {
        static let payCell = "PayCell"
    }
    
    lazy var tableView: UITableView = {
        let tableView = UITableView()
        tableView.alwaysBounceVertical = false
        tableView.delegate = self
        tableView.dataSource = self
        tableView.rowHeight = 50
        return tableView
    }()
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        if let selectedIndexPath = tableView.indexPathForSelectedRow {
            tableView.deselectRow(at: selectedIndexPath, animated: animated)
        }
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        if let selectedIndexPath = tableView.indexPathForSelectedRow {
            tableView.deselectRow(at: selectedIndexPath, animated: animated)
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        payWithOptions = setPayWith()

        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureRecognizerAction))
        view.addGestureRecognizer(panGesture)
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        configTableView()
        tableView.isScrollEnabled = tableView.contentSize.height > tableView.frame.size.height
    }
    
    override func viewDidLayoutSubviews() {
        if !hasSetPointOrigin {
            hasSetPointOrigin = true
            pointOrigin = self.view.frame.origin
        }
    }
    
    
    
    @objc func panGestureRecognizerAction(sender: UIPanGestureRecognizer) {
        let translation = sender.translation(in: view)
        guard translation.y >=  0 else { return }

        view.frame.origin = CGPoint(x: 0, y: self.pointOrigin!.y   translation.y)

        if sender.state == .ended {
            let dragVelocity = sender.velocity(in: view)

            if dragVelocity.y >= 1300 {
                self.dismiss(animated: true, completion: nil)
            } else {
                UIView.animate(withDuration: 0.3) {
                    self.view.frame.origin = self.pointOrigin ?? CGPoint(x: 0, y: 400)
                }
            }
        }
    }
}


//MARK: - Table View Methods
extension PayWithView: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return payWithOptions.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: Cells.payCell) as! PayWithCell
        let payOptions = payWithOptions[indexPath.row]
        cell.set(pay: payOptions)
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        if indexPath.row == 0 {
            dismiss(animated: true)
        }
        
        if indexPath.row == 3 {
            let vc = CreditCardEntryView()
            vc.delegate = delegate
            vc.modalPresentationStyle = .custom
            vc.transitioningDelegate = self
            self.navigationController?.present(vc, animated: false, completion: nil)
           
        }
    }

}

// Modal Presentation
extension PayWithView: UIViewControllerTransitioningDelegate {
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        PaymentPresentationController(presentedViewController: presented, presenting: presenting)
    }
}
 

UIPresentationController

 class PaymentPresentationController: UIPresentationController {
    
      let blurEffectView: UIVisualEffectView!
      var tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer()
      
      override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
          let blurEffect = UIBlurEffect(style: .dark)
          blurEffectView = UIVisualEffectView(effect: blurEffect)
          super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
          tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissController))
          blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
          self.blurEffectView.isUserInteractionEnabled = true
          self.blurEffectView.addGestureRecognizer(tapGestureRecognizer)
      }
      // Size of View
      override var frameOfPresentedViewInContainerView: CGRect {
        CGRect(origin: CGPoint(x: 0, y: self.containerView!.frame.height * 0.7),
                 size: CGSize(width: self.containerView!.frame.width, height: self.containerView!.frame.height *
                  0.3))
      }
    
      override func presentationTransitionWillBegin() {
          self.blurEffectView.alpha = 0
          self.containerView?.addSubview(blurEffectView)
          self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in
              self.blurEffectView.alpha = 0.7
          }, completion: { (UIViewControllerTransitionCoordinatorContext) in })
      }
      
      override func dismissalTransitionWillBegin() {
          self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in
              self.blurEffectView.alpha = 0
          }, completion: { (UIViewControllerTransitionCoordinatorContext) in
              self.blurEffectView.removeFromSuperview()
          })
      }
      
      override func containerViewWillLayoutSubviews() {
          super.containerViewWillLayoutSubviews()
        presentedView!.roundCornersCheckout([.topLeft, .topRight], radius: 22)
      }
    
      override func containerViewDidLayoutSubviews() {
          super.containerViewDidLayoutSubviews()
          presentedView?.frame = frameOfPresentedViewInContainerView
          blurEffectView.frame = containerView!.bounds
      }
    
      @objc func dismissController() {
          self.presentedViewController.dismiss(animated: true, completion: nil)
      }
    }
    // Round Corners
    extension UIView {
      func roundCornersCheckout(_ corners: UIRectCorner, radius: CGFloat) {
          let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners,
                                  cornerRadii: CGSize(width: radius, height: radius))
          let mask = CAShapeLayer()
          mask.path = path.cgPath
          layer.mask = mask
      }
    }
 

When the user clicks on the EnterCreditCardView row in the presented tableView, I would like for it to also be presented with this custom transition and height. However visibly nothing happens. I have tested presenting it as the main/ first presented view from the CollectionCell and it works fine. I think I need to dismiss the original TableView view but not sure. Thanks for any help🙏

EnterCreditCardView that should be presented but doesn’t

   class CreditCardEntryView: UIViewController {
    
    var hasSetPointOrigin = false
    var pointOrigin: CGPoint?
    
    var delegate: Checkout_VC!
    
   
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
        delegate.startCheckout()
        
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureRecognizerAction))
        view.addGestureRecognizer(panGesture)
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        addViews()
        constrainViews()
    }
    

    
    override func viewDidLayoutSubviews() {
        if !hasSetPointOrigin {
            hasSetPointOrigin = true
            pointOrigin = self.view.frame.origin
        }
    }

    @objc func panGestureRecognizerAction(sender: UIPanGestureRecognizer) {
        let translation = sender.translation(in: view)
        guard translation.y >=  0 else { return }

        view.frame.origin = CGPoint(x: 0, y: self.pointOrigin!.y   translation.y)

        if sender.state == .ended {
            let dragVelocity = sender.velocity(in: view)

            if dragVelocity.y >= 1300 {
                self.dismiss(animated: true, completion: nil)
            } else {
                UIView.animate(withDuration: 0.3) {
                    self.view.frame.origin = self.pointOrigin ?? CGPoint(x: 0, y: 400)
                }
            }
        }
    }
}


extension CreditCardEntryView {
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        PaymentPresentationController(presentedViewController: presented, presenting: presenting)
    }
}
 
 

Let me know if you need anymore of the code, trying to keep it to a minimal.

ALSO I set up breakpoints and I know my didSelectRow is being called, it enters the method but the breakpoint in The ViewDidLoad for CreditCardEntryView never fires?
Like I said the view works fine when presented from CollectionCell but not from TableView row