Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f4e9ee214 | |||
| bac8e1591d | |||
| 4ed14e7856 | |||
| 60157eb001 | |||
| 743deb7e03 | |||
| 8f26bbb2d9 | |||
| 192ef476bd | |||
| 8fbf4002ac | |||
| 63e0b2b11b | |||
| f371f0a7ee | |||
| fbb692cd61 | |||
| 78e6655363 | |||
| a18d25d409 | |||
| 422fbb72b0 | |||
| adb5f861e4 | |||
| 89d724cca0 | |||
| 08145abacb | |||
| b37bae8a02 | |||
| 52960fac95 | |||
| 72d998b184 | |||
| f29ad608f7 | |||
| 3ff92c69e6 | |||
| c3a50dac9b | |||
| 93dacf7b1d | |||
| 5ff4caff44 | |||
| 4656a951a2 | |||
| 18e1f751e8 | |||
| 513d9603b2 | |||
| 142d57e84c | |||
| f15081a31b | |||
| 4c04c841c3 | |||
| f6bf6972e9 | |||
| 02f643a667 | |||
| a1716035c3 | |||
| 99456afaed | |||
| 4b92f5a776 | |||
| 459a4a1e9f | |||
| 717fd3a325 | |||
| 802146708e | |||
| f34452d040 |
@@ -4,14 +4,16 @@ Thank you for your interest in SideMenu!
|
||||
|
||||
I have received a surprising amount of questions about SideMenu since putting it up here. A few people in the community have identified some problems and helped contribute to SideMenu to make it better for everyone and I'm truly grateful for the support! Keep them coming!
|
||||
|
||||
I have also received a number of questions about people having issues implementing SideMenu, mostly from beginners learning how to code. As much as I would love to help all of you, I simply do **not** have the time to teach you. That's why I spent a lot of time putting together a detailed [README](https://github.com/jonkykong/SideMenu/blob/master/README.md), adding comments about usage in code, and providing a [demo project](https://github.com/jonkykong/SideMenu/tree/master/Example). These will give you all the information you need to work through any problem and also save me the time it takes to personally respond. **It is faster for you to figure it out for yourself instead of waiting for me to respond to you.**
|
||||
I have also received a number of questions about people having issues implementing SideMenu, mostly from beginners learning how to code. As much as I would love to help all of you, I simply do **not** have the time to teach you.
|
||||
|
||||
I spent a lot of time putting together a detailed [README](https://github.com/jonkykong/SideMenu/blob/master/README.md), adding comments about usage in code, and provided a [demo project](https://github.com/jonkykong/SideMenu/tree/master/Example). These will give you all the information you need to work through any problem, **saving _you_ the time it takes for me to personally respond.**
|
||||
|
||||
### If your question begins with _"How do I..."_
|
||||
- It's **not** a bug. Stay persistent, try a few different things, and you will figure it out! I also recommend searching and posting your questions on [stackoverflow.com](stackoverflow.com). Or, if you're interested in hiring me to teach you, please email me using the email address listed at the top of my [README](https://github.com/jonkykong/SideMenu/blob/master/README.md).
|
||||
- It's **not** a bug. Go back through the [README](https://github.com/jonkykong/SideMenu/blob/master/README.md). Check out the [demo project](https://github.com/jonkykong/SideMenu/tree/master/Example). Stay persistent, try a few different things, and you will figure it out! **It is generally faster for you to figure it out for yourself instead of waiting for me to respond to you.** I also recommend searching and posting your questions on [stackoverflow.com](stackoverflow.com). If you're interested in hiring me to teach you, please email me using the email address listed at the top of my [README](https://github.com/jonkykong/SideMenu/blob/master/README.md).
|
||||
|
||||
### If your question begins with _"Can I..."_
|
||||
- That's a **new feature request**. I am no longer investing my personal time to implement one-off features because SideMenu currently meets the majority of people's needs with the features it already has. However, this is a great opportunity for you to contribute to this open source project! Feel free to open an issue to ask any clarifying questions for your new feature before you start building. Open a [pull request](https://github.com/jonkykong/SideMenu/pull/new/master) when you're ready for me to merge it.
|
||||
|
||||
**Again**, please do **not** email me or open any issues if you want to know how to use SideMenu or are having trouble getting it to work. However, if you think you found a bug, open an issue and I will respond to it as quickly as I can. You should be able to demonstrate the bug in the [demo project](https://github.com/jonkykong/SideMenu/tree/master/Example) which has a minimal amount of code.
|
||||
**Again**, please do **not** email me or open any issues if you want to know how to use SideMenu or are having trouble getting it to work. I am not **tech support**. I am a **teacher**. However, if you think you found a bug, open an issue and I will respond to it when I find time. You must be able to demonstrate the bug in the [demo project](https://github.com/jonkykong/SideMenu/tree/master/Example) which has a minimal amount of code.
|
||||
|
||||
Thanks again for your support and for being respectful of my time.
|
||||
### Thanks again for your support and for being respectful of my time.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!--- Provide a general summary of your changes in the Title above -->
|
||||
<!--- IF YOU DELETE OR IGNORE THIS TEMPLATE WHEN OPENING A NEW ISSUE, YOUR ISSUE WILL BE IGNORED -->
|
||||
## New Issue Checklist
|
||||
<!--- Please complete all of the checks below before submitting a new issue -->
|
||||
<!--- Please complete all of the checks below before submitting a new issue (complete a check by marking it [x] with no spaces) -->
|
||||
I have read the [guidelines for contributing](https://github.com/jonkykong/SideMenu/blob/master/.github/CONTRIBUTING.md) and I understand:
|
||||
- [ ] My issue was **not** solved in the [README](https://github.com/jonkykong/SideMenu/blob/master/README.md).
|
||||
- [ ] My issue can **not** be answered on [stackoverflow.com](stackoverflow.com).
|
||||
|
||||
@@ -18,11 +18,20 @@
|
||||
|
||||
open class SideMenuManager : NSObject {
|
||||
|
||||
@objc public enum MenuPushStyle : Int {
|
||||
case defaultBehavior,
|
||||
popWhenPossible,
|
||||
replace,
|
||||
preserve,
|
||||
preserveAndHideBackButton,
|
||||
subMenu
|
||||
}
|
||||
|
||||
@objc public enum MenuPresentMode : Int {
|
||||
case menuSlideIn
|
||||
case viewSlideOut
|
||||
case viewSlideInOut
|
||||
case menuDissolveIn
|
||||
case menuSlideIn,
|
||||
viewSlideOut,
|
||||
viewSlideInOut,
|
||||
menuDissolveIn
|
||||
}
|
||||
|
||||
// Bounds which has been allocated for the app on the whole device screen
|
||||
@@ -31,6 +40,19 @@ open class SideMenuManager : NSObject {
|
||||
return appWindowRect
|
||||
}
|
||||
|
||||
/**
|
||||
The push style of the menu.
|
||||
|
||||
There are six modes in MenuPushStyle:
|
||||
- defaultBehavior: The view controller is pushed onto the stack.
|
||||
- popWhenPossible: If a view controller already in the stack is of the same class as the pushed view controller, the stack is instead popped back to the existing view controller. This behavior can help users from getting lost in a deep navigation stack.
|
||||
- preserve: If a view controller already in the stack is of the same class as the pushed view controller, the existing view controller is pushed to the end of the stack. This behavior is similar to a UITabBarController.
|
||||
- preserveAndHideBackButton: Same as .preserve and back buttons are automatically hidden.
|
||||
- replace: Any existing view controllers are released from the stack and replaced with the pushed view controller. Back buttons are automatically hidden. This behavior is ideal if view controllers require a lot of memory or their state doesn't need to be preserved..
|
||||
- subMenu: Unlike all other behaviors that push using the menu's presentingViewController, this behavior pushes view controllers within the menu. Use this behavior if you want to display a sub menu.
|
||||
*/
|
||||
open static var menuPushStyle: MenuPushStyle = .defaultBehavior
|
||||
|
||||
/**
|
||||
The presentation mode of the menu.
|
||||
|
||||
@@ -45,17 +67,17 @@ open class SideMenuManager : NSObject {
|
||||
/// Prevents the same view controller (or a view controller of the same class) from being pushed more than once. Defaults to true.
|
||||
open static var menuAllowPushOfSameClassTwice = true
|
||||
|
||||
/// Pops to any view controller already in the navigation stack instead of the view controller being pushed if they share the same class. Defaults to false.
|
||||
open static var menuAllowPopIfPossible = false
|
||||
|
||||
/// Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is 75% of the screen width.
|
||||
open static var menuWidth: CGFloat = max(round(min((appScreenRect.width), (appScreenRect.height)) * 0.75), 240)
|
||||
|
||||
/// Duration of the animation when the menu is presented without gestures. Default is 0.35 seconds.
|
||||
open static var menuAnimationPresentDuration = 0.35
|
||||
open static var menuAnimationPresentDuration: Double = 0.35
|
||||
|
||||
/// Duration of the animation when the menu is dismissed without gestures. Default is 0.35 seconds.
|
||||
open static var menuAnimationDismissDuration = 0.35
|
||||
open static var menuAnimationDismissDuration: Double = 0.35
|
||||
|
||||
/// Duration of the remaining animation when the menu is partially dismissed with gestures. Default is 0.2 seconds.
|
||||
open static var menuAnimationCompleteGestureDuration: Double = 0.20
|
||||
|
||||
/// Amount to fade the existing view controller when the menu is presented. Default is 0 for no fade. Set to 1 to fade completely.
|
||||
open static var menuAnimationFadeStrength: CGFloat = 0
|
||||
@@ -90,11 +112,53 @@ open class SideMenuManager : NSObject {
|
||||
/// Draws the `menuAnimationBackgroundColor` behind the status bar. Default is true.
|
||||
open static var menuFadeStatusBar = true
|
||||
|
||||
/// When true, pushViewController called within the menu it will push the new view controller inside of the menu. Otherwise, it is pushed on the menu's presentingViewController. Default is false.
|
||||
open static var menuAllowSubmenus: Bool = false
|
||||
/// The animation options when a menu is displayed. Ignored when displayed with a gesture.
|
||||
open static var menuAnimationOptions: UIViewAnimationOptions = .curveEaseInOut
|
||||
|
||||
/// When true, pushViewController will replace the last view controller in the navigation controller's viewController stack instead of appending to it. This makes menus similar to tab bar controller behavior.
|
||||
open static var menuReplaceOnPush: Bool = false
|
||||
/// The animation spring damping when a menu is displayed. Ignored when displayed with a gesture.
|
||||
open static var menuAnimationUsingSpringWithDamping: CGFloat = 1
|
||||
|
||||
/// The animation initial spring velocity when a menu is displayed. Ignored when displayed with a gesture.
|
||||
open static var menuAnimationInitialSpringVelocity: CGFloat = 1
|
||||
|
||||
/// -Warning: Deprecated. Use `menuPushStyle = .subMenu` instead.
|
||||
@available(*, deprecated, renamed: "menuPushStyle", message: "Use `menuPushStyle = .subMenu` instead.")
|
||||
open static var menuAllowSubmenus: Bool {
|
||||
get {
|
||||
return menuPushStyle == .subMenu
|
||||
}
|
||||
set {
|
||||
if newValue {
|
||||
menuPushStyle = .subMenu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// -Warning: Deprecated. Use `menuPushStyle = .popWhenPossible` instead.
|
||||
@available(*, deprecated, renamed: "menuPushStyle", message: "Use `menuPushStyle = .popWhenPossible` instead.")
|
||||
open static var menuAllowPopIfPossible: Bool {
|
||||
get {
|
||||
return menuPushStyle == .popWhenPossible
|
||||
}
|
||||
set {
|
||||
if newValue {
|
||||
menuPushStyle = .popWhenPossible
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// -Warning: Deprecated. Use `menuPushStyle = .replace` instead.
|
||||
@available(*, deprecated, renamed: "menuPushStyle", message: "Use `menuPushStyle = .replace` instead.")
|
||||
open static var menuReplaceOnPush: Bool {
|
||||
get {
|
||||
return menuPushStyle == .replace
|
||||
}
|
||||
set {
|
||||
if newValue {
|
||||
menuPushStyle = .replace
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// -Warning: Deprecated. Use `menuAnimationTransformScaleFactor` instead.
|
||||
@available(*, deprecated, renamed: "menuAnimationTransformScaleFactor")
|
||||
@@ -264,6 +328,10 @@ open class SideMenuManager : NSObject {
|
||||
leftScreenEdgeGestureRecognizer.cancelsTouchesInView = true
|
||||
toView.addGestureRecognizer(leftScreenEdgeGestureRecognizer)
|
||||
array.append(leftScreenEdgeGestureRecognizer)
|
||||
|
||||
if SideMenuManager.menuLeftNavigationController == nil {
|
||||
print("SideMenu Warning: menuAddScreenEdgePanGesturesToPresent for the left side was called before menuLeftNavigationController has been defined. The gesture will not work without a menu.")
|
||||
}
|
||||
}
|
||||
|
||||
if forMenu != .left {
|
||||
@@ -273,6 +341,10 @@ open class SideMenuManager : NSObject {
|
||||
rightScreenEdgeGestureRecognizer.cancelsTouchesInView = true
|
||||
toView.addGestureRecognizer(rightScreenEdgeGestureRecognizer)
|
||||
array.append(rightScreenEdgeGestureRecognizer)
|
||||
|
||||
if SideMenuManager.menuRightNavigationController == nil {
|
||||
print("SideMenu Warning: menuAddScreenEdgePanGesturesToPresent for the right side was called before menuRightNavigationController has been defined. The gesture will not work without a menu.")
|
||||
}
|
||||
}
|
||||
|
||||
return array
|
||||
@@ -290,6 +362,10 @@ open class SideMenuManager : NSObject {
|
||||
panGestureRecognizer.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handlePresentMenuPan(_:)))
|
||||
toView.addGestureRecognizer(panGestureRecognizer)
|
||||
|
||||
if SideMenuManager.menuLeftNavigationController ?? SideMenuManager.menuRightNavigationController == nil {
|
||||
print("SideMenu Warning: menuAddPanGestureToPresent called before menuLeftNavigationController or menuRightNavigationController have been defined. Gestures will not work without a menu.")
|
||||
}
|
||||
|
||||
return panGestureRecognizer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {
|
||||
open class SideMenuTransition: UIPercentDrivenInteractiveTransition {
|
||||
|
||||
fileprivate var presenting = false
|
||||
fileprivate var interactive = false
|
||||
fileprivate static weak var originalSuperview: UIView?
|
||||
fileprivate static weak var activeGesture: UIGestureRecognizer?
|
||||
fileprivate static var switchMenus = false
|
||||
|
||||
internal static let singleton = SideMenuTransition()
|
||||
@@ -85,6 +86,14 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
}
|
||||
|
||||
internal class func handlePresentMenuPan(_ pan: UIPanGestureRecognizer) {
|
||||
if activeGesture == nil {
|
||||
activeGesture = pan
|
||||
} else if pan != activeGesture {
|
||||
pan.isEnabled = false
|
||||
pan.isEnabled = true
|
||||
return
|
||||
}
|
||||
|
||||
// how much distance have we panned in reference to the parent view?
|
||||
guard let view = viewControllerForPresentedMenu != nil ? viewControllerForPresentedMenu?.view : pan.view else {
|
||||
return
|
||||
@@ -109,6 +118,8 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
let visibleViewController = visibleViewController {
|
||||
singleton.interactive = true
|
||||
visibleViewController.present(menuViewController, animated: true, completion: nil)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,13 +152,23 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
singleton.update(0.9999)
|
||||
}
|
||||
singleton.finish()
|
||||
activeGesture = nil
|
||||
} else {
|
||||
singleton.cancel()
|
||||
activeGesture = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class func handleHideMenuPan(_ pan: UIPanGestureRecognizer) {
|
||||
if activeGesture == nil {
|
||||
activeGesture = pan
|
||||
} else if pan != activeGesture {
|
||||
pan.isEnabled = false
|
||||
pan.isEnabled = true
|
||||
return
|
||||
}
|
||||
|
||||
let translation = pan.translation(in: pan.view!)
|
||||
let direction:CGFloat = SideMenuTransition.presentDirection == .left ? -1 : 1
|
||||
let distance = translation.x / SideMenuManager.menuWidth * direction
|
||||
@@ -168,8 +189,10 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
singleton.update(0.9999)
|
||||
}
|
||||
singleton.finish()
|
||||
activeGesture = nil
|
||||
} else {
|
||||
singleton.cancel()
|
||||
activeGesture = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,36 +337,35 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: UIViewControllerAnimatedTransitioning protocol methods
|
||||
}
|
||||
|
||||
extension SideMenuTransition: UIViewControllerAnimatedTransitioning {
|
||||
|
||||
// animate a change from one viewcontroller to another
|
||||
open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
|
||||
// get reference to our fromView, toView and the container view that we should perform the transition in
|
||||
let container = transitionContext.containerView
|
||||
// prevent any other menu gestures from firing
|
||||
container.isUserInteractionEnabled = false
|
||||
|
||||
if let menuBackgroundColor = SideMenuManager.menuAnimationBackgroundColor {
|
||||
container.backgroundColor = menuBackgroundColor
|
||||
}
|
||||
|
||||
// create a tuple of our screens
|
||||
let screens : (from:UIViewController, to:UIViewController) = (transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!, transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!)
|
||||
let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
|
||||
let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
|
||||
|
||||
// assign references to our menu view controller and the 'bottom' view controller from the tuple
|
||||
// remember that our menuViewController will alternate between the from and to view controller depending if we're presenting or dismissing
|
||||
let menuViewController = (!presenting ? screens.from : screens.to)
|
||||
let topViewController = !presenting ? screens.to : screens.from
|
||||
let menuViewController = presenting ? toViewController : fromViewController
|
||||
let topViewController = presenting ? fromViewController : toViewController
|
||||
|
||||
let menuView = menuViewController.view!
|
||||
let topView = topViewController.view!
|
||||
|
||||
// prepare menu items to slide in
|
||||
if presenting {
|
||||
var tapView: UIView?
|
||||
if !SideMenuManager.menuPresentingViewControllerUserInteractionEnabled {
|
||||
tapView = UIView()
|
||||
SideMenuTransition.tapView = tapView
|
||||
}
|
||||
|
||||
SideMenuTransition.originalSuperview = topView.superview
|
||||
|
||||
// add the both views to our view controller
|
||||
@@ -351,17 +373,11 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
case .viewSlideOut, .viewSlideInOut:
|
||||
container.addSubview(menuView)
|
||||
container.addSubview(topView)
|
||||
if let tapView = tapView {
|
||||
topView.addSubview(tapView)
|
||||
}
|
||||
case .menuSlideIn, .menuDissolveIn:
|
||||
container.addSubview(topView)
|
||||
if let tapView = tapView {
|
||||
container.addSubview(tapView)
|
||||
}
|
||||
container.addSubview(menuView)
|
||||
}
|
||||
|
||||
|
||||
if SideMenuManager.menuFadeStatusBar {
|
||||
let statusBarView = UIView()
|
||||
SideMenuTransition.statusBarView = statusBarView
|
||||
@@ -371,28 +387,16 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
SideMenuTransition.hideMenuStart()
|
||||
}
|
||||
|
||||
let enableTapViewGestures = { (enable: Bool) in
|
||||
guard let gestures = SideMenuTransition.tapView?.gestureRecognizers else {
|
||||
return
|
||||
}
|
||||
|
||||
for gesture in gestures {
|
||||
gesture.isEnabled = enable
|
||||
}
|
||||
}
|
||||
|
||||
// perform the animation!
|
||||
let duration = transitionDuration(using: transitionContext)
|
||||
let options: UIViewAnimationOptions = interactive ? .curveLinear : UIViewAnimationOptions()
|
||||
UIView.animate(withDuration: duration, delay: 0, options: options, animations: { () -> Void in
|
||||
let animate = {
|
||||
if self.presenting {
|
||||
SideMenuTransition.presentMenuStart()
|
||||
} else {
|
||||
SideMenuTransition.hideMenuStart()
|
||||
}
|
||||
menuView.isUserInteractionEnabled = false
|
||||
enableTapViewGestures(false)
|
||||
}) { (finished) -> Void in
|
||||
}
|
||||
|
||||
let complete = {
|
||||
container.isUserInteractionEnabled = true
|
||||
|
||||
// tell our transitionContext object that we've finished animating
|
||||
if transitionContext.transitionWasCancelled {
|
||||
@@ -403,8 +407,6 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
} else {
|
||||
SideMenuTransition.presentMenuComplete()
|
||||
}
|
||||
menuView.isUserInteractionEnabled = true
|
||||
enableTapViewGestures(true)
|
||||
|
||||
transitionContext.completeTransition(false)
|
||||
|
||||
@@ -418,8 +420,12 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
|
||||
if self.presenting {
|
||||
SideMenuTransition.presentMenuComplete()
|
||||
menuView.isUserInteractionEnabled = true
|
||||
enableTapViewGestures(true)
|
||||
if !SideMenuManager.menuPresentingViewControllerUserInteractionEnabled {
|
||||
let tapView = UIView()
|
||||
topView.addSubview(tapView)
|
||||
tapView.frame = topView.bounds
|
||||
SideMenuTransition.tapView = tapView
|
||||
}
|
||||
transitionContext.completeTransition(true)
|
||||
switch SideMenuManager.menuPresentMode {
|
||||
case .viewSlideOut, .viewSlideInOut:
|
||||
@@ -438,14 +444,43 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
transitionContext.completeTransition(true)
|
||||
menuView.removeFromSuperview()
|
||||
}
|
||||
|
||||
// perform the animation!
|
||||
let duration = transitionDuration(using: transitionContext)
|
||||
if interactive {
|
||||
UIView.animate(withDuration: duration,
|
||||
delay: 0,
|
||||
options: .curveLinear,
|
||||
animations: {
|
||||
animate()
|
||||
}, completion: { (finished) in
|
||||
complete()
|
||||
})
|
||||
} else {
|
||||
UIView.animate(withDuration: duration,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: SideMenuManager.menuAnimationUsingSpringWithDamping,
|
||||
initialSpringVelocity: SideMenuManager.menuAnimationInitialSpringVelocity,
|
||||
options: SideMenuManager.menuAnimationOptions,
|
||||
animations: {
|
||||
animate()
|
||||
}) { (finished) -> Void in
|
||||
complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return how many seconds the transiton animation will take
|
||||
open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
if interactive {
|
||||
return SideMenuManager.menuAnimationCompleteGestureDuration
|
||||
}
|
||||
return presenting ? SideMenuManager.menuAnimationPresentDuration : SideMenuManager.menuAnimationDismissDuration
|
||||
}
|
||||
|
||||
// MARK: UIViewControllerTransitioningDelegate protocol methods
|
||||
}
|
||||
|
||||
extension SideMenuTransition: UIViewControllerTransitioningDelegate {
|
||||
|
||||
// return the animator when presenting a viewcontroller
|
||||
// rememeber that an animator (or animation controller) is any object that aheres to the UIViewControllerAnimatedTransitioning protocol
|
||||
|
||||
@@ -21,7 +21,7 @@ open class UISideMenuNavigationController: UINavigationController {
|
||||
}
|
||||
|
||||
/// Whether the menu appears on the right or left side of the screen. Right is the default.
|
||||
@IBInspectable open var leftSide:Bool = false {
|
||||
@IBInspectable open var leftSide: Bool = false {
|
||||
didSet {
|
||||
if isViewLoaded && oldValue != leftSide { // suppress warnings
|
||||
didSetSide()
|
||||
@@ -53,6 +53,10 @@ open class UISideMenuNavigationController: UINavigationController {
|
||||
self.view.isHidden = false
|
||||
})
|
||||
}
|
||||
|
||||
if topViewController == nil {
|
||||
print("SideMenu Warning: the menu doesn't have a view controller to show! UISideMenuNavigationController needs a view controller to display just like a UINavigationController.")
|
||||
}
|
||||
}
|
||||
|
||||
override open func viewWillDisappear(_ animated: Bool) {
|
||||
@@ -119,7 +123,7 @@ open class UISideMenuNavigationController: UINavigationController {
|
||||
}
|
||||
|
||||
override open func pushViewController(_ viewController: UIViewController, animated: Bool) {
|
||||
guard viewControllers.count > 0 && !SideMenuManager.menuAllowSubmenus else {
|
||||
guard viewControllers.count > 0 && SideMenuManager.menuPushStyle != .subMenu else {
|
||||
// NOTE: pushViewController is called by init(rootViewController: UIViewController)
|
||||
// so we must perform the normal super method in this case.
|
||||
super.pushViewController(viewController, animated: animated)
|
||||
@@ -140,30 +144,47 @@ open class UISideMenuNavigationController: UINavigationController {
|
||||
self.visibleViewController?.viewWillAppear(false) // Hack: force selection to get cleared on UITableViewControllers when reappearing using custom transitions
|
||||
})
|
||||
|
||||
let areAnimationsEnabled = UIView.areAnimationsEnabled
|
||||
UIView.setAnimationsEnabled(true)
|
||||
UIView.animate(withDuration: SideMenuManager.menuAnimationDismissDuration, animations: { () -> Void in
|
||||
SideMenuTransition.hideMenuStart()
|
||||
})
|
||||
UIView.setAnimationsEnabled(areAnimationsEnabled)
|
||||
|
||||
if SideMenuManager.menuAllowPopIfPossible {
|
||||
for subViewController in navigationController.viewControllers {
|
||||
if let lastViewController = navigationController.viewControllers.last, !SideMenuManager.menuAllowPushOfSameClassTwice && type(of: lastViewController) == type(of: viewController) {
|
||||
CATransaction.commit()
|
||||
return
|
||||
}
|
||||
|
||||
switch SideMenuManager.menuPushStyle {
|
||||
case .subMenu, .defaultBehavior: break // .subMenu handled earlier, .defaultBehavior falls through to end
|
||||
case .popWhenPossible:
|
||||
for subViewController in navigationController.viewControllers.reversed() {
|
||||
if type(of: subViewController) == type(of: viewController) {
|
||||
navigationController.popToViewController(subViewController, animated: animated)
|
||||
CATransaction.commit()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if SideMenuManager.menuReplaceOnPush {
|
||||
case .preserve, .preserveAndHideBackButton:
|
||||
var viewControllers = navigationController.viewControllers
|
||||
viewControllers.removeLast()
|
||||
viewControllers.append(viewController)
|
||||
navigationController.setViewControllers(viewControllers, animated: animated)
|
||||
CATransaction.commit()
|
||||
return
|
||||
}
|
||||
|
||||
if let lastViewController = navigationController.viewControllers.last, !SideMenuManager.menuAllowPushOfSameClassTwice && type(of: lastViewController) == type(of: viewController) {
|
||||
let filtered = viewControllers.filter { preservedViewController in type(of: preservedViewController) == type(of: viewController) }
|
||||
if let preservedViewController = filtered.last {
|
||||
viewControllers = viewControllers.filter { subViewController in subViewController !== preservedViewController }
|
||||
if SideMenuManager.menuPushStyle == .preserveAndHideBackButton {
|
||||
preservedViewController.navigationItem.hidesBackButton = true
|
||||
}
|
||||
viewControllers.append(preservedViewController)
|
||||
navigationController.setViewControllers(viewControllers, animated: animated)
|
||||
CATransaction.commit()
|
||||
return
|
||||
}
|
||||
if SideMenuManager.menuPushStyle == .preserveAndHideBackButton {
|
||||
viewController.navigationItem.hidesBackButton = true
|
||||
}
|
||||
case .replace:
|
||||
viewController.navigationItem.hidesBackButton = true
|
||||
navigationController.setViewControllers([viewController], animated: animated)
|
||||
CATransaction.commit()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
[](http://cocoapods.org/pods/SideMenu)
|
||||
|
||||
### If you like SideMenu, give it a ★ at the top right of its [GitHub](https://github.com/jonkykong/SideMenu) page.
|
||||
#### Using SideMenu in your app? [Send](mailto:contact@jonkent.me?subject=SideMenu in action!) me a link to your app in the app store!
|
||||
|
||||
My name is Jon Kent and I'm a freelance iOS designer, developer, and mobile strategist. I love coffee and play the drums. [**Hire me**](mailto:contact@jonkent.me?subject=Let's build something amazing.) to help you make cool stuff. I also have a [website](http://jonkent.me). *Note: If you're having a problem with SideMenu, please open an [issue](https://github.com/jonkykong/SideMenu/issues/new) and do not email me.*
|
||||
> I'm Jon Kent and I freelance iOS design, development, and mobile strategies. I love coffee and play the drums. [**Hire me**](mailto:contact@jonkent.me?subject=Let's build something amazing.) to help you make cool stuff. I also have a [website](http://jonkent.me). *Note: If you're having a problem with SideMenu, please open an [issue](https://github.com/jonkykong/SideMenu/issues/new) and do not email me.*
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -19,11 +20,10 @@ SideMenu is a simple and versatile side menu control written in Swift.
|
||||
* Menus can be presented and dismissed the same as any other View Controller since this control uses custom transitions.
|
||||
|
||||
Check out the example project to see it in action!
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
### Preview Samples
|
||||
| Slide Out | Slide In | Dissolve | Slide In + Out |
|
||||
| --- | --- | --- | --- |
|
||||
|  |  |  |  |
|
||||
|
||||
## Requirements
|
||||
* iOS 8 or higher
|
||||
@@ -96,11 +96,15 @@ In your View Controller's `viewDidLoad` event, do something like this:
|
||||
// Define the menus
|
||||
let menuLeftNavigationController = UISideMenuNavigationController()
|
||||
menuLeftNavigationController.leftSide = true
|
||||
// UISideMenuNavigationController is a subclass of UINavigationController, so do any additional configuration of it here like setting its viewControllers.
|
||||
// UISideMenuNavigationController is a subclass of UINavigationController, so do any additional configuration
|
||||
// of it here like setting its viewControllers. If you're using storyboards, you'll want to do something like:
|
||||
// let menuLeftNavigationController = storyboard!.instantiateViewController(withIdentifier: "LeftMenuNavigationController") as! UISideMenuNavigationController
|
||||
SideMenuManager.menuLeftNavigationController = menuLeftNavigationController
|
||||
|
||||
let menuRightNavigationController = UISideMenuNavigationController()
|
||||
// UISideMenuNavigationController is a subclass of UINavigationController, so do any additional configuration of it here like setting its viewControllers.
|
||||
// UISideMenuNavigationController is a subclass of UINavigationController, so do any additional configuration
|
||||
// of it here like setting its viewControllers. If you're using storyboards, you'll want to do something like:
|
||||
// let menuRightNavigationController = storyboard!.instantiateViewController(withIdentifier: "RightMenuNavigationController") as! UISideMenuNavigationController
|
||||
SideMenuManager.menuRightNavigationController = menuRightNavigationController
|
||||
|
||||
// Enable gestures. The left and/or right menus must be set up above for these to work.
|
||||
@@ -112,6 +116,9 @@ Then from a button, do something like this:
|
||||
``` swift
|
||||
present(SideMenuManager.menuLeftNavigationController!, animated: true, completion: nil)
|
||||
|
||||
// Similarly, to dismiss a menu programmatically, you would do this:
|
||||
dismiss(animated: true, completion: nil)
|
||||
|
||||
// For Swift 2.3, use:
|
||||
// presentViewController(SideMenuManager.menuLeftNavigationController!, animated: true, completion: nil)
|
||||
```
|
||||
@@ -119,6 +126,19 @@ That's it.
|
||||
### Customization
|
||||
Just type `SideMenuManager.menu...` and code completion will show you everything you can customize (defaults are shown below for reference):
|
||||
``` swift
|
||||
/**
|
||||
The push style of the menu.
|
||||
|
||||
There are six modes in MenuPushStyle:
|
||||
- defaultBehavior: The view controller is pushed onto the stack.
|
||||
- popWhenPossible: If a view controller already in the stack is of the same class as the pushed view controller, the stack is instead popped back to the existing view controller. This behavior can help users from getting lost in a deep navigation stack.
|
||||
- preserve: If a view controller already in the stack is of the same class as the pushed view controller, the existing view controller is pushed to the end of the stack. This behavior is similar to a UITabBarController.
|
||||
- preserveAndHideBackButton: Same as .preserve and back buttons are automatically hidden.
|
||||
- replace: Any existing view controllers are released from the stack and replaced with the pushed view controller. Back buttons are automatically hidden. This behavior is ideal if view controllers require a lot of memory or their state doesn't need to be preserved..
|
||||
- subMenu: Unlike all other behaviors that push using the menu's presentingViewController, this behavior pushes view controllers within the menu. Use this behavior if you want to display a sub menu.
|
||||
*/
|
||||
open static var menuPushStyle: MenuPushStyle = .defaultBehavior
|
||||
|
||||
/**
|
||||
The presentation mode of the menu.
|
||||
|
||||
@@ -133,9 +153,6 @@ open static var menuPresentMode: MenuPresentMode = .viewSlideOut
|
||||
/// Prevents the same view controller (or a view controller of the same class) from being pushed more than once. Defaults to true.
|
||||
open static var menuAllowPushOfSameClassTwice = true
|
||||
|
||||
/// Pops to any view controller already in the navigation stack instead of the view controller being pushed if they share the same class. Defaults to false.
|
||||
open static var menuAllowPopIfPossible = false
|
||||
|
||||
/// Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is 75% of the screen width.
|
||||
open static var menuWidth: CGFloat = max(round(min((appScreenRect.width), (appScreenRect.height)) * 0.75), 240)
|
||||
|
||||
@@ -181,11 +198,14 @@ open static var menuParallaxStrength: Int = 0
|
||||
/// Draws the `menuAnimationBackgroundColor` behind the status bar. Default is true.
|
||||
open static var menuFadeStatusBar = true
|
||||
|
||||
/// When true, pushViewController called within the menu it will push the new view controller inside of the menu. Otherwise, it is pushed on the menu's presentingViewController. Default is false.
|
||||
open static var menuAllowSubmenus: Bool = false
|
||||
/// The animation options when a menu is displayed. Ignored when displayed with a gesture.
|
||||
open static var menuAnimationOptions: UIViewAnimationOptions = .curveEaseInOut
|
||||
|
||||
/// When true, pushViewController will replace the last view controller in the navigation controller's viewController stack instead of appending to it. This makes menus similar to tab bar controller behavior.
|
||||
open static var menuReplaceOnPush: Bool = false
|
||||
/// The animation spring damping when a menu is displayed. Ignored when displayed with a gesture.
|
||||
open static var menuAnimationUsingSpringWithDamping: CGFloat = 1
|
||||
|
||||
/// The animation initial spring velocity when a menu is displayed. Ignored when displayed with a gesture.
|
||||
open static var menuAnimationInitialSpringVelocity: CGFloat = 1
|
||||
|
||||
/**
|
||||
The blur effect style of the menu if the menu's root view controller is a UITableViewController or UICollectionViewController.
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "SideMenu"
|
||||
s.version = "2.1.4"
|
||||
s.version = "2.2.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.
|
||||
|
||||
Reference in New Issue
Block a user