Compare commits

...

20 Commits

Author SHA1 Message Date
jonkykong 6aa68df4ce Update podspec 2019-07-28 23:05:46 -07:00
jonkykong 5c292125a6 Fix for tap gesture not working in landscape mode 2019-07-28 23:04:34 -07:00
jonkykong 48eb4a2e3a Fix for delegate not always being called 2019-07-28 23:04:18 -07:00
jonkykong 9503c814d6 README update 2019-07-28 23:03:59 -07:00
jonkykong 4dd4f54a8e Refactor + add support for tap gesture option 2019-07-28 23:03:41 -07:00
jonkykong 7429a42c26 Fix for landscape mode menu tap to dismiss 2019-07-28 16:05:49 -07:00
jonkykong df032e6072 Fix for updating UI in demo project 2019-07-28 16:05:23 -07:00
jonkykong 3a7c74e2e0 Fix for viewWillDisappear not being called 2019-07-27 03:28:14 -07:00
jonkykong 900926f268 Updated strings 2019-07-27 03:27:46 -07:00
jonkykong f5e585857c Updated documentation 2019-07-27 03:27:24 -07:00
Mikhail Apurin 1d96ef80b3 Unreliable dismissal by tap gesture 2019-07-25 16:50:11 +09:00
Jon Kent 452b354e19 Update README.md 2019-07-24 10:20:04 -07:00
jonkykong 78a3a66a69 Updated podspec 2019-07-23 01:41:18 -07:00
jonkykong 8fe0a23a38 Auto-assignment of leftSide property from SideMenuManager 2019-07-23 01:40:35 -07:00
jonkykong 3a873661c3 Refactor 2019-07-23 00:58:06 -07:00
jonkykong 89fc05a324 Example project fix 2019-07-23 00:55:16 -07:00
jonkykong 453e110246 Refactor 2019-07-23 00:55:02 -07:00
jonkykong b39142bc1a Updated podspec 2019-07-22 01:13:26 -07:00
jonkykong a6bd743e99 README correction 2019-07-22 01:13:15 -07:00
jonkykong 749588918c Logic correction 2019-07-22 01:13:02 -07:00
10 changed files with 143 additions and 104 deletions
+2 -2
View File
@@ -44,7 +44,7 @@ class MainViewController: UIViewController {
private func updateUI(settings: SideMenuSettings) {
let styles:[UIBlurEffect.Style] = [.dark, .light, .extraLight]
if let menuBlurEffectStyle = settings.blurEffectStyle {
blurSegmentControl.selectedSegmentIndex = styles.firstIndex(of: menuBlurEffectStyle) ?? 0
blurSegmentControl.selectedSegmentIndex = (styles.firstIndex(of: menuBlurEffectStyle) ?? 0) + 1
} else {
blurSegmentControl.selectedSegmentIndex = 0
}
@@ -54,7 +54,7 @@ class MainViewController: UIViewController {
menuScaleFactorSlider.value = Float(settings.presentationStyle.menuScaleFactor)
presentingAlphaSlider.value = Float(settings.presentationStyle.presentingEndAlpha)
presentingScaleFactorSlider.value = Float(settings.presentationStyle.presentingScaleFactor)
screenWidthSlider.value = Float(settings.menuWidth / view.frame.width)
screenWidthSlider.value = Float(settings.menuWidth / min(view.frame.width, view.frame.height))
shadowOpacitySlider.value = Float(settings.presentationStyle.onTopShadowOpacity)
}
+6
View File
@@ -182,6 +182,12 @@ extension SideMenuManager {
set {}
}
@available(*, deprecated, renamed: "enableSwipeToDismissGesture")
public var enableSwipeGestures: Bool {
get { return true }
set {}
}
@available(*, deprecated, renamed: "SideMenuPresentationStyle")
public typealias MenuPresentMode = SideMenuPresentationStyle
+14
View File
@@ -52,6 +52,20 @@ internal extension UIViewController {
}
}
internal extension UIGestureRecognizer {
convenience init(addTo view: UIView, target: Any, action: Selector) {
self.init()
addTarget(target, action: action)
view.addGestureRecognizer(self)
}
convenience init?(addTo view: UIView?, target: Any, action: Selector) {
guard let view = view else { return nil }
self.init(addTo: view, target: target, action: action)
}
}
internal extension UIPanGestureRecognizer {
var canSwitch: Bool {
+4 -2
View File
@@ -28,8 +28,10 @@ internal protocol MenuModel: TransitionModel {
var dismissOnRotation: Bool { get }
/// Automatically dismisses the menu when app goes to the background.
var dismissWhenBackgrounded: Bool { get }
/// Enable or disable gestures that would swipe to dismiss the menu. Default is true.
var enableSwipeGestures: Bool { get }
/// Enable or disable a swipe gesture that dismisses the menu. Will not be triggered when `presentingViewControllerUserInteractionEnabled` is set to true. Default is true.
var enableSwipeToDismissGesture: Bool { get }
/// Enable or disable a tap gesture that dismisses the menu. Will not be triggered when `presentingViewControllerUserInteractionEnabled` is set to true. Default is true.
var enableTapToDismissGesture: Bool { get }
/**
The push style of the menu.
+13 -5
View File
@@ -13,22 +13,30 @@ internal enum Print: String { case
menuAlreadyAssigned = "%@ was already assigned to the %@ of %@. When using multiple SideMenuManagers you may want to use new instances of UISideMenuNavigationController instead of existing instances to avoid crashes if the menu is presented more than once.",
menuInUse = "%@ cannot be modified while it's presented.",
panGestureAdded = "%@ was called before %@ or %@ was set. Gestures will not work without a menu.",
property = "a menu's %@ property can only be changed when it is hidden.",
property = "A menu's %@ property can only be changed when it is hidden.",
screenGestureAdded = "%@ was called before %@ was set. The gesture will not work without a menu. Use %@ to add gestures for only one menu.",
transitioningDelegate = "SideMenu requires use of the transitioningDelegate. It cannot be modified."
internal static func warning(_ print: Print, arguments: CVarArg..., required: Bool = false) {
warning(String(format: print.rawValue, arguments), required: required)
enum PropertyName: String { case
leftSide
}
internal static func warning(_ print: Print, required: Bool = false) {
static func warning(_ print: Print, arguments: CVarArg..., required: Bool = false) {
warning(String(format: print.rawValue, arguments: arguments), required: required)
}
static func warning(_ print: Print, arguments: PropertyName..., required: Bool = false) {
warning(String(format: print.rawValue, arguments: arguments.map { $0.rawValue }), required: required)
}
static func warning(_ print: Print, required: Bool = false) {
warning(print.rawValue, required: required)
}
}
private extension Print {
private static func warning(_ message: String, required: Bool = false) {
static func warning(_ message: String, required: Bool = false) {
let message = "SideMenu Warning: \(message)"
if required {
+6 -20
View File
@@ -9,32 +9,18 @@ import Foundation
internal final class Protected<T: Equatable> {
typealias ConditionBlock = (T) -> Bool
typealias Block = (T) -> Void
typealias ConditionBlock = (_ oldValue: T, T) -> T
private var _value: T
private var conditionBlock: ConditionBlock
private var thenBlock: Block?
private var elseBlock: Block?
private var condition: ConditionBlock
public var value: T {
get {
return _value
}
set {
guard conditionBlock(_value) else {
elseBlock?(_value)
return
}
_value = newValue
thenBlock?(_value)
}
get { return _value }
set { _value = condition(_value, newValue) }
}
init(_ value: T, if conditionBlock: @escaping ConditionBlock, then thenBlock: Block? = nil, else elseBlock: Block? = nil) {
init(_ value: T, when condition: @escaping ConditionBlock) {
self._value = value
self.conditionBlock = conditionBlock
self.thenBlock = thenBlock
self.elseBlock = elseBlock
self.condition = condition
}
}
+27 -21
View File
@@ -30,21 +30,14 @@ public class SideMenuManager: NSObject {
var name: String {
switch self {
case .left: return "menuLeftNavigationController"
case .right: return "menuRightNavigationController"
case .left: return "leftMenuNavigationController"
case .right: return "rightMenuNavigationController"
}
}
}
private var _leftMenu: Protected<Menu?> =
Protected(nil,
if: { $0?.isHidden != false },
else: { _ in Print.warning(.menuInUse, arguments: PresentDirection.left.name, required: true) } )
private var _rightMenu: Protected<Menu?> =
Protected(nil,
if: { $0?.isHidden != false },
else: { _ in Print.warning(.menuInUse, arguments: PresentDirection.right.name, required: true) } )
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
@@ -58,13 +51,23 @@ public class SideMenuManager: NSObject {
/// The left menu.
open var leftMenuNavigationController: UISideMenuNavigationController? {
get { return _leftMenu.value }
get {
if _leftMenu.value?.isHidden == true {
_leftMenu.value?.leftSide = true
}
return _leftMenu.value
}
set(menu) { _leftMenu.value = menu }
}
/// The right menu.
open var rightMenuNavigationController: UISideMenuNavigationController? {
get { return _rightMenu.value }
get {
if _rightMenu.value?.isHidden == true {
_rightMenu.value?.leftSide = false
}
return _rightMenu.value
}
set(menu) { _rightMenu.value = menu }
}
@@ -123,6 +126,14 @@ internal extension SideMenuManager {
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 {
@@ -193,10 +204,8 @@ private extension SideMenuManager {
screenEdgeGestureRecognizer.edges == edge {
view.removeGestureRecognizer(screenEdgeGestureRecognizer)
}
return SideMenuScreenEdgeGestureRecognizer {
return SideMenuScreenEdgeGestureRecognizer(addTo: view, target: self, action: #selector(handlePresentMenuScreenEdge(_:))).with {
$0.edges = edge
$0.addTarget(self, action: #selector(handlePresentMenuScreenEdge(_:)))
view.addGestureRecognizer($0)
}
}
@@ -204,13 +213,10 @@ private extension SideMenuManager {
if let panGestureRecognizer = view.gestureRecognizers?.first(where: { $0 is SideMenuPanGestureRecognizer }) as? SideMenuPanGestureRecognizer {
return panGestureRecognizer
}
return SideMenuPanGestureRecognizer {
$0.addTarget(self, action: #selector(handlePresentMenuPan(_:)))
view.addGestureRecognizer($0)
}
return SideMenuPanGestureRecognizer(addTo: view, target: self, action: #selector(handlePresentMenuPan(_:)))
}
private var activeViewController: UIViewController? {
var activeViewController: UIViewController? {
return UIApplication.shared.keyWindow?.rootViewController?.activeViewController
}
}
@@ -39,7 +39,8 @@ public struct SideMenuSettings: MenuModel {
public var dismissOnPush: Bool = true
public var dismissOnRotation: Bool = true
public var dismissWhenBackgrounded: Bool = true
public var enableSwipeGestures: Bool = true
public var enableSwipeToDismissGesture: Bool = true
public var enableTapToDismissGesture: Bool = true
public var initialSpringVelocity: CGFloat = 1
public var menuWidth: CGFloat = {
let appScreenRect = UIApplication.shared.keyWindow?.bounds ?? UIWindow().bounds
@@ -67,14 +68,14 @@ internal typealias Menu = UISideMenuNavigationController
@objcMembers
open class UISideMenuNavigationController: UINavigationController {
private enum PropertyName: String { case
leftSide
private lazy var _leftSide = Protected(false) { [weak self] oldValue, newValue in
guard self?.isHidden != false else {
Print.warning(.property, arguments: .leftSide, required: true)
return oldValue
}
return newValue
}
private lazy var _leftSide = Protected(false,
if: { [weak self] _ in self?.isHidden != false },
else: { _ in Menu.elseCondition(.leftSide) } )
private weak var _sideMenuManager: SideMenuManager?
private weak var foundViewController: UIViewController?
private weak var interactionController: SideMenuInteractionController?
@@ -87,7 +88,9 @@ open class UISideMenuNavigationController: UINavigationController {
internal weak var sideMenuDelegate: UISideMenuNavigationControllerDelegate?
/// The swipe to dismiss gesture.
private(set) weak var swipeToDismissGesture: UIPanGestureRecognizer? = nil
open private(set) weak var swipeToDismissGesture: UIPanGestureRecognizer? = nil
/// The tap to dismiss gesture.
open private(set) weak var tapToDismissGesture: UITapGestureRecognizer? = nil
open var sideMenuManager: SideMenuManager {
get { return _sideMenuManager ?? SideMenuManager.default }
@@ -106,9 +109,12 @@ open class UISideMenuNavigationController: UINavigationController {
open var settings = SideMenuSettings() {
didSet {
setupBlur()
if !enableSwipeGestures {
if !enableSwipeToDismissGesture {
removeSwipeGesture()
}
if !enableTapToDismissGesture {
removeTapGesture()
}
}
}
@@ -170,19 +176,19 @@ open class UISideMenuNavigationController: UINavigationController {
containerView.addSubview(view)
}
if dismissOnPresent && !isBeingDismissed {
if !isBeingDismissed && dismissOnPresent {
// We're presenting a view controller from the menu, so we need to hide the menu so it isn't showing when the presented view is dismissed.
transitionController?.transition(presenting: false, animated: animated, alongsideTransition: { [weak self] in
guard let self = self else { return }
self.activeDelegate?.sideMenuWillDisappear?(menu: self, animated: animated)
}, complete: false, completion: { [weak self] _ in
guard let self = self else { return }
self.activeDelegate?.sideMenuDidDisappear?(menu: self, animated: animated)
self.view.isHidden = true
}, complete: false, completion: { [weak self] _ in
guard let self = self else { return }
self.activeDelegate?.sideMenuDidDisappear?(menu: self, animated: animated)
self.view.isHidden = true
})
} else {
activeDelegate?.sideMenuWillDisappear?(menu: self, animated: animated)
}
activeDelegate?.sideMenuWillDisappear?(menu: self, animated: animated)
}
override open func viewDidDisappear(_ animated: Bool) {
@@ -317,9 +323,14 @@ extension UISideMenuNavigationController: MenuModel {
set { settings.dismissWhenBackgrounded = newValue }
}
@IBInspectable open var enableSwipeGestures: Bool {
get { return settings.enableSwipeGestures }
set { settings.enableSwipeGestures = newValue }
@IBInspectable open var enableSwipeToDismissGesture: Bool {
get { return settings.enableSwipeToDismissGesture }
set { settings.enableSwipeToDismissGesture = newValue }
}
@IBInspectable open var enableTapToDismissGesture: Bool {
get { return settings.enableTapToDismissGesture }
set { settings.enableTapToDismissGesture = newValue }
}
@IBInspectable open var initialSpringVelocity: CGFloat {
@@ -413,12 +424,8 @@ extension UISideMenuNavigationController: SideMenuTransitionControllerDelegate {
internal func sideMenuTransitionController(_ transitionController: SideMenuTransitionController, didPresent viewController: UIViewController) {
removeSwipeGesture()
swipeToDismissGesture = addDismissPanGesture(to: view.superview)
let tapGestureRecognizer = UITapGestureRecognizer()
tapGestureRecognizer.addTarget(self, action: #selector(handleDismissMenuTap(_:)))
tapGestureRecognizer.cancelsTouchesInView = false
view.superview?.addGestureRecognizer(tapGestureRecognizer)
swipeToDismissGesture = addSwipeToDismissGesture(to: view.superview)
tapToDismissGesture = addTapToDismissGesture(to: view.superview)
}
}
@@ -548,10 +555,6 @@ private extension UISideMenuNavigationController {
}
}
private class func elseCondition(_ propertyName: PropertyName) {
Print.warning(.property, arguments: propertyName.rawValue, required: true)
}
weak var activeDelegate: UISideMenuNavigationControllerDelegate? {
guard !view.isHidden else { return nil }
if let sideMenuDelegate = sideMenuDelegate {
@@ -634,6 +637,12 @@ private extension UISideMenuNavigationController {
}
}
func removeTapGesture() {
if let tapToDismissGesture = tapToDismissGesture {
tapToDismissGesture.view?.removeGestureRecognizer(tapToDismissGesture)
}
}
func registerForNotifications() {
NotificationCenter.default.removeObserver(self)
@@ -660,17 +669,23 @@ private extension UISideMenuNavigationController {
}
}
@discardableResult func addDismissPanGesture(to view: UIView?) -> UIPanGestureRecognizer? {
guard enableSwipeGestures, let view = view else { return nil }
return UIPanGestureRecognizer {
@discardableResult func addSwipeToDismissGesture(to view: UIView?) -> UIPanGestureRecognizer? {
guard enableSwipeToDismissGesture else { return nil }
return UIPanGestureRecognizer(addTo: view, target: self, action: #selector(handleDismissMenuPan(_:)))?.with {
$0.cancelsTouchesInView = false
}
}
@discardableResult func addTapToDismissGesture(to view: UIView?) -> UITapGestureRecognizer? {
guard enableTapToDismissGesture else { return nil }
return UITapGestureRecognizer(addTo: view, target: self, action: #selector(handleDismissMenuTap(_:)))?.with {
$0.cancelsTouchesInView = false
$0.addTarget(self, action: #selector(handleDismissMenuPan(_:)))
view.addGestureRecognizer($0)
}
}
@objc func handleDismissMenuTap(_ tap: UITapGestureRecognizer) {
guard view.window?.hitTest(tap.location(in: nil), with: nil) == view.superview else { return }
let hitTest = view.window?.hitTest(tap.location(in: view.superview), with: nil)
guard hitTest == view.superview else { return }
dismissMenu()
}
+20 -18
View File
@@ -137,11 +137,11 @@ dismiss(animated: true, completion: nil)
To use gestures you have to use the `SideMenuManager`. In your `AppDelegate` do something like this:
``` swift
// Define the menus
let menuLeftNavigationController = UISideMenuNavigationController(rootViewController: YourViewController)
SideMenuManager.default.menuLeftNavigationController = menuLeftNavigationController
let leftMenuNavigationController = UISideMenuNavigationController(rootViewController: YourViewController)
SideMenuManager.default.leftMenuNavigationController = leftMenuNavigationController
let menuRightNavigationController = UISideMenuNavigationController(rootViewController: YourViewController)
SideMenuManager.default.menuRightNavigationController = menuRightNavigationController
let rightMenuNavigationController = UISideMenuNavigationController(rootViewController: YourViewController)
SideMenuManager.default.rightMenuNavigationController = rightMenuNavigationController
// Setup gestures: the left and/or right menus must be set up (above) for these to work.
// Note that these continue to work on the Navigation Controller independent of the view controller it displays!
@@ -149,9 +149,9 @@ SideMenuManager.default.addPanGestureToPresent(toView: self.navigationController
SideMenuManager.default.addScreenEdgePanGesturesToPresent(toView: self.navigationController!.view)
// (Optional) Prevent status bar area from turning black when menu appears:
menuLeftNavigationController.statusBarEndAlpha = 0
leftMenuNavigationController.statusBarEndAlpha = 0
// Copy all settings to the other menu
menuRightNavigationController.settings = menuLeftNavigationController.settings
rightMenuNavigationController.settings = leftMenuNavigationController.settings
```
That's it.
### Customization
@@ -159,9 +159,9 @@ That's it.
`SideMenuManager` supports the following:
``` swift
/// The left menu.
open var menuLeftNavigationController: UISideMenuNavigationController?
open var leftMenuNavigationController: UISideMenuNavigationController?
/// The right menu.
public var menuRightNavigationController: UISideMenuNavigationController?
public var rightMenuNavigationController: UISideMenuNavigationController?
/**
Adds screen edge gestures for both left and right sides to a view to present a menu.
@@ -217,8 +217,10 @@ var dismissOnPush: Bool = true
var dismissOnRotation: Bool = true
/// Automatically dismisses the menu when app goes to the background.
var dismissWhenBackgrounded: Bool = true
/// Enable or disable gestures that would swipe to dismiss the menu. Default is true.
var enableSwipeGestures: Bool = true
/// Enable or disable a swipe gesture that dismisses the menu. Will not be triggered when `presentingViewControllerUserInteractionEnabled` is set to true. Default is true.
var enableSwipeToDismissGesture: Bool = true
/// Enable or disable a tap gesture that dismisses the menu. Will not be triggered when `presentingViewControllerUserInteractionEnabled` is set to true. Default is true.
var enableTapToDismissGesture: Bool = true
/// The animation initial spring velocity when a menu is displayed. Ignored when displayed with a gesture.
var initialSpringVelocity: CGFloat = 1
/// Whether the menu appears on the right or left side of the screen. Right is the default. This property cannot be changed after the menu has loaded.
@@ -256,21 +258,21 @@ var isHidden: Bool
There are 8 pre-defined `SideMenuPresentStyle` options:
``` swift
/// Menu slides in over the existing view.
static let menuSlideIn = SideMenuPresentStyle
static let menuSlideIn: SideMenuPresentStyle
/// The existing view slides out to reveal the menu underneath.
static let viewSlideOut = SideMenuPresentStyle
static let viewSlideOut: SideMenuPresentStyle
/// The existing view slides out while the menu slides in.
static let viewSlideOutMenuIn = SideMenuPresentStyle
static let viewSlideOutMenuIn: SideMenuPresentStyle
/// The menu dissolves in over the existing view.
static let menuDissolveIn = SideMenuPresentStyle
static let menuDissolveIn: SideMenuPresentStyle
/// The existing view slides out while the menu partially slides in.
static let viewSlideOutMenuPartialIn = SideMenuPresentStyle
static let viewSlideOutMenuPartialIn: SideMenuPresentStyle
/// The existing view slides out while the menu slides out from under it.
static let viewSlideOutMenuOut = SideMenuPresentStyle
static let viewSlideOutMenuOut: SideMenuPresentStyle
/// The existing view slides out while the menu partially slides out from under it.
static let viewSlideOutMenuPartialOut = SideMenuPresentStyle
static let viewSlideOutMenuPartialOut: SideMenuPresentStyle
/// The existing view slides out and shrinks to reveal the menu underneath.
static let viewSlideOutMenuZoom = SideMenuPresentStyle
static let viewSlideOutMenuZoom: SideMenuPresentStyle
```
#### UISideMenuNavigationControllerDelegate
To receive notifications when a menu is displayed from a view controller, have it adhere to the `UISideMenuNavigationControllerDelegate` protocol:
+1 -1
View File
@@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = "SideMenu"
s.version = "6.0.7"
s.version = "6.1.0"
s.summary = "Simple side menu control for iOS in Swift inspired by Facebook. Right and Left sides. No coding required."
# This description is used to generate tags and improve search results.