如何在任何视图控制器中重复使用向上滑动菜单 class?

How to reuse slide up menu class in any view controller?

我正在尝试创建一个向上滑动菜单,完全在名为 OldMenu 的自定义 class 中定义。

它会在用户每次点击 UIBarButtonItem 时显示,它也在 class 中定义。

所以按钮、菜单、显示菜单的菜单动画都在这个自定义里定义了class。

现在,我只能从另一个 class 访问按钮,但它似乎不会触发动画来显示菜单。

有人知道为什么这段代码不起作用吗?

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let oldMenu = OldMenu()
        navigationItem.rightBarButtonItems = [oldMenu.MenuButton]
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

class OldMenu: NSObject, UITableViewDelegate, UITableViewDataSource {
    var tableView: UITableView {
        let tableView = UITableView()
        tableView.isScrollEnabled = true
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "Cell")
        return tableView
    }
    
    var transparentView = UIView()
    
    let height: CGFloat = 250
    
    var MenuButton: UIBarButtonItem {
        let btn = UIBarButtonItem()
        btn.title = "Menu"
        btn.target = self
        btn.action = #selector(onClickMenu)
        return btn
    }
}

extension OldMenu {
    
    @objc func onClickMenu(_ sender: Any) {
        let window = UIApplication.shared.keyWindow
        transparentView.backgroundColor = UIColor.black.withAlphaComponent(0.9)
        transparentView.frame = window?.bounds ?? .zero
        window?.addSubview(transparentView)
        
        let screenSize = UIScreen.main.bounds.size
        tableView.frame = CGRect(x: 0, y: screenSize.height, width: screenSize.width, height: height)
        window?.addSubview(tableView)
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onClickTransparentView))
        transparentView.addGestureRecognizer(tapGesture)
        
        transparentView.alpha = 0
        
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: {
            self.transparentView.alpha = 0.5
            self.tableView.frame = CGRect(x: 0, y: screenSize.height - self.height, width: screenSize.width, height: self.height)
        }, completion: nil)
        
    }
    
    @objc func onClickTransparentView() {
        let screenSize = UIScreen.main.bounds.size

        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: {
            self.transparentView.alpha = 0
            self.tableView.frame = CGRect(x: 0, y: screenSize.height, width: screenSize.width, height: self.height)
        }, completion: nil)
    }
}

extension OldMenu {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return UITableViewCell()
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 50
    }
    
}

代码

class ViewController: UIViewController {

var oldMenu: OldMenu!

   override func viewDidLoad() {
      super.viewDidLoad()
    
       oldMenu = OldMenu(controller: self)
       navigationItem.rightBarButtonItem = oldMenu.MenuButton
   }

   override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      // Dispose of any resources that can be recreated.
   }
}

extension ViewController: MenuDelegate {


   func tableRowDidSelect(_ indexPath: IndexPath) {
    
   }
}

旧菜单

class OldMenu: NSObject {
    
    var presenter: UIViewController?
    
    var menuController = MenuViewController()

    var MenuButton: UIBarButtonItem!
    
    init(controller: UIViewController?) {
        super.init()
        presenter = controller
        MenuButton = UIBarButtonItem(title: "Menu", style: .plain, target: self, action: #selector(onClickMenu))
    }
    
    @objc func onClickMenu() {
        menuController.modalPresentationStyle = .custom
        menuController.transitioningDelegate = self
        menuController.delegate = presenter as! MenuDelegate
        presenter?.present(menuController, animated: true, completion: nil)
    }
}

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

MenuViewController

在 MenuViewController 中配置您的 tableView,并使用委托与主视图通信

protocol MenuDelegate {
    func tableRowDidSelect(_ indexPath: IndexPath)
}

// Configure your tableView in MenuViewController
// Use delegates to communicate back to main view
class MenuViewController: UIViewController {
    
    var delegate: MenuDelegate?
    
    override func viewDidLoad() {
        view.backgroundColor = .gray
    }
}

PresentationController

您可以重用 PresentationController 在半屏中呈现视图控制器。

import Foundation
import UIKit

class PresentationController: 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)
  }
  
  override var frameOfPresentedViewInContainerView: CGRect {

      // Set your presentation height
      let presentHeight: CGFloat = 300
      
      return CGRect(origin: CGPoint(x: 0, y: self.containerView!.frame.height - presentHeight),
             size: CGSize(width: self.containerView!.frame.width, height: presentHeight))
  }

  override func presentationTransitionWillBegin() {
      self.blurEffectView.alpha = 0
      self.containerView?.addSubview(blurEffectView)
      self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in
          self.blurEffectView.alpha = 0.5
      }, 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()
      
      // set your corner radius
    presentedView!.roundCorners([.topLeft, .topRight], radius: 18)
  }

  override func containerViewDidLayoutSubviews() {
      super.containerViewDidLayoutSubviews()
      presentedView?.frame = frameOfPresentedViewInContainerView
      blurEffectView.frame = containerView!.bounds
  }

  @objc func dismissController(){
      self.presentedViewController.dismiss(animated: true, completion: nil)
  }
}

extension UIView {
  func roundCorners(_ 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
  }
}

结果