123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- import UIKit
- @objcMembers
- public class SideMenuManager: NSObject {
- final private class SideMenuPanGestureRecognizer: UIPanGestureRecognizer {}
- final private class SideMenuScreenEdgeGestureRecognizer: UIScreenEdgePanGestureRecognizer {}
- @objc public enum PresentDirection: Int { case
- left = 1,
- right = 0
- init(leftSide: Bool) {
- self.init(rawValue: leftSide ? 1 : 0)!
- }
- var edge: UIRectEdge {
- switch self {
- case .left: return .left
- case .right: return .right
- }
- }
- var name: String {
- switch self {
- case .left: return "leftMenuNavigationController"
- case .right: return "rightMenuNavigationController"
- }
- }
- }
- private var _leftMenu: Protected<Menu?> = Protected(nil) { SideMenuManager.setMenu(fromMenu: $0, toMenu: $1) }
- private var _rightMenu: Protected<Menu?> = Protected(nil) { SideMenuManager.setMenu(fromMenu: $0, toMenu: $1) }
- private var switching: Bool = false
-
- public static let `default` = SideMenuManager()
-
- public class var defaultManager: SideMenuManager {
- return SideMenuManager.default
- }
-
- open var leftMenuNavigationController: SideMenuNavigationController? {
- get {
- if _leftMenu.value?.isHidden == true {
- _leftMenu.value?.leftSide = true
- }
- return _leftMenu.value
- }
- set(menu) { _leftMenu.value = menu }
- }
-
-
- open var rightMenuNavigationController: SideMenuNavigationController? {
- get {
- if _rightMenu.value?.isHidden == true {
- _rightMenu.value?.leftSide = false
- }
- return _rightMenu.value
- }
- set(menu) { _rightMenu.value = menu }
- }
-
- @discardableResult public func addScreenEdgePanGesturesToPresent(toView view: UIView) -> [UIScreenEdgePanGestureRecognizer] {
- return [
- addScreenEdgePanGesturesToPresent(toView: view, forMenu: .left),
- addScreenEdgePanGesturesToPresent(toView: view, forMenu: .right)
- ]
- }
-
- @discardableResult public func addScreenEdgePanGesturesToPresent(toView view: UIView, forMenu side: PresentDirection) -> UIScreenEdgePanGestureRecognizer {
- if menu(forSide: side) == nil {
- let methodName = #function
- let suggestedMethodName = "addScreenEdgePanGesturesToPresent(toView:forMenu:))"
- Print.warning(.screenGestureAdded, arguments: methodName, side.name, suggestedMethodName)
- }
- return self.addScreenEdgeGesture(to: view, edge: side.edge)
- }
-
-
- @discardableResult public func addPanGestureToPresent(toView view: UIView) -> UIPanGestureRecognizer {
- if leftMenuNavigationController ?? rightMenuNavigationController == nil {
- Print.warning(.panGestureAdded, arguments: #function, PresentDirection.left.name, PresentDirection.right.name, required: true)
- }
-
- return addPresentPanGesture(to: view)
- }
- }
- internal extension SideMenuManager {
- func setMenu(_ menu: Menu?, forLeftSide leftSide: Bool) {
- switch leftSide {
- case true: leftMenuNavigationController = menu
- case false: rightMenuNavigationController = menu
- }
- }
- private class func setMenu(fromMenu: Menu?, toMenu: Menu?) -> Menu? {
- if fromMenu?.isHidden == false {
- Print.warning(.menuInUse, arguments: PresentDirection.left.name, required: true)
- return fromMenu
- }
- return toMenu
- }
- }
- private extension SideMenuManager {
- @objc func handlePresentMenuScreenEdge(_ gesture: UIScreenEdgePanGestureRecognizer) {
- handleMenuPan(gesture)
- }
- @objc func handlePresentMenuPan(_ gesture: UIPanGestureRecognizer) {
- handleMenuPan(gesture)
- }
- func handleMenuPan(_ gesture: UIPanGestureRecognizer) {
- if let activeMenu = activeMenu {
- let width = activeMenu.menuWidth
- let distance = gesture.xTranslation / width
- switch (gesture.state) {
- case .changed:
- if gesture.canSwitch {
- switching = (distance > 0 && !activeMenu.leftSide) || (distance < 0 && activeMenu.leftSide)
- if switching {
- activeMenu.cancelMenuPan(gesture)
- return
- }
- }
- default:
- switching = false
- }
- } else {
- let leftSide: Bool
- if let gesture = gesture as? UIScreenEdgePanGestureRecognizer {
- leftSide = gesture.edges.contains(.left)
- } else {
-
- if gesture.xTranslation == 0 { return }
- leftSide = gesture.xTranslation > 0
- }
- guard let menu = menu(forLeftSide: leftSide) else { return }
- menu.present(from: topMostViewController, interactively: true)
- }
- activeMenu?.handleMenuPan(gesture, true)
- }
- var activeMenu: Menu? {
- if leftMenuNavigationController?.isHidden == false { return leftMenuNavigationController }
- if rightMenuNavigationController?.isHidden == false { return rightMenuNavigationController }
- return nil
- }
- func menu(forSide: PresentDirection) -> Menu? {
- switch forSide {
- case .left: return leftMenuNavigationController
- case .right: return rightMenuNavigationController
- }
- }
- func menu(forLeftSide leftSide: Bool) -> Menu? {
- return menu(forSide: leftSide ? .left : .right)
- }
- func addScreenEdgeGesture(to view: UIView, edge: UIRectEdge) -> UIScreenEdgePanGestureRecognizer {
- if let screenEdgeGestureRecognizer = view.gestureRecognizers?.first(where: { $0 is SideMenuScreenEdgeGestureRecognizer }) as? SideMenuScreenEdgeGestureRecognizer,
- screenEdgeGestureRecognizer.edges == edge {
- screenEdgeGestureRecognizer.remove()
- }
- return SideMenuScreenEdgeGestureRecognizer(addTo: view, target: self, action: #selector(handlePresentMenuScreenEdge(_:))).with {
- $0.edges = edge
- }
- }
- @discardableResult func addPresentPanGesture(to view: UIView) -> UIPanGestureRecognizer {
- if let panGestureRecognizer = view.gestureRecognizers?.first(where: { $0 is SideMenuPanGestureRecognizer }) as? SideMenuPanGestureRecognizer {
- return panGestureRecognizer
- }
- return SideMenuPanGestureRecognizer(addTo: view, target: self, action: #selector(handlePresentMenuPan(_:)))
- }
- var topMostViewController: UIViewController? {
- return UIApplication.shared.keyWindow?.rootViewController?.topMostViewController
- }
- }
- extension SideMenuManager: SideMenuNavigationControllerTransitionDelegate {
- internal func sideMenuTransitionDidDismiss(menu: Menu) {
- defer { switching = false }
- guard switching, let switchToMenu = self.menu(forLeftSide: !menu.leftSide) else { return }
- switchToMenu.present(from: topMostViewController, interactively: true)
- }
- }
|