如何在任何视图控制器中重复使用向上滑动菜单 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
}
}
结果
我正在尝试创建一个向上滑动菜单,完全在名为 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
}
}