Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 90878cc0d5 | |||
| 3f78b9bb49 | |||
| 6f8f224e00 | |||
| bc37f1b9cb | |||
| 8aff2b3627 | |||
| 135a3d2a94 | |||
| a10b94e351 | |||
| e1bf7c4991 | |||
| b19c93ff2c | |||
| 3968686410 | |||
| 0a457b414e | |||
| 5abf5ca740 | |||
| 498669bf58 | |||
| 159c87871b |
@@ -291,7 +291,7 @@
|
||||
<!--Side Menu Navigation Controller-->
|
||||
<scene sceneID="Zbc-0f-8nT">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="LeftMenuNavigationController" navigationBarHidden="YES" id="DuX-EW-0mP" customClass="UISideMenuNavigationController" customModule="SideMenu" sceneMemberID="viewController">
|
||||
<navigationController storyboardIdentifier="LeftMenuNavigationController" navigationBarHidden="YES" id="DuX-EW-0mP" customClass="SideMenuNavigationController" customModule="SideMenu" sceneMemberID="viewController">
|
||||
<navigationItem key="navigationItem" id="ipz-Lx-Wgf"/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="35F-wh-r6h">
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
@@ -423,7 +423,7 @@
|
||||
<!--Side Menu Navigation Controller-->
|
||||
<scene sceneID="kei-0w-mFw">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="RightMenuNavigationController" navigationBarHidden="YES" id="z7k-fk-pfc" customClass="UISideMenuNavigationController" customModule="SideMenu" sceneMemberID="viewController">
|
||||
<navigationController storyboardIdentifier="RightMenuNavigationController" navigationBarHidden="YES" id="z7k-fk-pfc" customClass="SideMenuNavigationController" customModule="SideMenu" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="qOd-yQ-2i8">
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
|
||||
@@ -98,7 +98,7 @@ class MainViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
extension MainViewController: UISideMenuNavigationControllerDelegate {
|
||||
extension MainViewController: SideMenuNavigationControllerDelegate {
|
||||
|
||||
func sideMenuWillAppear(menu: SideMenuNavigationController, animated: Bool) {
|
||||
print("SideMenu Appearing! (animated: \(animated))")
|
||||
|
||||
@@ -20,43 +20,43 @@ extension SideMenuManager {
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuPresentMode: SideMenuPresentationStyle {
|
||||
get { return .viewSlideOut }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuPushStyle: SideMenuPushStyle {
|
||||
get { return .default }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuAllowPushOfSameClassTwice: Bool {
|
||||
get { return true }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuWidth: CGFloat {
|
||||
get { return 0 }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuAnimationPresentDuration: Double {
|
||||
get { return 0.35 }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuAnimationDismissDuration: Double {
|
||||
get { return 0.35 }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuAnimationCompleteGestureDuration: Double {
|
||||
get { return 0.35 }
|
||||
set {}
|
||||
@@ -98,7 +98,7 @@ extension SideMenuManager {
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuPresentingViewControllerUserInteractionEnabled: Bool {
|
||||
get { return false }
|
||||
set {}
|
||||
@@ -110,73 +110,73 @@ extension SideMenuManager {
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuFadeStatusBar: Bool {
|
||||
get { return true }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuAnimationOptions: UIView.AnimationOptions {
|
||||
get { return .curveEaseInOut }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuAnimationCompletionCurve: UIView.AnimationCurve {
|
||||
get { return .easeIn }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuAnimationUsingSpringWithDamping: CGFloat {
|
||||
get { return 1 }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuAnimationInitialSpringVelocity: CGFloat {
|
||||
get { return 1 }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuDismissOnPush: Bool {
|
||||
get { return true }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuAlwaysAnimate: Bool {
|
||||
get { return false }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuDismissWhenBackgrounded: Bool {
|
||||
get { return true }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuBlurEffectStyle: UIBlurEffect.Style? {
|
||||
get { return nil }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer? {
|
||||
get { return nil }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer? {
|
||||
get { return nil }
|
||||
set {}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "This property has been moved to the UISideMenuNavigationController class.")
|
||||
@available(*, deprecated, message: "This property has been moved to the SideMenuNavigationController class.")
|
||||
public var menuEnableSwipeGestures: Bool {
|
||||
get { return true }
|
||||
set {}
|
||||
@@ -209,3 +209,6 @@ extension SideMenuPresentationStyle {
|
||||
|
||||
@available(*, deprecated, renamed: "SideMenuNavigationController")
|
||||
public typealias UISideMenuNavigationController = SideMenuNavigationController
|
||||
|
||||
@available(*, deprecated, renamed: "SideMenuNavigationControllerDelegate")
|
||||
public typealias UISideMenuNavigationControllerDelegate = SideMenuNavigationControllerDelegate
|
||||
|
||||
@@ -63,6 +63,10 @@ internal extension UIGestureRecognizer {
|
||||
guard let view = view else { return nil }
|
||||
self.init(addTo: view, target: target, action: action)
|
||||
}
|
||||
|
||||
func remove() {
|
||||
view?.removeGestureRecognizer(self)
|
||||
}
|
||||
}
|
||||
|
||||
internal extension UIPanGestureRecognizer {
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
//
|
||||
// Models.swift
|
||||
// SideMenu
|
||||
//
|
||||
// Created by Jon Kent on 7/3/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
internal protocol MenuModel: TransitionModel {
|
||||
/// Prevents the same view controller (or a view controller of the same class) from being pushed more than once. Defaults to true.
|
||||
var allowPushOfSameClassTwice: Bool { get }
|
||||
/// Forces menus to always animate when appearing or disappearing, regardless of a pushed view controller's animation.
|
||||
var alwaysAnimate: Bool { get }
|
||||
/**
|
||||
The blur effect style of the menu if the menu's root view controller is a UITableViewController or UICollectionViewController.
|
||||
|
||||
- Note: If you want cells in a UITableViewController menu to show vibrancy, make them a subclass of UITableViewVibrantCell.
|
||||
*/
|
||||
var blurEffectStyle: UIBlurEffect.Style? { get }
|
||||
/// Animation curve of the remaining animation when the menu is partially dismissed with gestures. Default is .easeIn.
|
||||
var completionCurve: UIView.AnimationCurve { get }
|
||||
/// Automatically dismisses the menu when another view is presented from it.
|
||||
var dismissOnPresent: Bool { get }
|
||||
/// Automatically dismisses the menu when another view controller is pushed from it.
|
||||
var dismissOnPush: Bool { get }
|
||||
/// Automatically dismisses the menu when the screen is rotated.
|
||||
var dismissOnRotation: Bool { get }
|
||||
/// Automatically dismisses the menu when app goes to the background.
|
||||
var dismissWhenBackgrounded: 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.
|
||||
|
||||
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.
|
||||
*/
|
||||
var pushStyle: SideMenuPushStyle { get }
|
||||
}
|
||||
|
||||
internal protocol TransitionModel: PresentationModel {
|
||||
/// The animation options when a menu is displayed. Ignored when displayed with a gesture.
|
||||
var animationOptions: UIView.AnimationOptions { get }
|
||||
/// Duration of the remaining animation when the menu is partially dismissed with gestures. Default is 0.35 seconds.
|
||||
var completeGestureDuration: Double { get }
|
||||
/// Duration of the animation when the menu is dismissed without gestures. Default is 0.35 seconds.
|
||||
var dismissDuration: Double { get }
|
||||
/// The animation initial spring velocity when a menu is displayed. Ignored when displayed with a gesture.
|
||||
var initialSpringVelocity: CGFloat { get }
|
||||
/// Duration of the animation when the menu is presented without gestures. Default is 0.35 seconds.
|
||||
var presentDuration: Double { get }
|
||||
/// The animation spring damping when a menu is displayed. Ignored when displayed with a gesture.
|
||||
var usingSpringWithDamping: CGFloat { get }
|
||||
}
|
||||
|
||||
internal protocol PresentationModel {
|
||||
/// Draws `presentStyle.backgroundColor` behind the status bar. Default is 1.
|
||||
var statusBarEndAlpha: CGFloat { get }
|
||||
/// Enable or disable interaction with the presenting view controller while the menu is displayed. Enabling may make it difficult to dismiss the menu or cause exceptions if the user tries to present and already presented menu. `presentingViewControllerUseSnapshot` must also set to false. Default is false.
|
||||
var presentingViewControllerUserInteractionEnabled: Bool { get }
|
||||
/// Use a snapshot for the presenting vierw controller while the menu is displayed. Useful when layout changes occur during transitions. Not recommended for apps that support rotation. Default is false.
|
||||
var presentingViewControllerUseSnapshot: Bool { get }
|
||||
/// The presentation style of the menu.
|
||||
var presentationStyle: SideMenuPresentationStyle { get }
|
||||
/// Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is zero.
|
||||
var menuWidth: CGFloat { get }
|
||||
}
|
||||
@@ -9,8 +9,8 @@ import Foundation
|
||||
|
||||
internal enum Print: String { case
|
||||
cannotPush = "Attempt to push a View Controller from %@ where its navigationController == nil. It must be embedded in a UINavigationController for this to work.",
|
||||
emptyMenu = "The menu doesn't have a view controller to show! UISideMenuNavigationController needs a view controller to display just like a UINavigationController.",
|
||||
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.",
|
||||
emptyMenu = "The menu doesn't have a view controller to show! SideMenuNavigationController needs a view controller to display just like a UINavigationController.",
|
||||
menuAlreadyAssigned = "%@ was already assigned to the %@ of %@. When using multiple SideMenuManagers you may want to use new instances of SideMenuNavigationController 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.",
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
//
|
||||
// SideMenuAnimationController.swift
|
||||
// SideMenu
|
||||
//
|
||||
// Created by Jon Kent on 10/24/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
internal protocol AnimationModel {
|
||||
/// The animation options when a menu is displayed. Ignored when displayed with a gesture.
|
||||
var animationOptions: UIView.AnimationOptions { get }
|
||||
/// Duration of the remaining animation when the menu is partially dismissed with gestures. Default is 0.35 seconds.
|
||||
var completeGestureDuration: Double { get }
|
||||
/// Duration of the animation when the menu is dismissed without gestures. Default is 0.35 seconds.
|
||||
var dismissDuration: Double { get }
|
||||
/// The animation initial spring velocity when a menu is displayed. Ignored when displayed with a gesture.
|
||||
var initialSpringVelocity: CGFloat { get }
|
||||
/// Duration of the animation when the menu is presented without gestures. Default is 0.35 seconds.
|
||||
var presentDuration: Double { get }
|
||||
/// The animation spring damping when a menu is displayed. Ignored when displayed with a gesture.
|
||||
var usingSpringWithDamping: CGFloat { get }
|
||||
}
|
||||
|
||||
internal protocol SideMenuAnimationControllerDelegate: class {
|
||||
func sideMenuAnimationController(_ animationController: SideMenuAnimationController, didDismiss viewController: UIViewController)
|
||||
func sideMenuAnimationController(_ animationController: SideMenuAnimationController, didPresent viewController: UIViewController)
|
||||
}
|
||||
|
||||
internal final class SideMenuAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
|
||||
typealias Model = AnimationModel & PresentationModel
|
||||
|
||||
private var config: Model
|
||||
private weak var containerView: UIView?
|
||||
private let leftSide: Bool
|
||||
private var presentationController: SideMenuPresentationController!
|
||||
private unowned var presentedViewController: UIViewController?
|
||||
private unowned var presentingViewController: UIViewController?
|
||||
weak var delegate: SideMenuAnimationControllerDelegate?
|
||||
|
||||
init(config: Model, leftSide: Bool, delegate: SideMenuAnimationControllerDelegate? = nil) {
|
||||
self.config = config
|
||||
self.leftSide = leftSide
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard
|
||||
let presentedViewController = transitionContext.presentedViewController,
|
||||
let presentingViewController = transitionContext.presentingViewController
|
||||
else { return }
|
||||
|
||||
if transitionContext.isPresenting {
|
||||
self.containerView = transitionContext.containerView
|
||||
self.presentedViewController = presentedViewController
|
||||
self.presentingViewController = presentingViewController
|
||||
self.presentationController = SideMenuPresentationController(
|
||||
config: config,
|
||||
leftSide: leftSide,
|
||||
presentedViewController: presentedViewController,
|
||||
presentingViewController: presentingViewController,
|
||||
containerView: transitionContext.containerView
|
||||
)
|
||||
}
|
||||
|
||||
transition(using: transitionContext)
|
||||
}
|
||||
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
guard let transitionContext = transitionContext else { return 0 }
|
||||
return duration(presenting: transitionContext.isPresenting, interactive: transitionContext.isInteractive)
|
||||
}
|
||||
|
||||
func animationEnded(_ transitionCompleted: Bool) {
|
||||
guard let presentedViewController = presentedViewController else { return }
|
||||
if presentedViewController.isHidden {
|
||||
delegate?.sideMenuAnimationController(self, didDismiss: presentedViewController)
|
||||
} else {
|
||||
delegate?.sideMenuAnimationController(self, didPresent: presentedViewController)
|
||||
}
|
||||
}
|
||||
|
||||
func transition(presenting: Bool, animated: Bool = true, interactive: Bool = false, alongsideTransition: (() -> Void)? = nil, complete: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||
transitionWillBegin(presenting: presenting)
|
||||
transition(presenting: presenting,
|
||||
animated: animated,
|
||||
interactive: interactive,
|
||||
animations: { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.transition(presenting: presenting)
|
||||
alongsideTransition?()
|
||||
}, completion: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
if complete {
|
||||
self.transitionDidEnd(presenting: presenting, completed: true)
|
||||
}
|
||||
completion?(true)
|
||||
})
|
||||
}
|
||||
|
||||
func layout() {
|
||||
presentationController.containerViewWillLayoutSubviews()
|
||||
}
|
||||
}
|
||||
|
||||
private extension SideMenuAnimationController {
|
||||
|
||||
func duration(presenting: Bool, interactive: Bool) -> Double {
|
||||
if interactive { return config.completeGestureDuration }
|
||||
return presenting ? config.presentDuration : config.dismissDuration
|
||||
}
|
||||
|
||||
func transitionWillBegin(presenting: Bool) {
|
||||
// prevent any other menu gestures from firing
|
||||
containerView?.isUserInteractionEnabled = false
|
||||
|
||||
if presenting {
|
||||
presentationController.presentationTransitionWillBegin()
|
||||
} else {
|
||||
presentationController.dismissalTransitionWillBegin()
|
||||
}
|
||||
}
|
||||
|
||||
func transition(presenting: Bool) {
|
||||
if presenting {
|
||||
presentationController.presentationTransition()
|
||||
} else {
|
||||
presentationController.dismissalTransition()
|
||||
}
|
||||
}
|
||||
|
||||
func transitionDidEnd(presenting: Bool, completed: Bool) {
|
||||
if presenting {
|
||||
presentationController.presentationTransitionDidEnd(completed)
|
||||
} else {
|
||||
presentationController.dismissalTransitionDidEnd(completed)
|
||||
}
|
||||
|
||||
containerView?.isUserInteractionEnabled = true
|
||||
}
|
||||
|
||||
func transition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
transitionWillBegin(presenting: transitionContext.isPresenting)
|
||||
transition(presenting: transitionContext.isPresenting,
|
||||
animated: transitionContext.isAnimated,
|
||||
interactive: transitionContext.isInteractive,
|
||||
animations: { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.transition(presenting: transitionContext.isPresenting)
|
||||
}, completion: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
let completed = !transitionContext.transitionWasCancelled
|
||||
self.transitionDidEnd(presenting: transitionContext.isPresenting, completed: completed)
|
||||
transitionContext.completeTransition(completed)
|
||||
})
|
||||
}
|
||||
|
||||
func transition(presenting: Bool, animated: Bool = true, interactive: Bool = false, animations: @escaping (() -> Void) = {}, completion: @escaping ((Bool) -> Void) = { _ in }) {
|
||||
if !animated {
|
||||
animations()
|
||||
completion(true)
|
||||
return
|
||||
}
|
||||
|
||||
let duration = self.duration(presenting: presenting, interactive: interactive)
|
||||
if interactive {
|
||||
// IMPORTANT: The non-interactive animation block will not complete if adapted for interactive. The below animation block must be used!
|
||||
UIView.animate(
|
||||
withDuration: duration,
|
||||
delay: duration, // HACK: If zero, the animation briefly flashes in iOS 11.
|
||||
options: .curveLinear,
|
||||
animations: animations,
|
||||
completion: completion
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
UIView.animate(
|
||||
withDuration: duration,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: config.usingSpringWithDamping,
|
||||
initialSpringVelocity: config.initialSpringVelocity,
|
||||
options: config.animationOptions,
|
||||
animations: animations,
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private extension UIViewControllerContextTransitioning {
|
||||
|
||||
var isPresenting: Bool {
|
||||
return viewController(forKey: .from)?.presentedViewController === viewController(forKey: .to)
|
||||
}
|
||||
|
||||
var presentingViewController: UIViewController? {
|
||||
return viewController(forKey: isPresenting ? .from : .to)
|
||||
}
|
||||
|
||||
var presentedViewController: UIViewController? {
|
||||
return viewController(forKey: isPresenting ? .to : .from)
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,6 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
internal protocol SideMenuInteractable {
|
||||
func handle(state: SideMenuInteractionController.State)
|
||||
}
|
||||
|
||||
internal final class SideMenuInteractionController: UIPercentDrivenInteractiveTransition {
|
||||
|
||||
enum State { case
|
||||
@@ -44,20 +40,6 @@ internal final class SideMenuInteractionController: UIPercentDrivenInteractiveTr
|
||||
guard !isCancelled && !isFinished else { return }
|
||||
super.update(percentComplete)
|
||||
}
|
||||
}
|
||||
|
||||
private extension SideMenuInteractionController {
|
||||
|
||||
@objc func handleNotification(notification: NSNotification) {
|
||||
switch notification.name {
|
||||
case UIApplication.didEnterBackgroundNotification:
|
||||
cancel()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SideMenuInteractionController: SideMenuInteractable {
|
||||
|
||||
func handle(state: State) {
|
||||
switch state {
|
||||
@@ -70,3 +52,14 @@ extension SideMenuInteractionController: SideMenuInteractable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension SideMenuInteractionController {
|
||||
|
||||
@objc func handleNotification(notification: NSNotification) {
|
||||
switch notification.name {
|
||||
case UIApplication.didEnterBackgroundNotification:
|
||||
cancel()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ private extension SideMenuManager {
|
||||
func addScreenEdgeGesture(to view: UIView, edge: UIRectEdge) -> UIScreenEdgePanGestureRecognizer {
|
||||
if let screenEdgeGestureRecognizer = view.gestureRecognizers?.first(where: { $0 is SideMenuScreenEdgeGestureRecognizer }) as? SideMenuScreenEdgeGestureRecognizer,
|
||||
screenEdgeGestureRecognizer.edges == edge {
|
||||
view.removeGestureRecognizer(screenEdgeGestureRecognizer)
|
||||
screenEdgeGestureRecognizer.remove()
|
||||
}
|
||||
return SideMenuScreenEdgeGestureRecognizer(addTo: view, target: self, action: #selector(handlePresentMenuScreenEdge(_:))).with {
|
||||
$0.edges = edge
|
||||
@@ -221,7 +221,7 @@ private extension SideMenuManager {
|
||||
}
|
||||
}
|
||||
|
||||
extension SideMenuManager: UISideMenuNavigationControllerTransitionDelegate {
|
||||
extension SideMenuManager: SideMenuNavigationControllerTransitionDelegate {
|
||||
|
||||
internal func sideMenuTransitionDidDismiss(menu: Menu) {
|
||||
defer { switching = false }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// UISideMenuNavigationController.swift
|
||||
// SideMenuNavigationController.swift
|
||||
//
|
||||
// Created by Jon Kent on 1/14/16.
|
||||
// Copyright © 2016 Jon Kent. All rights reserved.
|
||||
@@ -16,18 +16,57 @@ import UIKit
|
||||
subMenu
|
||||
}
|
||||
|
||||
@objc public protocol UISideMenuNavigationControllerDelegate {
|
||||
internal protocol MenuModel {
|
||||
/// Prevents the same view controller (or a view controller of the same class) from being pushed more than once. Defaults to true.
|
||||
var allowPushOfSameClassTwice: Bool { get }
|
||||
/// Forces menus to always animate when appearing or disappearing, regardless of a pushed view controller's animation.
|
||||
var alwaysAnimate: Bool { get }
|
||||
/**
|
||||
The blur effect style of the menu if the menu's root view controller is a UITableViewController or UICollectionViewController.
|
||||
|
||||
- Note: If you want cells in a UITableViewController menu to show vibrancy, make them a subclass of UITableViewVibrantCell.
|
||||
*/
|
||||
var blurEffectStyle: UIBlurEffect.Style? { get }
|
||||
/// Animation curve of the remaining animation when the menu is partially dismissed with gestures. Default is .easeIn.
|
||||
var completionCurve: UIView.AnimationCurve { get }
|
||||
/// Automatically dismisses the menu when another view is presented from it.
|
||||
var dismissOnPresent: Bool { get }
|
||||
/// Automatically dismisses the menu when another view controller is pushed from it.
|
||||
var dismissOnPush: Bool { get }
|
||||
/// Automatically dismisses the menu when the screen is rotated.
|
||||
var dismissOnRotation: Bool { get }
|
||||
/// Automatically dismisses the menu when app goes to the background.
|
||||
var dismissWhenBackgrounded: 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.
|
||||
|
||||
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.
|
||||
*/
|
||||
var pushStyle: SideMenuPushStyle { get }
|
||||
}
|
||||
|
||||
@objc public protocol SideMenuNavigationControllerDelegate {
|
||||
@objc optional func sideMenuWillAppear(menu: SideMenuNavigationController, animated: Bool)
|
||||
@objc optional func sideMenuDidAppear(menu: SideMenuNavigationController, animated: Bool)
|
||||
@objc optional func sideMenuWillDisappear(menu: SideMenuNavigationController, animated: Bool)
|
||||
@objc optional func sideMenuDidDisappear(menu: SideMenuNavigationController, animated: Bool)
|
||||
}
|
||||
|
||||
internal protocol UISideMenuNavigationControllerTransitionDelegate: class {
|
||||
internal protocol SideMenuNavigationControllerTransitionDelegate: class {
|
||||
func sideMenuTransitionDidDismiss(menu: Menu)
|
||||
}
|
||||
|
||||
public struct SideMenuSettings: MenuModel, InitializableStruct {
|
||||
public struct SideMenuSettings: MenuModel & PresentationModel & AnimationModel, InitializableStruct {
|
||||
public var allowPushOfSameClassTwice: Bool = true
|
||||
public var alwaysAnimate: Bool = true
|
||||
public var animationOptions: UIView.AnimationOptions = .curveEaseInOut
|
||||
@@ -73,14 +112,12 @@ open class SideMenuNavigationController: UINavigationController {
|
||||
|
||||
private weak var _sideMenuManager: SideMenuManager?
|
||||
private weak var foundViewController: UIViewController?
|
||||
private weak var interactionController: SideMenuInteractionController?
|
||||
private var interactive: Bool = false
|
||||
private var originalBackgroundColor: UIColor?
|
||||
private var rotating: Bool = false
|
||||
private var transitionController: SideMenuTransitionController?
|
||||
|
||||
/// Delegate for receiving appear and disappear related events. If `nil` the visible view controller that displays a `UISideMenuNavigationController` automatically receives these events.
|
||||
internal weak var sideMenuDelegate: UISideMenuNavigationControllerDelegate?
|
||||
/// Delegate for receiving appear and disappear related events. If `nil` the visible view controller that displays a `SideMenuNavigationController` automatically receives these events.
|
||||
public weak var sideMenuDelegate: SideMenuNavigationControllerDelegate?
|
||||
|
||||
/// The swipe to dismiss gesture.
|
||||
open private(set) weak var swipeToDismissGesture: UIPanGestureRecognizer? = nil
|
||||
@@ -105,10 +142,10 @@ open class SideMenuNavigationController: UINavigationController {
|
||||
didSet {
|
||||
setupBlur()
|
||||
if !enableSwipeToDismissGesture {
|
||||
removeSwipeGesture()
|
||||
swipeToDismissGesture?.remove()
|
||||
}
|
||||
if !enableTapToDismissGesture {
|
||||
removeTapGesture()
|
||||
tapToDismissGesture?.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,7 +359,11 @@ open class SideMenuNavigationController: UINavigationController {
|
||||
}
|
||||
|
||||
override open var transitioningDelegate: UIViewControllerTransitioningDelegate? {
|
||||
get { return self }
|
||||
get {
|
||||
transitionController = transitionController ?? SideMenuTransitionController(leftSide: leftSide, config: settings)
|
||||
transitionController?.delegate = self
|
||||
return transitionController
|
||||
}
|
||||
set { Print.warning(.transitioningDelegate, required: true) }
|
||||
}
|
||||
}
|
||||
@@ -452,46 +493,14 @@ extension SideMenuNavigationController: MenuModel {
|
||||
}
|
||||
}
|
||||
|
||||
// IMPORTANT: These methods must be declared open or they will not be called.
|
||||
extension SideMenuNavigationController: UIViewControllerTransitioningDelegate {
|
||||
|
||||
open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
transitionController = SideMenuTransitionController(
|
||||
config: self,
|
||||
leftSide: leftSide,
|
||||
delegate: self)
|
||||
return transitionController
|
||||
}
|
||||
|
||||
open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return transitionController
|
||||
}
|
||||
|
||||
open func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
return interactionController(using: animator)
|
||||
}
|
||||
|
||||
open func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
return interactionController(using: animator)
|
||||
}
|
||||
|
||||
private func interactionController(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
guard interactive else { return nil }
|
||||
interactive = false
|
||||
let interactionController = SideMenuInteractionController(cancelWhenBackgrounded: dismissWhenBackgrounded, completionCurve: completionCurve)
|
||||
self.interactionController = interactionController
|
||||
return interactionController
|
||||
}
|
||||
}
|
||||
|
||||
extension SideMenuNavigationController: SideMenuTransitionControllerDelegate {
|
||||
|
||||
internal func sideMenuTransitionController(_ transitionController: SideMenuTransitionController, didDismiss viewController: UIViewController) {
|
||||
func sideMenuTransitionController(_ transitionController: SideMenuTransitionController, didDismiss viewController: UIViewController) {
|
||||
sideMenuManager.sideMenuTransitionDidDismiss(menu: self)
|
||||
}
|
||||
|
||||
internal func sideMenuTransitionController(_ transitionController: SideMenuTransitionController, didPresent viewController: UIViewController) {
|
||||
removeSwipeGesture()
|
||||
func sideMenuTransitionController(_ transitionController: SideMenuTransitionController, didPresent viewController: UIViewController) {
|
||||
swipeToDismissGesture?.remove()
|
||||
swipeToDismissGesture = addSwipeToDismissGesture(to: view.superview)
|
||||
tapToDismissGesture = addTapToDismissGesture(to: view.superview)
|
||||
}
|
||||
@@ -508,44 +517,44 @@ internal extension SideMenuNavigationController {
|
||||
if !presenting {
|
||||
dismissMenu(interactively: true)
|
||||
}
|
||||
interactionController?.handle(state: .update(progress: progress))
|
||||
transitionController?.handle(state: .update(progress: progress))
|
||||
case .changed:
|
||||
interactionController?.handle(state: .update(progress: progress))
|
||||
transitionController?.handle(state: .update(progress: progress))
|
||||
case .ended:
|
||||
let velocity = gesture.xVelocity * factor(presenting)
|
||||
let finished = velocity >= 100 || velocity >= -50 && abs(progress) >= 0.5
|
||||
interactionController?.handle(state: finished ? .finish : .cancel)
|
||||
transitionController?.handle(state: finished ? .finish : .cancel)
|
||||
default:
|
||||
interactionController?.handle(state: .cancel)
|
||||
transitionController?.handle(state: .cancel)
|
||||
}
|
||||
}
|
||||
|
||||
func cancelMenuPan(_ gesture: UIPanGestureRecognizer) {
|
||||
interactionController?.handle(state: .cancel)
|
||||
transitionController?.handle(state: .cancel)
|
||||
}
|
||||
|
||||
func dismissMenu(animated flag: Bool = true, interactively interactive: Bool = false, completion: (() -> Void)? = nil) {
|
||||
guard !isHidden else { return }
|
||||
self.interactive = interactive
|
||||
transitionController?.interactive = interactive
|
||||
dismiss(animated: flag, completion: completion)
|
||||
}
|
||||
|
||||
// Note: although this method is syntactically reversed it allows the interactive property to scoped privately
|
||||
func presentFrom(_ viewControllerToPresentFrom: UIViewController?, interactively interactive: Bool, completion: (() -> Void)? = nil) {
|
||||
guard let viewControllerToPresentFrom = viewControllerToPresentFrom else { return }
|
||||
self.interactive = interactive
|
||||
guard let viewControllerToPresentFrom = viewControllerToPresentFrom, transitioningDelegate != nil else { return }
|
||||
transitionController?.interactive = interactive
|
||||
viewControllerToPresentFrom.present(self, animated: true, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
private extension SideMenuNavigationController {
|
||||
|
||||
weak var activeDelegate: UISideMenuNavigationControllerDelegate? {
|
||||
weak var activeDelegate: SideMenuNavigationControllerDelegate? {
|
||||
guard !view.isHidden else { return nil }
|
||||
if let sideMenuDelegate = sideMenuDelegate {
|
||||
return sideMenuDelegate
|
||||
}
|
||||
return visibleViewController(from: presentingViewController) as? UISideMenuNavigationControllerDelegate
|
||||
return visibleViewController(from: presentingViewController) as? SideMenuNavigationControllerDelegate
|
||||
}
|
||||
|
||||
func visibleViewController(from: UIViewController?) -> UIViewController? {
|
||||
@@ -616,18 +625,6 @@ private extension SideMenuNavigationController {
|
||||
}
|
||||
}
|
||||
|
||||
func removeSwipeGesture() {
|
||||
if let swipeToDismissGesture = swipeToDismissGesture {
|
||||
swipeToDismissGesture.view?.removeGestureRecognizer(swipeToDismissGesture)
|
||||
}
|
||||
}
|
||||
|
||||
func removeTapGesture() {
|
||||
if let tapToDismissGesture = tapToDismissGesture {
|
||||
tapToDismissGesture.view?.removeGestureRecognizer(tapToDismissGesture)
|
||||
}
|
||||
}
|
||||
|
||||
func registerForNotifications() {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
|
||||
|
||||
@@ -7,6 +7,19 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
internal protocol PresentationModel {
|
||||
/// Draws `presentStyle.backgroundColor` behind the status bar. Default is 1.
|
||||
var statusBarEndAlpha: CGFloat { get }
|
||||
/// Enable or disable interaction with the presenting view controller while the menu is displayed. Enabling may make it difficult to dismiss the menu or cause exceptions if the user tries to present and already presented menu. `presentingViewControllerUseSnapshot` must also set to false. Default is false.
|
||||
var presentingViewControllerUserInteractionEnabled: Bool { get }
|
||||
/// Use a snapshot for the presenting vierw controller while the menu is displayed. Useful when layout changes occur during transitions. Not recommended for apps that support rotation. Default is false.
|
||||
var presentingViewControllerUseSnapshot: Bool { get }
|
||||
/// The presentation style of the menu.
|
||||
var presentationStyle: SideMenuPresentationStyle { get }
|
||||
/// Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is zero.
|
||||
var menuWidth: CGFloat { get }
|
||||
}
|
||||
|
||||
internal protocol SideMenuPresentationControllerDelegate: class {
|
||||
func sideMenuPresentationControllerDidTap(_ presentationController: SideMenuPresentationController)
|
||||
func sideMenuPresentationController(_ presentationController: SideMenuPresentationController, didPanWith gesture: UIPanGestureRecognizer)
|
||||
@@ -221,7 +234,7 @@ private extension SideMenuPresentationController {
|
||||
view.layer.shadowColor = config.presentationStyle.onTopShadowColor.cgColor
|
||||
view.layer.shadowRadius = config.presentationStyle.onTopShadowRadius
|
||||
view.layer.shadowOpacity = config.presentationStyle.onTopShadowOpacity
|
||||
view.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
view.layer.shadowOffset = config.presentationStyle.onTopShadowOffset
|
||||
}
|
||||
|
||||
func addParallax(to view: UIView) {
|
||||
|
||||
@@ -1,187 +1,93 @@
|
||||
//
|
||||
// SideMenuAnimationController.swift
|
||||
// SideMenuTransitioningDelegate.swift
|
||||
// SideMenu
|
||||
//
|
||||
// Created by Jon Kent on 10/24/18.
|
||||
// Created by Jon Kent on 8/29/19.
|
||||
// Copyright © 2019 jonkykong. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Foundation
|
||||
|
||||
internal protocol SideMenuTransitionControllerDelegate: class {
|
||||
func sideMenuTransitionController(_ transitionController: SideMenuTransitionController, didDismiss viewController: UIViewController)
|
||||
func sideMenuTransitionController(_ transitionController: SideMenuTransitionController, didPresent viewController: UIViewController)
|
||||
}
|
||||
|
||||
internal final class SideMenuTransitionController: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
internal final class SideMenuTransitionController: NSObject, UIViewControllerTransitioningDelegate {
|
||||
|
||||
typealias Model = MenuModel & AnimationModel & PresentationModel
|
||||
|
||||
private var config: TransitionModel
|
||||
private weak var containerView: UIView?
|
||||
private let leftSide: Bool
|
||||
private var presentationController: SideMenuPresentationController!
|
||||
private unowned var presentedViewController: UIViewController?
|
||||
private unowned var presentingViewController: UIViewController?
|
||||
private let config: Model
|
||||
private var animationController: SideMenuAnimationController?
|
||||
private weak var interactionController: SideMenuInteractionController?
|
||||
|
||||
var interactive: Bool = false
|
||||
weak var delegate: SideMenuTransitionControllerDelegate?
|
||||
|
||||
init(config: TransitionModel, leftSide: Bool, delegate: SideMenuTransitionControllerDelegate? = nil) {
|
||||
self.config = config
|
||||
init(leftSide: Bool, config: Model) {
|
||||
self.leftSide = leftSide
|
||||
self.delegate = delegate
|
||||
self.config = config
|
||||
super.init()
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard
|
||||
let presentedViewController = transitionContext.presentedViewController,
|
||||
let presentingViewController = transitionContext.presentingViewController
|
||||
else { return }
|
||||
|
||||
if transitionContext.isPresenting {
|
||||
self.containerView = transitionContext.containerView
|
||||
self.presentedViewController = presentedViewController
|
||||
self.presentingViewController = presentingViewController
|
||||
self.presentationController = SideMenuPresentationController(
|
||||
config: config,
|
||||
leftSide: leftSide,
|
||||
presentedViewController: presentedViewController,
|
||||
presentingViewController: presentingViewController,
|
||||
containerView: transitionContext.containerView
|
||||
)
|
||||
}
|
||||
|
||||
transition(using: transitionContext)
|
||||
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
animationController = SideMenuAnimationController(
|
||||
config: config,
|
||||
leftSide: leftSide,
|
||||
delegate: self)
|
||||
return animationController
|
||||
}
|
||||
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
guard let transitionContext = transitionContext else { return 0 }
|
||||
return duration(presenting: transitionContext.isPresenting, interactive: transitionContext.isInteractive)
|
||||
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return animationController
|
||||
}
|
||||
|
||||
func animationEnded(_ transitionCompleted: Bool) {
|
||||
guard let presentedViewController = presentedViewController else { return }
|
||||
if presentedViewController.isHidden {
|
||||
delegate?.sideMenuTransitionController(self, didDismiss: presentedViewController)
|
||||
} else {
|
||||
delegate?.sideMenuTransitionController(self, didPresent: presentedViewController)
|
||||
}
|
||||
func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
return interactionController(using: animator)
|
||||
}
|
||||
|
||||
func transition(presenting: Bool, animated: Bool = true, interactive: Bool = false, alongsideTransition: (() -> Void)? = nil, complete: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||
transitionWillBegin(presenting: presenting)
|
||||
transition(presenting: presenting,
|
||||
animated: animated,
|
||||
interactive: interactive,
|
||||
animations: { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.transition(presenting: presenting)
|
||||
alongsideTransition?()
|
||||
}, completion: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
if complete {
|
||||
self.transitionDidEnd(presenting: presenting, completed: true)
|
||||
}
|
||||
completion?(true)
|
||||
})
|
||||
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
return interactionController(using: animator)
|
||||
}
|
||||
|
||||
internal func handle(state: SideMenuInteractionController.State) {
|
||||
interactionController?.handle(state: state)
|
||||
}
|
||||
|
||||
func layout() {
|
||||
presentationController.containerViewWillLayoutSubviews()
|
||||
animationController?.layout()
|
||||
}
|
||||
|
||||
func transition(presenting: Bool, animated: Bool = true, interactive: Bool = false, alongsideTransition: (() -> Void)? = nil, complete: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||
animationController?.transition(
|
||||
presenting: presenting,
|
||||
animated: animated,
|
||||
interactive: interactive,
|
||||
alongsideTransition: alongsideTransition,
|
||||
complete: complete, completion: completion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension SideMenuTransitionController: SideMenuAnimationControllerDelegate {
|
||||
|
||||
internal func sideMenuAnimationController(_ animationController: SideMenuAnimationController, didDismiss viewController: UIViewController) {
|
||||
delegate?.sideMenuTransitionController(self, didDismiss: viewController)
|
||||
}
|
||||
|
||||
internal func sideMenuAnimationController(_ animationController: SideMenuAnimationController, didPresent viewController: UIViewController) {
|
||||
delegate?.sideMenuTransitionController(self, didPresent: viewController)
|
||||
}
|
||||
}
|
||||
|
||||
private extension SideMenuTransitionController {
|
||||
|
||||
func duration(presenting: Bool, interactive: Bool) -> Double {
|
||||
if interactive { return config.completeGestureDuration }
|
||||
return presenting ? config.presentDuration : config.dismissDuration
|
||||
}
|
||||
|
||||
func transitionWillBegin(presenting: Bool) {
|
||||
// prevent any other menu gestures from firing
|
||||
containerView?.isUserInteractionEnabled = false
|
||||
|
||||
if presenting {
|
||||
presentationController.presentationTransitionWillBegin()
|
||||
} else {
|
||||
presentationController.dismissalTransitionWillBegin()
|
||||
}
|
||||
}
|
||||
|
||||
func transition(presenting: Bool) {
|
||||
if presenting {
|
||||
presentationController.presentationTransition()
|
||||
} else {
|
||||
presentationController.dismissalTransition()
|
||||
}
|
||||
}
|
||||
|
||||
func transitionDidEnd(presenting: Bool, completed: Bool) {
|
||||
if presenting {
|
||||
presentationController.presentationTransitionDidEnd(completed)
|
||||
} else {
|
||||
presentationController.dismissalTransitionDidEnd(completed)
|
||||
}
|
||||
|
||||
containerView?.isUserInteractionEnabled = true
|
||||
}
|
||||
|
||||
func transition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
transitionWillBegin(presenting: transitionContext.isPresenting)
|
||||
transition(presenting: transitionContext.isPresenting,
|
||||
animated: transitionContext.isAnimated,
|
||||
interactive: transitionContext.isInteractive,
|
||||
animations: { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.transition(presenting: transitionContext.isPresenting)
|
||||
}, completion: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
let completed = !transitionContext.transitionWasCancelled
|
||||
self.transitionDidEnd(presenting: transitionContext.isPresenting, completed: completed)
|
||||
transitionContext.completeTransition(completed)
|
||||
})
|
||||
}
|
||||
|
||||
func transition(presenting: Bool, animated: Bool = true, interactive: Bool = false, animations: @escaping (() -> Void) = {}, completion: @escaping ((Bool) -> Void) = { _ in }) {
|
||||
if !animated {
|
||||
animations()
|
||||
completion(true)
|
||||
return
|
||||
}
|
||||
|
||||
let duration = self.duration(presenting: presenting, interactive: interactive)
|
||||
if interactive {
|
||||
// IMPORTANT: The non-interactive animation block will not complete if adapted for interactive. The below animation block must be used!
|
||||
UIView.animate(
|
||||
withDuration: duration,
|
||||
delay: duration, // HACK: If zero, the animation briefly flashes in iOS 11.
|
||||
options: .curveLinear,
|
||||
animations: animations,
|
||||
completion: completion
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
UIView.animate(
|
||||
withDuration: duration,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: config.usingSpringWithDamping,
|
||||
initialSpringVelocity: config.initialSpringVelocity,
|
||||
options: config.animationOptions,
|
||||
animations: animations,
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private extension UIViewControllerContextTransitioning {
|
||||
|
||||
var isPresenting: Bool {
|
||||
return viewController(forKey: .from)?.presentedViewController === viewController(forKey: .to)
|
||||
}
|
||||
|
||||
var presentingViewController: UIViewController? {
|
||||
return viewController(forKey: isPresenting ? .from : .to)
|
||||
}
|
||||
|
||||
var presentedViewController: UIViewController? {
|
||||
return viewController(forKey: isPresenting ? .to : .from)
|
||||
func interactionController(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
guard interactive else { return nil }
|
||||
interactive = false
|
||||
let interactionController = SideMenuInteractionController(cancelWhenBackgrounded: config.dismissWhenBackgrounded, completionCurve: config.completionCurve)
|
||||
self.interactionController = interactionController
|
||||
return interactionController
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+10
-10
@@ -8,7 +8,6 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
032FEB01E4C88E7C87032FCF9780FC7C /* SideMenuNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F8B09C72B577EFDB20A3EFF4F0C668A /* SideMenuNavigationController.swift */; };
|
||||
0D2D410A6087D4E0A54642165913EF30 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F10B146FA49E99D07F0FA0E38ABE8F /* Models.swift */; };
|
||||
0F9F3CABB81269CF711A73BAD21E7976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */; };
|
||||
287FA1360FD3EF72D700C54856C685C1 /* SideMenuManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FE7B8C8447DD35D435B2AF4A23E221C /* SideMenuManager.swift */; };
|
||||
31E79D7BA42208D00E73C18CBAF1598E /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B25193EDCA447F119795F68F75BF05 /* Extensions.swift */; };
|
||||
@@ -17,6 +16,7 @@
|
||||
5B0A5C9933D0FCD08E12CFFB56145333 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */; };
|
||||
614FC39555CB129D97FC24B2D953A427 /* SideMenuPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61AFB3209BCB97295281AC6437FB346B /* SideMenuPresentationController.swift */; };
|
||||
6E6E17C0E8351D23CB4B20332C70BE61 /* Print.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB39D726F07EA27650309A1A3213412 /* Print.swift */; };
|
||||
84278B87231F932A000B9B05 /* SideMenuTransitionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84278B85231F9316000B9B05 /* SideMenuTransitionController.swift */; };
|
||||
8428210422E0540800C6F2D8 /* Deprecations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8428210022E0448700C6F2D8 /* Deprecations.swift */; };
|
||||
89B2CFA98A07964FBD2D7775FF5FB98D /* Pods-Example-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D6D7C498FA339E02BD53ECB8916CEA8E /* Pods-Example-dummy.m */; };
|
||||
A2ACFE2D997BC8F1DA4EE3064A4270DD /* SideMenuInteractionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB64592703EA9A196B5C0F07BA1A918A /* SideMenuInteractionController.swift */; };
|
||||
@@ -25,7 +25,7 @@
|
||||
D7BD58D0FF7BF7E887B6D70D4651D70B /* Pods-Example-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 7825A90E082A1582EB16256B0E722B3F /* Pods-Example-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
DA6F42990A37DF6179D6386008BB87F8 /* SideMenu-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 36C5772477B00653D86343BABD123EF3 /* SideMenu-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F1967413D21BCA08963F09969D47E152 /* SideMenu-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CF977D7FB42D367F2810037EBA59B7D /* SideMenu-dummy.m */; };
|
||||
FCBFE25630B6309DF3213AE0F6F34D1A /* SideMenuTransitionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C3188EE68841BE36716860BE079AF8 /* SideMenuTransitionController.swift */; };
|
||||
FCBFE25630B6309DF3213AE0F6F34D1A /* SideMenuAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C3188EE68841BE36716860BE079AF8 /* SideMenuAnimationController.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -48,7 +48,7 @@
|
||||
1FE7B8C8447DD35D435B2AF4A23E221C /* SideMenuManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SideMenuManager.swift; path = Pod/Classes/SideMenuManager.swift; sourceTree = "<group>"; };
|
||||
243410B9535472556EA4BB6DBC133A0D /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Example.release.xcconfig"; sourceTree = "<group>"; };
|
||||
2CB39D726F07EA27650309A1A3213412 /* Print.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Print.swift; path = Pod/Classes/Print.swift; sourceTree = "<group>"; };
|
||||
30C3188EE68841BE36716860BE079AF8 /* SideMenuTransitionController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SideMenuTransitionController.swift; path = Pod/Classes/SideMenuTransitionController.swift; sourceTree = "<group>"; };
|
||||
30C3188EE68841BE36716860BE079AF8 /* SideMenuAnimationController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SideMenuAnimationController.swift; path = Pod/Classes/SideMenuAnimationController.swift; sourceTree = "<group>"; };
|
||||
319D06AA0D1D0BA345459C039040A1ED /* SideMenu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SideMenu.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
31C1D37707DFAA5E6A164BCC07834264 /* Pods-Example-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Example-Info.plist"; sourceTree = "<group>"; };
|
||||
3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
|
||||
@@ -58,6 +58,7 @@
|
||||
441854E35F81731E63E53DC7E4EEAD9D /* Pods-Example-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Example-acknowledgements.markdown"; sourceTree = "<group>"; };
|
||||
61AFB3209BCB97295281AC6437FB346B /* SideMenuPresentationController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SideMenuPresentationController.swift; path = Pod/Classes/SideMenuPresentationController.swift; sourceTree = "<group>"; };
|
||||
7825A90E082A1582EB16256B0E722B3F /* Pods-Example-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Example-umbrella.h"; sourceTree = "<group>"; };
|
||||
84278B85231F9316000B9B05 /* SideMenuTransitionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SideMenuTransitionController.swift; path = Pod/Classes/SideMenuTransitionController.swift; sourceTree = "<group>"; };
|
||||
8428210022E0448700C6F2D8 /* Deprecations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Deprecations.swift; path = Pod/Classes/Deprecations.swift; sourceTree = "<group>"; };
|
||||
8F8B09C72B577EFDB20A3EFF4F0C668A /* SideMenuNavigationController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SideMenuNavigationController.swift; path = Pod/Classes/SideMenuNavigationController.swift; sourceTree = "<group>"; };
|
||||
9CF977D7FB42D367F2810037EBA59B7D /* SideMenu-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SideMenu-dummy.m"; sourceTree = "<group>"; };
|
||||
@@ -65,7 +66,6 @@
|
||||
AB64592703EA9A196B5C0F07BA1A918A /* SideMenuInteractionController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SideMenuInteractionController.swift; path = Pod/Classes/SideMenuInteractionController.swift; sourceTree = "<group>"; };
|
||||
AFECBC24E09D0D25F822C27BD944AFD4 /* Pods-Example-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Example-frameworks.sh"; sourceTree = "<group>"; };
|
||||
B45138496B85A072654D1D0F8EBBEDE5 /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Example.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
B5F10B146FA49E99D07F0FA0E38ABE8F /* Models.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Models.swift; path = Pod/Classes/Models.swift; sourceTree = "<group>"; };
|
||||
BB1EC6DFBB713FC2F280D596714173EC /* SideMenu.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SideMenu.xcconfig; sourceTree = "<group>"; };
|
||||
BD4CEC04022777F53C1CA2113A43FACE /* SideMenu-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SideMenu-prefix.pch"; sourceTree = "<group>"; };
|
||||
BE262D79CAE897127A1984945DCE9FEE /* SideMenu.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; path = SideMenu.podspec; sourceTree = "<group>"; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
|
||||
@@ -140,15 +140,15 @@
|
||||
8428210022E0448700C6F2D8 /* Deprecations.swift */,
|
||||
F1B25193EDCA447F119795F68F75BF05 /* Extensions.swift */,
|
||||
00F847D27577EF5335081BFA43A0BFA3 /* Initializable.swift */,
|
||||
B5F10B146FA49E99D07F0FA0E38ABE8F /* Models.swift */,
|
||||
2CB39D726F07EA27650309A1A3213412 /* Print.swift */,
|
||||
13846826E721392191757B9033D6F63F /* Protected.swift */,
|
||||
AB64592703EA9A196B5C0F07BA1A918A /* SideMenuInteractionController.swift */,
|
||||
1FE7B8C8447DD35D435B2AF4A23E221C /* SideMenuManager.swift */,
|
||||
30C3188EE68841BE36716860BE079AF8 /* SideMenuAnimationController.swift */,
|
||||
8F8B09C72B577EFDB20A3EFF4F0C668A /* SideMenuNavigationController.swift */,
|
||||
61AFB3209BCB97295281AC6437FB346B /* SideMenuPresentationController.swift */,
|
||||
D154DB98A04C7BD96E4EFBBD3FE0008A /* SideMenuPresentationStyle.swift */,
|
||||
30C3188EE68841BE36716860BE079AF8 /* SideMenuTransitionController.swift */,
|
||||
8F8B09C72B577EFDB20A3EFF4F0C668A /* SideMenuNavigationController.swift */,
|
||||
84278B85231F9316000B9B05 /* SideMenuTransitionController.swift */,
|
||||
3ACA278071ED85D04DA83677D6C60528 /* UITableViewVibrantCell.swift */,
|
||||
);
|
||||
name = SideMenu;
|
||||
@@ -281,7 +281,7 @@
|
||||
LastUpgradeCheck = 1100;
|
||||
TargetAttributes = {
|
||||
0AEE99A309977BD12A049FF48AF9BA4B = {
|
||||
LastSwiftMigration = 1020;
|
||||
LastSwiftMigration = 1030;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -328,7 +328,6 @@
|
||||
8428210422E0540800C6F2D8 /* Deprecations.swift in Sources */,
|
||||
31E79D7BA42208D00E73C18CBAF1598E /* Extensions.swift in Sources */,
|
||||
BD5C0C12B6BB0BF6DA294E095B35E38D /* Initializable.swift in Sources */,
|
||||
0D2D410A6087D4E0A54642165913EF30 /* Models.swift in Sources */,
|
||||
6E6E17C0E8351D23CB4B20332C70BE61 /* Print.swift in Sources */,
|
||||
384CBA8850C8BF3A7AAB3A3D78844B1E /* Protected.swift in Sources */,
|
||||
F1967413D21BCA08963F09969D47E152 /* SideMenu-dummy.m in Sources */,
|
||||
@@ -336,7 +335,8 @@
|
||||
287FA1360FD3EF72D700C54856C685C1 /* SideMenuManager.swift in Sources */,
|
||||
614FC39555CB129D97FC24B2D953A427 /* SideMenuPresentationController.swift in Sources */,
|
||||
D0B45D0F0E7B359807CAFB744FB80CE4 /* SideMenuPresentationStyle.swift in Sources */,
|
||||
FCBFE25630B6309DF3213AE0F6F34D1A /* SideMenuTransitionController.swift in Sources */,
|
||||
FCBFE25630B6309DF3213AE0F6F34D1A /* SideMenuAnimationController.swift in Sources */,
|
||||
84278B87231F932A000B9B05 /* SideMenuTransitionController.swift in Sources */,
|
||||
032FEB01E4C88E7C87032FCF9780FC7C /* SideMenuNavigationController.swift in Sources */,
|
||||
45A647715262A70BB9F2F4E7C1439C58 /* UITableViewVibrantCell.swift in Sources */,
|
||||
);
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
* [Code Implementation](#code-implementation)
|
||||
* **[Customization](#customization)**
|
||||
* [SideMenuManager](#sidemenumanager)
|
||||
* [UISideMenuNavigationController](#uisidemenunavigationcontroller)
|
||||
* [UISideMenuNavigationControllerDelegate](#uisidemenunavigationcontrollerdelegate)
|
||||
* [SideMenuNavigationController](#sidemenunavigationcontroller)
|
||||
* [SideMenuNavigationControllerDelegate](#sidemenunavigationcontrollerdelegate)
|
||||
* [Advanced](#advanced)
|
||||
* [Known Issues](#known-issues)
|
||||
* [Thank You](#thank-you)
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
SideMenu is a simple and versatile side menu control written in Swift.
|
||||
- [x] **It can be implemented in storyboard without a single line of [code](#code-less-storyboard-implementation).**
|
||||
- [x] Four standard animation styles to choose from (there's even a parallax effect if you want to get weird).
|
||||
- [x] Eight standard animation styles to choose from (there's even a parallax effect if you want to get weird).
|
||||
- [x] Highly customizable without needing to write tons of custom code.
|
||||
- [x] Supports continuous swiping between side menus on boths sides in a single gesture.
|
||||
- [x] Global menu configuration. Set-up once and be done for all screens.
|
||||
@@ -104,10 +104,10 @@ github "jonkykong/SideMenu" "master"
|
||||
|
||||
## Usage
|
||||
### Code-less Storyboard Implementation
|
||||
1. Create a Navigation Controller for a side menu. Set the `Custom Class` of the Navigation Controller to be `UISideMenuNavigationController` in the **Identity Inspector**. Set the `Module` to `SideMenu` (ignore this step if you've manually added SideMenu to your project). Create a Root View Controller for the Navigation Controller (shown as a UITableViewController below). Set up any Triggered Segues you want in that view controller.
|
||||
1. Create a Navigation Controller for a side menu. Set the `Custom Class` of the Navigation Controller to be `SideMenuNavigationController` in the **Identity Inspector**. Set the `Module` to `SideMenu` (ignore this step if you've manually added SideMenu to your project). Create a Root View Controller for the Navigation Controller (shown as a UITableViewController below). Set up any Triggered Segues you want in that view controller.
|
||||

|
||||
|
||||
2. Set the `Left Side` property of the `UISideMenuNavigationController` to On if you want it to appear from the left side of the screen, or Off/Default if you want it to appear from the right side.
|
||||
2. Set the `Left Side` property of the `SideMenuNavigationController` to On if you want it to appear from the left side of the screen, or Off/Default if you want it to appear from the right side.
|
||||

|
||||
|
||||
3. Add a UIButton or UIBarButton to a view controller that you want to display the menu from. Set that button's Triggered Segues action to modally present the Navigation Controller from step 1.
|
||||
@@ -123,10 +123,10 @@ import SideMenu
|
||||
From a button, do something like this:
|
||||
``` swift
|
||||
// Define the menu
|
||||
let menu = UISideMenuNavigationController(rootViewController: YourViewController)
|
||||
// UISideMenuNavigationController is a subclass of UINavigationController, so do any additional configuration
|
||||
let menu = SideMenuNavigationController(rootViewController: YourViewController)
|
||||
// SideMenuNavigationController 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 menu = storyboard!.instantiateViewController(withIdentifier: "RightMenu") as! UISideMenuNavigationController
|
||||
// let menu = storyboard!.instantiateViewController(withIdentifier: "RightMenu") as! SideMenuNavigationController
|
||||
present(menu, animated: true, completion: nil)
|
||||
```
|
||||
|
||||
@@ -138,10 +138,10 @@ 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 leftMenuNavigationController = UISideMenuNavigationController(rootViewController: YourViewController)
|
||||
let leftMenuNavigationController = SideMenuNavigationController(rootViewController: YourViewController)
|
||||
SideMenuManager.default.leftMenuNavigationController = leftMenuNavigationController
|
||||
|
||||
let rightMenuNavigationController = UISideMenuNavigationController(rootViewController: YourViewController)
|
||||
let rightMenuNavigationController = SideMenuNavigationController(rootViewController: YourViewController)
|
||||
SideMenuManager.default.rightMenuNavigationController = rightMenuNavigationController
|
||||
|
||||
// Setup gestures: the left and/or right menus must be set up (above) for these to work.
|
||||
@@ -160,9 +160,9 @@ That's it.
|
||||
`SideMenuManager` supports the following:
|
||||
``` swift
|
||||
/// The left menu.
|
||||
open var leftMenuNavigationController: UISideMenuNavigationController?
|
||||
open var leftMenuNavigationController: SideMenuNavigationController?
|
||||
/// The right menu.
|
||||
public var rightMenuNavigationController: UISideMenuNavigationController?
|
||||
public var rightMenuNavigationController: SideMenuNavigationController?
|
||||
/**
|
||||
Adds screen edge gestures for both left and right sides to a view to present a menu.
|
||||
|
||||
@@ -189,8 +189,8 @@ public var rightMenuNavigationController: UISideMenuNavigationController?
|
||||
*/
|
||||
@discardableResult public func addPanGestureToPresent(toView view: UIView) -> UIPanGestureRecognizer
|
||||
```
|
||||
#### UISideMenuNavigationController
|
||||
`UISideMenuNavigationController` supports the following:
|
||||
#### SideMenuNavigationController
|
||||
`SideMenuNavigationController` supports the following:
|
||||
``` swift
|
||||
/// Prevents the same view controller (or a view controller of the same class) from being pushed more than once. Defaults to true.
|
||||
var allowPushOfSameClassTwice: Bool = true
|
||||
@@ -275,29 +275,29 @@ static let viewSlideOutMenuPartialOut: SideMenuPresentStyle
|
||||
/// The existing view slides out and shrinks to reveal the menu underneath.
|
||||
static let viewSlideOutMenuZoom: SideMenuPresentStyle
|
||||
```
|
||||
#### UISideMenuNavigationControllerDelegate
|
||||
To receive notifications when a menu is displayed from a view controller, have it adhere to the `UISideMenuNavigationControllerDelegate` protocol:
|
||||
#### SideMenuNavigationControllerDelegate
|
||||
To receive notifications when a menu is displayed from a view controller, have it adhere to the `SideMenuNavigationControllerDelegate` protocol:
|
||||
``` swift
|
||||
extension MyViewController: UISideMenuNavigationControllerDelegate {
|
||||
extension MyViewController: SideMenuNavigationControllerDelegate {
|
||||
|
||||
func sideMenuWillAppear(menu: UISideMenuNavigationController, animated: Bool) {
|
||||
func sideMenuWillAppear(menu: SideMenuNavigationController, animated: Bool) {
|
||||
print("SideMenu Appearing! (animated: \(animated))")
|
||||
}
|
||||
|
||||
func sideMenuDidAppear(menu: UISideMenuNavigationController, animated: Bool) {
|
||||
func sideMenuDidAppear(menu: SideMenuNavigationController, animated: Bool) {
|
||||
print("SideMenu Appeared! (animated: \(animated))")
|
||||
}
|
||||
|
||||
func sideMenuWillDisappear(menu: UISideMenuNavigationController, animated: Bool) {
|
||||
func sideMenuWillDisappear(menu: SideMenuNavigationController, animated: Bool) {
|
||||
print("SideMenu Disappearing! (animated: \(animated))")
|
||||
}
|
||||
|
||||
func sideMenuDidDisappear(menu: UISideMenuNavigationController, animated: Bool) {
|
||||
func sideMenuDidDisappear(menu: SideMenuNavigationController, animated: Bool) {
|
||||
print("SideMenu Disappeared! (animated: \(animated))")
|
||||
}
|
||||
}
|
||||
```
|
||||
*Note: setting the `sideMenuDelegate` property on `UISideMenuNavigationController` is optional. If your view controller adheres to the protocol then the methods will be called automatically.*
|
||||
*Note: setting the `sideMenuDelegate` property on `SideMenuNavigationController` is optional. If your view controller adheres to the protocol then the methods will be called automatically.*
|
||||
### Advanced
|
||||
<details>
|
||||
<summary>Click for Details</summary>
|
||||
@@ -309,9 +309,9 @@ For simplicity, `SideMenuManager.default` serves as the primary instance as most
|
||||
let customSideMenuManager = SideMenuManager()
|
||||
```
|
||||
2. Setup and display menus with your custom instance the same as you would with the `SideMenuManager.default` instance.
|
||||
3. If using Storyboards, subclass your instance of `UISideMenuNavigationController` and set its `sideMenuManager` property to your custom instance. This must be done before `viewDidLoad` is called:
|
||||
3. If using Storyboards, subclass your instance of `SideMenuNavigationController` and set its `sideMenuManager` property to your custom instance. This must be done before `viewDidLoad` is called:
|
||||
``` swift
|
||||
class MySideMenuNavigationController: UISideMenuNavigationController {
|
||||
class MySideMenuNavigationController: SideMenuNavigationController {
|
||||
|
||||
let customSideMenuManager = SideMenuManager()
|
||||
|
||||
@@ -322,10 +322,10 @@ class MySideMenuNavigationController: UISideMenuNavigationController {
|
||||
}
|
||||
}
|
||||
```
|
||||
Alternatively, you can set `sideMenuManager` from the view controller that segues to your UISideMenuNavigationController:
|
||||
Alternatively, you can set `sideMenuManager` from the view controller that segues to your SideMenuNavigationController:
|
||||
``` swift
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if let sideMenuNavigationController = segue.destination as? UISideMenuNavigationController {
|
||||
if let sideMenuNavigationController = segue.destination as? SideMenuNavigationController {
|
||||
sideMenuNavigationController.sideMenuManager = customSideMenuManager
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "SideMenu"
|
||||
s.version = "6.2.0"
|
||||
s.version = "6.2.5"
|
||||
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.
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
7B552D5D1DCC65830010301C /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B552D5C1DCC65830010301C /* Launch Screen.storyboard */; };
|
||||
7B5FA9B61DCB269700278DF6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B5FA9B51DCB269700278DF6 /* Main.storyboard */; };
|
||||
84276D8A2282929A0095B7C5 /* SideMenuManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84276D892282929A0095B7C5 /* SideMenuManager.swift */; };
|
||||
84278B84231F9126000B9B05 /* SideMenuTransitionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84278B83231F9126000B9B05 /* SideMenuTransitionController.swift */; };
|
||||
84278B89231F948D000B9B05 /* SideMenuAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84278B88231F948D000B9B05 /* SideMenuAnimationController.swift */; };
|
||||
842820EF22DDD1BC00C6F2D8 /* Protected.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842820EE22DDD1BC00C6F2D8 /* Protected.swift */; };
|
||||
842820F622DDD1E800C6F2D8 /* SideMenuPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842820F022DDD1E800C6F2D8 /* SideMenuPresentationController.swift */; };
|
||||
842820F722DDD1E800C6F2D8 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842820F122DDD1E800C6F2D8 /* Models.swift */; };
|
||||
842820F822DDD1E800C6F2D8 /* SideMenuTransitionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842820F222DDD1E800C6F2D8 /* SideMenuTransitionController.swift */; };
|
||||
842820F922DDD1E800C6F2D8 /* Print.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842820F322DDD1E800C6F2D8 /* Print.swift */; };
|
||||
842820FA22DDD1E800C6F2D8 /* Initializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842820F422DDD1E800C6F2D8 /* Initializable.swift */; };
|
||||
842820FB22DDD1E800C6F2D8 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842820F522DDD1E800C6F2D8 /* Extensions.swift */; };
|
||||
@@ -62,9 +62,10 @@
|
||||
7B5FA9B51DCB269700278DF6 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
|
||||
7B9DC9041DC6E8C1000D4007 /* SideMenu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SideMenu.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84276D892282929A0095B7C5 /* SideMenuManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SideMenuManager.swift; path = Pod/Classes/SideMenuManager.swift; sourceTree = "<group>"; };
|
||||
84278B83231F9126000B9B05 /* SideMenuTransitionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SideMenuTransitionController.swift; path = Pod/Classes/SideMenuTransitionController.swift; sourceTree = "<group>"; };
|
||||
84278B88231F948D000B9B05 /* SideMenuAnimationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SideMenuAnimationController.swift; path = Pod/Classes/SideMenuAnimationController.swift; sourceTree = "<group>"; };
|
||||
842820EE22DDD1BC00C6F2D8 /* Protected.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Protected.swift; path = Pod/Classes/Protected.swift; sourceTree = "<group>"; };
|
||||
842820F022DDD1E800C6F2D8 /* SideMenuPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SideMenuPresentationController.swift; path = Pod/Classes/SideMenuPresentationController.swift; sourceTree = "<group>"; };
|
||||
842820F122DDD1E800C6F2D8 /* Models.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Models.swift; path = Pod/Classes/Models.swift; sourceTree = "<group>"; };
|
||||
842820F222DDD1E800C6F2D8 /* SideMenuTransitionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SideMenuTransitionController.swift; path = Pod/Classes/SideMenuTransitionController.swift; sourceTree = "<group>"; };
|
||||
842820F322DDD1E800C6F2D8 /* Print.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Print.swift; path = Pod/Classes/Print.swift; sourceTree = "<group>"; };
|
||||
842820F422DDD1E800C6F2D8 /* Initializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Initializable.swift; path = Pod/Classes/Initializable.swift; sourceTree = "<group>"; };
|
||||
@@ -128,16 +129,16 @@
|
||||
8428210222E0449300C6F2D8 /* Deprecations.swift */,
|
||||
842820F522DDD1E800C6F2D8 /* Extensions.swift */,
|
||||
842820F422DDD1E800C6F2D8 /* Initializable.swift */,
|
||||
842820F122DDD1E800C6F2D8 /* Models.swift */,
|
||||
842820F322DDD1E800C6F2D8 /* Print.swift */,
|
||||
842820EE22DDD1BC00C6F2D8 /* Protected.swift */,
|
||||
65FF1B3D1DE321D8007B0845 /* SideMenu.h */,
|
||||
84278B88231F948D000B9B05 /* SideMenuAnimationController.swift */,
|
||||
842820FE22DDD24200C6F2D8 /* SideMenuInteractionController.swift */,
|
||||
84276D892282929A0095B7C5 /* SideMenuManager.swift */,
|
||||
8461A2D11E145A08001DA4F8 /* SideMenuNavigationController.swift */,
|
||||
842820F022DDD1E800C6F2D8 /* SideMenuPresentationController.swift */,
|
||||
842820FC22DDD21100C6F2D8 /* SideMenuPresentationStyle.swift */,
|
||||
842820F222DDD1E800C6F2D8 /* SideMenuTransitionController.swift */,
|
||||
8461A2D11E145A08001DA4F8 /* SideMenuNavigationController.swift */,
|
||||
84278B83231F9126000B9B05 /* SideMenuTransitionController.swift */,
|
||||
8461A2D21E145A08001DA4F8 /* UITableViewVibrantCell.swift */,
|
||||
);
|
||||
name = Source;
|
||||
@@ -167,6 +168,7 @@
|
||||
7B9DC9051DC6E8C1000D4007 /* Products */,
|
||||
9FB98148377EAEC00E35AC14 /* Pods */,
|
||||
9C94EEEBD250FF394115AAFC /* Frameworks */,
|
||||
84B19B84231F9BB000601D5C /* Recovered References */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -189,6 +191,14 @@
|
||||
path = ExampleTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84B19B84231F9BB000601D5C /* Recovered References */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
842820F222DDD1E800C6F2D8 /* SideMenuTransitionController.swift */,
|
||||
);
|
||||
name = "Recovered References";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84B489B81DD469B900D6CB43 /* Podspec Metadata */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -428,15 +438,15 @@
|
||||
files = (
|
||||
842820EF22DDD1BC00C6F2D8 /* Protected.swift in Sources */,
|
||||
8461A2D51E145A08001DA4F8 /* SideMenuNavigationController.swift in Sources */,
|
||||
84278B84231F9126000B9B05 /* SideMenuTransitionController.swift in Sources */,
|
||||
842820FD22DDD21100C6F2D8 /* SideMenuPresentationStyle.swift in Sources */,
|
||||
842820F822DDD1E800C6F2D8 /* SideMenuTransitionController.swift in Sources */,
|
||||
842820FF22DDD24200C6F2D8 /* SideMenuInteractionController.swift in Sources */,
|
||||
842820F922DDD1E800C6F2D8 /* Print.swift in Sources */,
|
||||
84276D8A2282929A0095B7C5 /* SideMenuManager.swift in Sources */,
|
||||
842820FB22DDD1E800C6F2D8 /* Extensions.swift in Sources */,
|
||||
842820F722DDD1E800C6F2D8 /* Models.swift in Sources */,
|
||||
842820F622DDD1E800C6F2D8 /* SideMenuPresentationController.swift in Sources */,
|
||||
842820FA22DDD1E800C6F2D8 /* Initializable.swift in Sources */,
|
||||
84278B89231F948D000B9B05 /* SideMenuAnimationController.swift in Sources */,
|
||||
8428210322E0449300C6F2D8 /* Deprecations.swift in Sources */,
|
||||
8461A2D61E145A08001DA4F8 /* UITableViewVibrantCell.swift in Sources */,
|
||||
);
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 135 KiB |
Reference in New Issue
Block a user