Compare commits
114 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f9f8fd7817 | |||
| dda720eee7 | |||
| 8bb2505b6f | |||
| 2878eea03f | |||
| 7512cb9373 | |||
| 189586d8e8 | |||
| f4d78af501 | |||
| af2ef21f88 | |||
| 25b87e4780 | |||
| c966675305 | |||
| b0dfba206c | |||
| 8960d3b4df | |||
| 05a117655b | |||
| 5ed24a978e | |||
| 330ea36ce5 | |||
| 05d934d084 | |||
| a520e98dbe | |||
| e49d564a2f | |||
| 632c35509b | |||
| b0a40b6fcf | |||
| 3d86c530ee | |||
| 4983a468c5 | |||
| bfbe4d21bf | |||
| 615ff0e391 | |||
| bdb9e4b3ae | |||
| 1c7d834c63 | |||
| 2e5ee04587 | |||
| 806a7dd1c7 | |||
| 094586042f | |||
| e919cd5b56 | |||
| e79169e735 | |||
| a43fbb64f8 | |||
| 275e01bdfd | |||
| 98da8fefc1 | |||
| b68fa60439 | |||
| 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 | |||
| bda990bbef | |||
| b8f13dc333 | |||
| 793ffd77bc | |||
| 132a50766e | |||
| 85f00c63b4 | |||
| e71ec145f1 | |||
| 37b0f6d57e | |||
| ccee3e1f83 | |||
| 1786b459c5 | |||
| 5f5fdcba7a | |||
| 27f490435e | |||
| 96dd886575 | |||
| 5b4b6afdbe | |||
| fa6f9eb9e1 | |||
| 0c3aaa471d | |||
| f184415f7c | |||
| 1c883abf55 | |||
| f82138dc87 | |||
| 56f5270405 | |||
| 0c1086a9da | |||
| 138a13bd60 | |||
| 430fad0568 | |||
| d8d19b6207 | |||
| eaa6d2f86f | |||
| 2a35158c3c | |||
| 9f454c8fc9 | |||
| fedff5a09e | |||
| 065e6e236e | |||
| 143a8cdde9 | |||
| cadb63bf21 | |||
| 6903ffb958 | |||
| ac5c1eaba6 | |||
| db4eb5380d | |||
| 5d08277cd4 | |||
| 2798c61d73 | |||
| 8037148fd2 | |||
| 2b8e46d503 | |||
| 49841b0b4e | |||
| 516e845da7 |
@@ -0,0 +1,23 @@
|
||||
# Contribution Guidelines
|
||||
|
||||
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 do not have time to teach you**. I am only supporting bugfixes or reviewing pull requests.
|
||||
|
||||
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..."_
|
||||
- That'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 _"How 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 [join the proud members](https://github.com/jonkykong/SideMenu/graphs/contributors) who have contributed 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.
|
||||
|
||||
### If your question is about SideMenu not working the way it's described in the [README](https://github.com/jonkykong/SideMenu/blob/master/README.md)...
|
||||
- This *may* be a bug. You must be able to reproduce the bug in the [demo project](https://github.com/jonkykong/SideMenu/tree/master/Example) which has a minimal amount of code. This helps ensure you don't have a bug in your code unrelated to SideMenu. If the bug is reproducable, open an issue and I will respond to it when I find time.
|
||||
|
||||
**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 behave a specific way not described in the [README](https://github.com/jonkykong/SideMenu/blob/master/README.md). I am not **tech support**. I am not a **teacher**. If you open an issue while failing to follow these guidelines or use the provided templates your **request for help will be ignored**.
|
||||
|
||||
### Thanks again for your support and for being respectful of my time.
|
||||
I apologize if this seems harsh, but there have been too many developers that have willfully ignored all of this and continued to contact me.
|
||||
@@ -0,0 +1,13 @@
|
||||
<!--- 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 (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 is happening in the **latest version** of SideMenu.
|
||||
- [ ] 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).
|
||||
- [ ] My issue is **not** a request for new functionality that I am unwilling to build and contribute with a pull request.
|
||||
- [ ] My issue **is** reproducible in the [demo project](https://github.com/jonkykong/SideMenu/tree/master/Example).
|
||||
|
||||
## Issue Description
|
||||
<!--- After completing all of the checks above, describe the issue here -->
|
||||
@@ -4,6 +4,8 @@
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>SideMenu</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -18,12 +22,14 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright © 2016 jonkykong. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd">
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright © 2016 Jon Kent. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd">
|
||||
<rect key="frame" x="20" y="626.5" width="335" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SideMenu" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
|
||||
<rect key="frame" x="20" y="202" width="335" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
||||
@@ -24,5 +24,26 @@ class SideMenuTableView: UITableViewController {
|
||||
imageView.contentMode = .scaleAspectFit
|
||||
imageView.backgroundColor = UIColor.black.withAlphaComponent(0.2)
|
||||
tableView.backgroundView = imageView
|
||||
|
||||
print("SideMenu Appearing!")
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
print("SideMenu Appeared!")
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
print("SideMenu Disappearing!")
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
print("SideMenu Disappeared!")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -75,15 +97,6 @@ open class SideMenuManager : NSObject {
|
||||
/// The radius of the shadow around the menu view controller or existing view controller depending on the `menuPresentMode`. Default is 5.
|
||||
open static var menuShadowRadius: CGFloat = 5
|
||||
|
||||
/// The left menu swipe to dismiss gesture.
|
||||
open static weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer?
|
||||
|
||||
/// The right menu swipe to dismiss gesture.
|
||||
open static weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer?
|
||||
|
||||
/// Enable or disable gestures that would swipe to present or dismiss the menu. Default is true.
|
||||
open static var menuEnableSwipeGestures: Bool = true
|
||||
|
||||
/// 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. Default is false.
|
||||
open static var menuPresentingViewControllerUserInteractionEnabled: Bool = false
|
||||
|
||||
@@ -93,11 +106,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")
|
||||
@@ -160,25 +215,60 @@ open class SideMenuManager : NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// The left menu swipe to dismiss gesture.
|
||||
open static weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer? {
|
||||
didSet {
|
||||
oldValue?.view?.removeGestureRecognizer(oldValue!)
|
||||
setupGesture(gesture: menuLeftSwipeToDismissGesture)
|
||||
}
|
||||
}
|
||||
|
||||
/// The right menu swipe to dismiss gesture.
|
||||
open static weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer? {
|
||||
didSet {
|
||||
oldValue?.view?.removeGestureRecognizer(oldValue!)
|
||||
setupGesture(gesture: menuRightSwipeToDismissGesture)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class func setupGesture(gesture: UIPanGestureRecognizer?) {
|
||||
guard let gesture = gesture else {
|
||||
return
|
||||
}
|
||||
|
||||
gesture.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handleHideMenuPan(_:)))
|
||||
}
|
||||
|
||||
fileprivate class func setupNavigationController(_ forMenu: UISideMenuNavigationController?, leftSide: Bool) {
|
||||
guard let forMenu = forMenu else {
|
||||
return
|
||||
}
|
||||
|
||||
let exitPanGesture = UIPanGestureRecognizer()
|
||||
exitPanGesture.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handleHideMenuPan(_:)))
|
||||
forMenu.view.addGestureRecognizer(exitPanGesture)
|
||||
if menuEnableSwipeGestures {
|
||||
let exitPanGesture = UIPanGestureRecognizer()
|
||||
forMenu.view.addGestureRecognizer(exitPanGesture)
|
||||
if leftSide {
|
||||
menuLeftSwipeToDismissGesture = exitPanGesture
|
||||
} else {
|
||||
menuRightSwipeToDismissGesture = exitPanGesture
|
||||
}
|
||||
}
|
||||
forMenu.transitioningDelegate = SideMenuTransition.singleton
|
||||
forMenu.modalPresentationStyle = .overFullScreen
|
||||
forMenu.leftSide = leftSide
|
||||
if leftSide {
|
||||
menuLeftSwipeToDismissGesture = exitPanGesture
|
||||
} else {
|
||||
menuRightSwipeToDismissGesture = exitPanGesture
|
||||
}
|
||||
updateMenuBlurIfNecessary()
|
||||
}
|
||||
|
||||
/// Enable or disable gestures that would swipe to dismiss the menu. Default is true.
|
||||
open static var menuEnableSwipeGestures: Bool = true {
|
||||
didSet {
|
||||
menuLeftSwipeToDismissGesture?.view?.removeGestureRecognizer(menuLeftSwipeToDismissGesture!)
|
||||
menuRightSwipeToDismissGesture?.view?.removeGestureRecognizer(menuRightSwipeToDismissGesture!)
|
||||
setupNavigationController(menuLeftNavigationController, leftSide: true)
|
||||
setupNavigationController(menuRightNavigationController, leftSide: false)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class func updateMenuBlurIfNecessary() {
|
||||
let menuBlurBlock = { (forMenu: UISideMenuNavigationController?) in
|
||||
if let forMenu = forMenu {
|
||||
@@ -197,7 +287,7 @@ open class SideMenuManager : NSObject {
|
||||
let menuBlurEffectStyle = menuBlurEffectStyle,
|
||||
let view = forMenu.visibleViewController?.view
|
||||
, !UIAccessibilityIsReduceTransparencyEnabled() else {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
if forMenu.originalMenuBackgroundColor == nil {
|
||||
@@ -222,7 +312,7 @@ open class SideMenuManager : NSObject {
|
||||
guard let forMenu = forMenu,
|
||||
let originalMenuBackgroundColor = forMenu.originalMenuBackgroundColor,
|
||||
let view = forMenu.visibleViewController?.view else {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
view.backgroundColor = originalMenuBackgroundColor
|
||||
@@ -255,6 +345,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 {
|
||||
@@ -264,6 +358,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
|
||||
@@ -281,6 +379,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,24 +8,66 @@
|
||||
|
||||
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()
|
||||
internal static var presentDirection: UIRectEdge = .left;
|
||||
internal static weak var tapView: UIView?
|
||||
internal static weak var statusBarView: UIView?
|
||||
internal static var presentDirection: UIRectEdge = .left
|
||||
internal static weak var tapView: UIView? {
|
||||
didSet {
|
||||
guard let tapView = tapView else {
|
||||
return
|
||||
}
|
||||
|
||||
tapView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
|
||||
let exitPanGesture = UIPanGestureRecognizer()
|
||||
exitPanGesture.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handleHideMenuPan(_:)))
|
||||
let exitTapGesture = UITapGestureRecognizer()
|
||||
exitTapGesture.addTarget(SideMenuTransition.self, action: #selector(SideMenuTransition.handleHideMenuTap(_:)))
|
||||
tapView.addGestureRecognizer(exitPanGesture)
|
||||
tapView.addGestureRecognizer(exitTapGesture)
|
||||
}
|
||||
}
|
||||
internal static weak var statusBarView: UIView? {
|
||||
didSet {
|
||||
guard let statusBarView = statusBarView else {
|
||||
return
|
||||
}
|
||||
|
||||
if let menuShrinkBackgroundColor = SideMenuManager.menuAnimationBackgroundColor {
|
||||
statusBarView.backgroundColor = menuShrinkBackgroundColor
|
||||
} else {
|
||||
statusBarView.backgroundColor = UIColor.black
|
||||
}
|
||||
statusBarView.isUserInteractionEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
// prevent instantiation
|
||||
fileprivate override init() {}
|
||||
fileprivate override init() {
|
||||
super.init()
|
||||
NotificationCenter.default.addObserver(self, selector:#selector(SideMenuTransition.handleNotification), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector:#selector(SideMenuTransition.handleNotification), name: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil)
|
||||
}
|
||||
|
||||
fileprivate class var viewControllerForPresentedMenu: UIViewController? {
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(SideMenuTransition.singleton)
|
||||
}
|
||||
|
||||
fileprivate class var presentingViewControllerForMenu: UIViewController? {
|
||||
get {
|
||||
return SideMenuManager.menuLeftNavigationController?.presentingViewController != nil ? SideMenuManager.menuLeftNavigationController?.presentingViewController : SideMenuManager.menuRightNavigationController?.presentingViewController
|
||||
return SideMenuManager.menuLeftNavigationController?.presentingViewController ?? SideMenuManager.menuRightNavigationController?.presentingViewController
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class var viewControllerForMenu: UISideMenuNavigationController? {
|
||||
get {
|
||||
return SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController : SideMenuManager.menuRightNavigationController
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,17 +100,21 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
}
|
||||
|
||||
internal class func handlePresentMenuPan(_ pan: UIPanGestureRecognizer) {
|
||||
guard SideMenuManager.menuEnableSwipeGestures else {
|
||||
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 {
|
||||
guard let view = presentingViewControllerForMenu?.view ?? pan.view else {
|
||||
return
|
||||
}
|
||||
|
||||
let transform = view.transform
|
||||
view.transform = CGAffineTransform.identity
|
||||
view.transform = .identity
|
||||
let translation = pan.translation(in: pan.view!)
|
||||
view.transform = transform
|
||||
|
||||
@@ -82,10 +128,11 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
SideMenuTransition.presentDirection = translation.x > 0 ? .left : .right
|
||||
}
|
||||
|
||||
if let menuViewController = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController : SideMenuManager.menuRightNavigationController,
|
||||
let visibleViewController = visibleViewController {
|
||||
if let menuViewController = viewControllerForMenu, let visibleViewController = visibleViewController {
|
||||
singleton.interactive = true
|
||||
visibleViewController.present(menuViewController, animated: true, completion: nil)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,23 +156,29 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
}
|
||||
default:
|
||||
singleton.interactive = false
|
||||
view.transform = CGAffineTransform.identity
|
||||
view.transform = .identity
|
||||
let velocity = pan.velocity(in: pan.view!).x * direction
|
||||
view.transform = transform
|
||||
if velocity >= 100 || velocity >= -50 && abs(distance) >= 0.5 {
|
||||
// bug workaround: animation briefly resets after call to finishInteractiveTransition() but before animateTransition completion is called.
|
||||
if ProcessInfo().operatingSystemVersion.majorVersion == 8 && singleton.percentComplete > 1 - CGFloat(FLT_EPSILON) {
|
||||
if ProcessInfo().operatingSystemVersion.majorVersion == 8 && singleton.percentComplete > 1 - CGFloat.ulpOfOne {
|
||||
singleton.update(0.9999)
|
||||
}
|
||||
singleton.finish()
|
||||
activeGesture = nil
|
||||
} else {
|
||||
singleton.cancel()
|
||||
activeGesture = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class func handleHideMenuPan(_ pan: UIPanGestureRecognizer) {
|
||||
if !SideMenuManager.menuEnableSwipeGestures {
|
||||
if activeGesture == nil {
|
||||
activeGesture = pan
|
||||
} else if pan != activeGesture {
|
||||
pan.isEnabled = false
|
||||
pan.isEnabled = true
|
||||
return
|
||||
}
|
||||
|
||||
@@ -137,7 +190,7 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
|
||||
case .began:
|
||||
singleton.interactive = true
|
||||
viewControllerForPresentedMenu?.dismiss(animated: true, completion: nil)
|
||||
presentingViewControllerForMenu?.dismiss(animated: true, completion: nil)
|
||||
case .changed:
|
||||
singleton.update(max(min(distance, 1), 0))
|
||||
default:
|
||||
@@ -145,33 +198,43 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
let velocity = pan.velocity(in: pan.view!).x * direction
|
||||
if velocity >= 100 || velocity >= -50 && distance >= 0.5 {
|
||||
// bug workaround: animation briefly resets after call to finishInteractiveTransition() but before animateTransition completion is called.
|
||||
if ProcessInfo().operatingSystemVersion.majorVersion == 8 && singleton.percentComplete > 1 - CGFloat(FLT_EPSILON) {
|
||||
if ProcessInfo().operatingSystemVersion.majorVersion == 8 && singleton.percentComplete > 1 - CGFloat.ulpOfOne {
|
||||
singleton.update(0.9999)
|
||||
}
|
||||
singleton.finish()
|
||||
activeGesture = nil
|
||||
} else {
|
||||
singleton.cancel()
|
||||
activeGesture = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class func handleHideMenuTap(_ tap: UITapGestureRecognizer) {
|
||||
viewControllerForPresentedMenu?.dismiss(animated: true, completion: nil)
|
||||
presentingViewControllerForMenu?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
internal class func hideMenuStart() {
|
||||
NotificationCenter.default.removeObserver(SideMenuTransition.singleton)
|
||||
guard let mainViewController = SideMenuTransition.viewControllerForPresentedMenu,
|
||||
let menuView = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController?.view : SideMenuManager.menuRightNavigationController?.view else {return}
|
||||
guard let mainViewController = presentingViewControllerForMenu,
|
||||
let menuView = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController?.view : SideMenuManager.menuRightNavigationController?.view else {
|
||||
return
|
||||
}
|
||||
|
||||
menuView.transform = CGAffineTransform.identity
|
||||
mainViewController.view.transform = CGAffineTransform.identity
|
||||
mainViewController.view.transform = .identity
|
||||
mainViewController.view.alpha = 1
|
||||
SideMenuTransition.tapView?.frame = CGRect(x: 0, y: 0, width: mainViewController.view.frame.width, height: mainViewController.view.frame.height)
|
||||
mainViewController.view.frame.origin.y = 0
|
||||
menuView.transform = .identity
|
||||
menuView.frame.origin.y = 0
|
||||
menuView.frame.size.width = SideMenuManager.menuWidth
|
||||
menuView.frame.size.height = mainViewController.view.frame.height
|
||||
SideMenuTransition.statusBarView?.frame = UIApplication.shared.statusBarFrame
|
||||
menuView.frame.size.height = mainViewController.view.frame.height // in case status bar height changed
|
||||
var statusBarFrame = UIApplication.shared.statusBarFrame
|
||||
let statusBarOffset = SideMenuManager.appScreenRect.size.height - mainViewController.view.frame.maxY
|
||||
// For in-call status bar, height is normally 40, which overlaps view. Instead, calculate height difference
|
||||
// of view and set height to fill in remaining space.
|
||||
if statusBarOffset >= CGFloat.ulpOfOne {
|
||||
statusBarFrame.size.height = statusBarOffset
|
||||
}
|
||||
SideMenuTransition.statusBarView?.frame = statusBarFrame
|
||||
SideMenuTransition.statusBarView?.alpha = 0
|
||||
|
||||
switch SideMenuManager.menuPresentMode {
|
||||
@@ -199,8 +262,8 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
}
|
||||
|
||||
internal class func hideMenuComplete() {
|
||||
guard let mainViewController = SideMenuTransition.viewControllerForPresentedMenu,
|
||||
let menuView = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController?.view : SideMenuManager.menuRightNavigationController?.view else {
|
||||
guard let mainViewController = presentingViewControllerForMenu,
|
||||
let menuView = viewControllerForMenu?.view else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -212,62 +275,71 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
if let topNavigationController = mainViewController as? UINavigationController {
|
||||
topNavigationController.interactivePopGestureRecognizer!.isEnabled = true
|
||||
}
|
||||
originalSuperview?.addSubview(mainViewController.view)
|
||||
if let originalSuperview = originalSuperview {
|
||||
originalSuperview.addSubview(mainViewController.view)
|
||||
let y = originalSuperview.bounds.height - mainViewController.view.frame.size.height
|
||||
mainViewController.view.frame.origin.y = max(y, 0)
|
||||
}
|
||||
}
|
||||
|
||||
internal class func presentMenuStart(forSize size: CGSize = SideMenuManager.appScreenRect.size) {
|
||||
guard let menuView = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController?.view : SideMenuManager.menuRightNavigationController?.view,
|
||||
let mainViewController = SideMenuTransition.viewControllerForPresentedMenu else {
|
||||
internal class func presentMenuStart() {
|
||||
guard let menuView = viewControllerForMenu?.view,
|
||||
let mainViewController = presentingViewControllerForMenu else {
|
||||
return
|
||||
}
|
||||
|
||||
menuView.transform = CGAffineTransform.identity
|
||||
mainViewController.view.transform = CGAffineTransform.identity
|
||||
menuView.alpha = 1
|
||||
menuView.transform = .identity
|
||||
menuView.frame.size.width = SideMenuManager.menuWidth
|
||||
menuView.frame.size.height = size.height
|
||||
let size = SideMenuManager.appScreenRect.size
|
||||
menuView.frame.origin.x = SideMenuTransition.presentDirection == .left ? 0 : size.width - SideMenuManager.menuWidth
|
||||
SideMenuTransition.statusBarView?.frame = UIApplication.shared.statusBarFrame
|
||||
mainViewController.view.transform = .identity
|
||||
mainViewController.view.frame.size.width = size.width
|
||||
let statusBarOffset = size.height - menuView.bounds.height
|
||||
mainViewController.view.bounds.size.height = size.height - max(statusBarOffset, 0)
|
||||
mainViewController.view.frame.origin.y = 0
|
||||
var statusBarFrame = UIApplication.shared.statusBarFrame
|
||||
// For in-call status bar, height is normally 40, which overlaps view. Instead, calculate height difference
|
||||
// of view and set height to fill in remaining space.
|
||||
if statusBarOffset >= CGFloat.ulpOfOne {
|
||||
statusBarFrame.size.height = statusBarOffset
|
||||
}
|
||||
SideMenuTransition.tapView?.transform = .identity
|
||||
SideMenuTransition.tapView?.bounds = mainViewController.view.bounds
|
||||
SideMenuTransition.statusBarView?.frame = statusBarFrame
|
||||
SideMenuTransition.statusBarView?.alpha = 1
|
||||
|
||||
switch SideMenuManager.menuPresentMode {
|
||||
|
||||
case .viewSlideOut:
|
||||
menuView.alpha = 1
|
||||
case .viewSlideOut, .viewSlideInOut:
|
||||
mainViewController.view.layer.shadowColor = SideMenuManager.menuShadowColor.cgColor
|
||||
mainViewController.view.layer.shadowRadius = SideMenuManager.menuShadowRadius
|
||||
mainViewController.view.layer.shadowOpacity = SideMenuManager.menuShadowOpacity
|
||||
mainViewController.view.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
let direction:CGFloat = SideMenuTransition.presentDirection == .left ? 1 : -1
|
||||
mainViewController.view.frame.origin.x = direction * (menuView.frame.width)
|
||||
mainViewController.view.layer.shadowColor = SideMenuManager.menuShadowColor.cgColor
|
||||
mainViewController.view.layer.shadowRadius = SideMenuManager.menuShadowRadius
|
||||
mainViewController.view.layer.shadowOpacity = SideMenuManager.menuShadowOpacity
|
||||
mainViewController.view.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
|
||||
case .viewSlideInOut:
|
||||
menuView.alpha = 1
|
||||
mainViewController.view.layer.shadowColor = SideMenuManager.menuShadowColor.cgColor
|
||||
mainViewController.view.layer.shadowRadius = SideMenuManager.menuShadowRadius
|
||||
mainViewController.view.layer.shadowOpacity = SideMenuManager.menuShadowOpacity
|
||||
mainViewController.view.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
let direction:CGFloat = SideMenuTransition.presentDirection == .left ? 1 : -1
|
||||
mainViewController.view.frame = CGRect(x: direction * (menuView.frame.width), y: 0, width: size.width, height: size.height)
|
||||
mainViewController.view.transform = CGAffineTransform(scaleX: SideMenuManager.menuAnimationTransformScaleFactor, y: SideMenuManager.menuAnimationTransformScaleFactor)
|
||||
mainViewController.view.alpha = 1 - SideMenuManager.menuAnimationFadeStrength
|
||||
|
||||
case .menuSlideIn, .menuDissolveIn:
|
||||
menuView.alpha = 1
|
||||
if SideMenuManager.menuBlurEffectStyle == nil {
|
||||
menuView.layer.shadowColor = SideMenuManager.menuShadowColor.cgColor
|
||||
menuView.layer.shadowRadius = SideMenuManager.menuShadowRadius
|
||||
menuView.layer.shadowOpacity = SideMenuManager.menuShadowOpacity
|
||||
menuView.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
}
|
||||
mainViewController.view.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
|
||||
mainViewController.view.frame.origin.x = 0
|
||||
}
|
||||
|
||||
if SideMenuManager.menuPresentMode != .viewSlideOut {
|
||||
mainViewController.view.transform = CGAffineTransform(scaleX: SideMenuManager.menuAnimationTransformScaleFactor, y: SideMenuManager.menuAnimationTransformScaleFactor)
|
||||
if SideMenuManager.menuAnimationTransformScaleFactor > 1 {
|
||||
SideMenuTransition.tapView?.transform = mainViewController.view.transform
|
||||
}
|
||||
mainViewController.view.alpha = 1 - SideMenuManager.menuAnimationFadeStrength
|
||||
}
|
||||
}
|
||||
|
||||
internal class func presentMenuComplete() {
|
||||
NotificationCenter.default.addObserver(SideMenuTransition.singleton, selector:#selector(SideMenuTransition.applicationDidEnterBackgroundNotification), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
|
||||
guard let mainViewController = SideMenuTransition.viewControllerForPresentedMenu else {
|
||||
guard let mainViewController = presentingViewControllerForMenu else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -293,137 +365,177 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: UIViewControllerAnimatedTransitioning protocol methods
|
||||
internal func handleNotification() {
|
||||
guard let mainViewController = SideMenuTransition.presentingViewControllerForMenu,
|
||||
let menuViewController = SideMenuTransition.viewControllerForMenu,
|
||||
menuViewController.presentedViewController == nil && menuViewController.presentingViewController != nil else {
|
||||
return
|
||||
}
|
||||
|
||||
if let originalSuperview = SideMenuTransition.originalSuperview {
|
||||
originalSuperview.addSubview(mainViewController.view)
|
||||
}
|
||||
UIView.animate(withDuration: SideMenuManager.menuAnimationDismissDuration,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: SideMenuManager.menuAnimationUsingSpringWithDamping,
|
||||
initialSpringVelocity: SideMenuManager.menuAnimationInitialSpringVelocity,
|
||||
options: SideMenuManager.menuAnimationOptions,
|
||||
animations: {
|
||||
SideMenuTransition.hideMenuStart()
|
||||
}) { (finished) -> Void in
|
||||
SideMenuTransition.hideMenuComplete()
|
||||
menuViewController.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
let menuView = menuViewController.view!
|
||||
let topView = topViewController.view!
|
||||
|
||||
// prepare menu items to slide in
|
||||
if presenting {
|
||||
var tapView: UIView?
|
||||
if !SideMenuManager.menuPresentingViewControllerUserInteractionEnabled {
|
||||
tapView = UIView()
|
||||
tapView!.autoresizingMask = [.flexibleHeight, .flexibleWidth]
|
||||
let exitPanGesture = UIPanGestureRecognizer()
|
||||
exitPanGesture.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handleHideMenuPan(_:)))
|
||||
let exitTapGesture = UITapGestureRecognizer()
|
||||
exitTapGesture.addTarget(SideMenuTransition.self, action: #selector(SideMenuTransition.handleHideMenuTap(_:)))
|
||||
tapView!.addGestureRecognizer(exitPanGesture)
|
||||
tapView!.addGestureRecognizer(exitTapGesture)
|
||||
SideMenuTransition.tapView = tapView
|
||||
}
|
||||
|
||||
SideMenuTransition.originalSuperview = topView?.superview
|
||||
SideMenuTransition.originalSuperview = topView.superview
|
||||
|
||||
// add the both views to our view controller
|
||||
switch SideMenuManager.menuPresentMode {
|
||||
case .viewSlideOut, .viewSlideInOut:
|
||||
container.addSubview(menuView!)
|
||||
container.addSubview(topView!)
|
||||
if let tapView = tapView {
|
||||
topView?.addSubview(tapView)
|
||||
}
|
||||
container.addSubview(menuView)
|
||||
container.addSubview(topView)
|
||||
case .menuSlideIn, .menuDissolveIn:
|
||||
container.addSubview(topView!)
|
||||
if let tapView = tapView {
|
||||
container.addSubview(tapView)
|
||||
}
|
||||
container.addSubview(menuView!)
|
||||
container.addSubview(topView)
|
||||
container.addSubview(menuView)
|
||||
}
|
||||
|
||||
|
||||
if SideMenuManager.menuFadeStatusBar {
|
||||
let blackBar = UIView()
|
||||
if let menuShrinkBackgroundColor = SideMenuManager.menuAnimationBackgroundColor {
|
||||
blackBar.backgroundColor = menuShrinkBackgroundColor
|
||||
} else {
|
||||
blackBar.backgroundColor = UIColor.black
|
||||
}
|
||||
blackBar.isUserInteractionEnabled = false
|
||||
container.addSubview(blackBar)
|
||||
SideMenuTransition.statusBarView = blackBar
|
||||
let statusBarView = UIView()
|
||||
SideMenuTransition.statusBarView = statusBarView
|
||||
container.addSubview(statusBarView)
|
||||
}
|
||||
|
||||
SideMenuTransition.hideMenuStart() // offstage for interactive
|
||||
SideMenuTransition.hideMenuStart()
|
||||
}
|
||||
|
||||
let animate = {
|
||||
if self.presenting {
|
||||
SideMenuTransition.presentMenuStart()
|
||||
} else {
|
||||
SideMenuTransition.hideMenuStart()
|
||||
}
|
||||
}
|
||||
|
||||
let complete = {
|
||||
container.isUserInteractionEnabled = true
|
||||
|
||||
// tell our transitionContext object that we've finished animating
|
||||
if transitionContext.transitionWasCancelled {
|
||||
let viewControllerForPresentedMenu = SideMenuTransition.presentingViewControllerForMenu
|
||||
|
||||
if self.presenting {
|
||||
SideMenuTransition.hideMenuComplete()
|
||||
} else {
|
||||
SideMenuTransition.presentMenuComplete()
|
||||
}
|
||||
|
||||
transitionContext.completeTransition(false)
|
||||
|
||||
if SideMenuTransition.switchMenus {
|
||||
SideMenuTransition.switchMenus = false
|
||||
viewControllerForPresentedMenu?.present(SideMenuTransition.viewControllerForMenu!, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if self.presenting {
|
||||
SideMenuTransition.presentMenuComplete()
|
||||
transitionContext.completeTransition(true)
|
||||
switch SideMenuManager.menuPresentMode {
|
||||
case .viewSlideOut, .viewSlideInOut:
|
||||
container.addSubview(topView)
|
||||
case .menuSlideIn, .menuDissolveIn:
|
||||
container.insertSubview(topView, at: 0)
|
||||
}
|
||||
if !SideMenuManager.menuPresentingViewControllerUserInteractionEnabled {
|
||||
let tapView = UIView()
|
||||
container.insertSubview(tapView, aboveSubview: topView)
|
||||
tapView.bounds = container.bounds
|
||||
tapView.center = topView.center
|
||||
if SideMenuManager.menuAnimationTransformScaleFactor > 1 {
|
||||
tapView.transform = topView.transform
|
||||
}
|
||||
SideMenuTransition.tapView = tapView
|
||||
}
|
||||
if let statusBarView = SideMenuTransition.statusBarView {
|
||||
container.bringSubview(toFront: statusBarView)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
SideMenuTransition.hideMenuComplete()
|
||||
transitionContext.completeTransition(true)
|
||||
menuView.removeFromSuperview()
|
||||
}
|
||||
|
||||
// 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
|
||||
if self.presenting {
|
||||
SideMenuTransition.presentMenuStart() // onstage items: slide in
|
||||
} else {
|
||||
SideMenuTransition.hideMenuStart()
|
||||
}
|
||||
menuView?.isUserInteractionEnabled = false
|
||||
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
|
||||
// tell our transitionContext object that we've finished animating
|
||||
if transitionContext.transitionWasCancelled {
|
||||
let viewControllerForPresentedMenu = SideMenuTransition.viewControllerForPresentedMenu
|
||||
|
||||
if self.presenting {
|
||||
SideMenuTransition.hideMenuComplete()
|
||||
} else {
|
||||
SideMenuTransition.presentMenuComplete()
|
||||
}
|
||||
menuView?.isUserInteractionEnabled = true
|
||||
|
||||
transitionContext.completeTransition(false)
|
||||
|
||||
if SideMenuTransition.switchMenus {
|
||||
SideMenuTransition.switchMenus = false
|
||||
viewControllerForPresentedMenu?.present(SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController! : SideMenuManager.menuRightNavigationController!, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if self.presenting {
|
||||
SideMenuTransition.presentMenuComplete()
|
||||
menuView?.isUserInteractionEnabled = true
|
||||
transitionContext.completeTransition(true)
|
||||
switch SideMenuManager.menuPresentMode {
|
||||
case .viewSlideOut, .viewSlideInOut:
|
||||
container.addSubview(topView!)
|
||||
case .menuSlideIn, .menuDissolveIn:
|
||||
container.insertSubview(topView!, at: 0)
|
||||
}
|
||||
if let statusBarView = SideMenuTransition.statusBarView {
|
||||
container.bringSubview(toFront: statusBarView)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
SideMenuTransition.hideMenuComplete()
|
||||
transitionContext.completeTransition(true)
|
||||
menuView?.removeFromSuperview()
|
||||
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
|
||||
@@ -449,13 +561,4 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewContr
|
||||
return interactive ? SideMenuTransition.singleton : nil
|
||||
}
|
||||
|
||||
internal func applicationDidEnterBackgroundNotification() {
|
||||
if let menuViewController: UINavigationController = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController : SideMenuManager.menuRightNavigationController,
|
||||
menuViewController.presentedViewController == nil {
|
||||
SideMenuTransition.hideMenuStart()
|
||||
SideMenuTransition.hideMenuComplete()
|
||||
menuViewController.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
@@ -35,6 +35,13 @@ open class UISideMenuNavigationController: UINavigationController {
|
||||
didSetSide()
|
||||
}
|
||||
|
||||
open override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
// Dismiss keyboard to prevent weird keyboard animations from occurring during transition
|
||||
presentingViewController?.view.endEditing(true)
|
||||
}
|
||||
|
||||
fileprivate func didSetSide() {
|
||||
if leftSide {
|
||||
SideMenuManager.menuLeftNavigationController = self
|
||||
@@ -46,13 +53,17 @@ open class UISideMenuNavigationController: UINavigationController {
|
||||
override open func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
// we had presented a view before, so lets dismiss ourselves as already acted upon
|
||||
// We had presented a view before, so lets dismiss ourselves as already acted upon
|
||||
if view.isHidden {
|
||||
SideMenuTransition.hideMenuComplete()
|
||||
dismiss(animated: false, completion: { () -> Void in
|
||||
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) {
|
||||
@@ -79,7 +90,7 @@ open class UISideMenuNavigationController: UINavigationController {
|
||||
override open func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
// we're presenting a view controller from the menu, so we need to hide the menu so it isn't g when the presented view is dismissed.
|
||||
// We're presenting a view controller from the menu, so we need to hide the menu so it isn't showing when the presented view is dismissed.
|
||||
if !isBeingDismissed {
|
||||
view.isHidden = true
|
||||
SideMenuTransition.hideMenuStart()
|
||||
@@ -89,49 +100,48 @@ open class UISideMenuNavigationController: UINavigationController {
|
||||
override open func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
|
||||
// don't bother resizing if the view isn't visible
|
||||
if view.isHidden {
|
||||
// Don't bother resizing if the view isn't visible
|
||||
guard !view.isHidden else {
|
||||
return
|
||||
}
|
||||
|
||||
SideMenuTransition.statusBarView?.isHidden = true
|
||||
coordinator.animate(alongsideTransition: { (context) -> Void in
|
||||
SideMenuTransition.presentMenuStart(forSize: size)
|
||||
}) { (context) -> Void in
|
||||
SideMenuTransition.statusBarView?.isHidden = false
|
||||
NotificationCenter.default.removeObserver(SideMenuTransition.singleton, name: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil)
|
||||
coordinator.animate(alongsideTransition: { (context) in
|
||||
SideMenuTransition.presentMenuStart()
|
||||
}) { (context) in
|
||||
NotificationCenter.default.addObserver(SideMenuTransition.singleton, selector:#selector(SideMenuTransition.handleNotification), name: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
override open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if let menuViewController: UINavigationController = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController : SideMenuManager.menuRightNavigationController,
|
||||
let presentingViewController = menuViewController.presentingViewController as? UINavigationController {
|
||||
presentingViewController.prepare(for: segue, sender: sender)
|
||||
if let presentingViewController = presentingViewController {
|
||||
presentingViewController.prepare(for: segue, sender: sender)
|
||||
}
|
||||
}
|
||||
|
||||
override open func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||
if let menuViewController: UINavigationController = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController : SideMenuManager.menuRightNavigationController,
|
||||
let presentingViewController = menuViewController.presentingViewController as? UINavigationController {
|
||||
return presentingViewController.shouldPerformSegue(withIdentifier: identifier, sender: sender)
|
||||
if let presentingViewController = presentingViewController {
|
||||
return presentingViewController.shouldPerformSegue(withIdentifier: identifier, sender: sender)
|
||||
}
|
||||
|
||||
return super.shouldPerformSegue(withIdentifier: identifier, sender: sender)
|
||||
}
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
guard let presentingViewController = presentingViewController as? UINavigationController else {
|
||||
print("SideMenu Warning: attempt to push a View Controller from \(self.presentingViewController.self) where its navigationController == nil. It must be embedded in a Navigation Controller for this to work.")
|
||||
|
||||
let tabBarController = presentingViewController as? UITabBarController
|
||||
guard let navigationController = (tabBarController?.selectedViewController ?? presentingViewController) as? UINavigationController else {
|
||||
print("SideMenu Warning: attempt to push a View Controller from \(String(describing: presentingViewController.self)) where its navigationController == nil. It must be embedded in a Navigation Controller for this to work.")
|
||||
return
|
||||
}
|
||||
|
||||
// to avoid overlapping dismiss & pop/push calls, create a transaction block where the menu
|
||||
// To avoid overlapping dismiss & pop/push calls, create a transaction block where the menu
|
||||
// is dismissed after showing the appropriate screen
|
||||
CATransaction.begin()
|
||||
CATransaction.setCompletionBlock( { () -> Void in
|
||||
@@ -139,39 +149,55 @@ 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 presentingViewController.viewControllers {
|
||||
if type(of: subViewController) == type(of: viewController) {
|
||||
presentingViewController.popToViewController(subViewController, animated: animated)
|
||||
CATransaction.commit()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if SideMenuManager.menuReplaceOnPush {
|
||||
var viewControllers = presentingViewController.viewControllers
|
||||
viewControllers.removeLast()
|
||||
viewControllers.append(viewController)
|
||||
presentingViewController.setViewControllers(viewControllers, animated: animated)
|
||||
if let lastViewController = navigationController.viewControllers.last, !SideMenuManager.menuAllowPushOfSameClassTwice && type(of: lastViewController) == type(of: viewController) {
|
||||
CATransaction.commit()
|
||||
return
|
||||
}
|
||||
|
||||
if let lastViewController = presentingViewController.viewControllers.last, !SideMenuManager.menuAllowPushOfSameClassTwice {
|
||||
if type(of: lastViewController) == type(of: viewController) {
|
||||
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
|
||||
}
|
||||
}
|
||||
case .preserve, .preserveAndHideBackButton:
|
||||
var viewControllers = navigationController.viewControllers
|
||||
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
|
||||
}
|
||||
|
||||
presentingViewController.pushViewController(viewController, animated: animated)
|
||||
navigationController.pushViewController(viewController, animated: animated)
|
||||
CATransaction.commit()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,27 +4,34 @@
|
||||
[](http://cocoapods.org/pods/SideMenu)
|
||||
[](http://cocoapods.org/pods/SideMenu)
|
||||
|
||||
### If you like SideMenu, give it a ★ at the top right of this page.
|
||||
### 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!
|
||||
|
||||
> 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. *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.*
|
||||
> * [Website](http://jonkent.me).
|
||||
> * Building and maintaining this free library takes time. Help keep me awake and buy me a coffee ☕️ via [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=contact%40jonkent%2eme&lc=US¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted).
|
||||
|
||||
## Overview
|
||||
|
||||
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 (even parallax 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.
|
||||
- [x] Menus can be presented and dismissed the same as any other View Controller since this control uses custom transitions.
|
||||
* **It can be implemented in storyboard without a single line of [code](#code-less-storyboard-implementation).**
|
||||
* Four standard animation styles to choose from (there's even a parallax effect if you want to get weird).
|
||||
* Highly customizable without needing to write tons of custom code.
|
||||
* Supports continuous swiping between side menus on boths sides in a single gesture.
|
||||
* Global menu configuration. Set-up once and be done for all screens.
|
||||
* Menus can be presented and dismissed the same as any other view controller since this control uses [custom transitions](https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html).
|
||||
* Animations use your view controllers, not snapshots.
|
||||
* Properly handles screen rotation and in-call status bar height changes.
|
||||
|
||||
Check out the example project to see it in action!
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
### Preview Samples
|
||||
| Slide Out | Slide In | Dissolve | Slide In + Out |
|
||||
| --- | --- | --- | --- |
|
||||
|  |  |  |  |
|
||||
|
||||
## Requirements
|
||||
- [x] iOS 8 or higher
|
||||
* iOS 8 or higher
|
||||
|
||||
## Installation
|
||||
### CocoaPods
|
||||
@@ -44,7 +51,7 @@ use_frameworks!
|
||||
|
||||
pod 'SideMenu'
|
||||
|
||||
# For Swift 2.3, use:
|
||||
# For Swift 2.3 (no longer maintained), use:
|
||||
# pod 'SideMenu', '~> 1.2.1'
|
||||
```
|
||||
|
||||
@@ -73,13 +80,13 @@ 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**. 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 `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.
|
||||

|
||||
|
||||
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.
|
||||

|
||||
|
||||
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.
|
||||
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.
|
||||

|
||||
|
||||
That's it. *Note: you can only enable gestures in code.*
|
||||
@@ -89,20 +96,24 @@ First:
|
||||
import SideMenu
|
||||
```
|
||||
|
||||
In your View Controller's `viewDidLoad` event, do something like this:
|
||||
In your view controller's `viewDidLoad` event, do something like this (**IMPORTANT: If you're seeing a black menu when you use gestures, read this section carefully!**):
|
||||
``` swift
|
||||
// Define the menus
|
||||
let menuLeftNavigationController = UISideMenuNavigationController()
|
||||
let menuLeftNavigationController = UISideMenuNavigationController(rootViewController: YourViewController)
|
||||
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.
|
||||
let menuRightNavigationController = UISideMenuNavigationController(rootViewController: YourViewController)
|
||||
// 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.
|
||||
// Note that these continue to work on the Navigation Controller independent of the View Controller it displays!
|
||||
// Note that these continue to work on the Navigation Controller independent of the view controller it displays!
|
||||
SideMenuManager.menuAddPanGestureToPresent(toView: self.navigationController!.navigationBar)
|
||||
SideMenuManager.menuAddScreenEdgePanGesturesToPresent(toView: self.navigationController!.view)
|
||||
```
|
||||
@@ -110,13 +121,29 @@ Then from a button, do something like this:
|
||||
``` swift
|
||||
present(SideMenuManager.menuLeftNavigationController!, animated: true, completion: nil)
|
||||
|
||||
// For Swift 2.3, use:
|
||||
// Similarly, to dismiss a menu programmatically, you would do this:
|
||||
dismiss(animated: true, completion: nil)
|
||||
|
||||
// For Swift 2.3 (no longer maintained), use:
|
||||
// presentViewController(SideMenuManager.menuLeftNavigationController!, animated: true, completion: nil)
|
||||
```
|
||||
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.
|
||||
|
||||
@@ -131,9 +158,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)
|
||||
|
||||
@@ -167,7 +191,7 @@ open static weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer?
|
||||
/// The right menu swipe to dismiss gesture.
|
||||
open static weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer?
|
||||
|
||||
/// Enable or disable gestures that would swipe to present or dismiss the menu. Default is true.
|
||||
/// Enable or disable gestures that would swipe to dismiss the menu. Default is true.
|
||||
open static var menuEnableSwipeGestures: Bool = true
|
||||
|
||||
/// 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. Default is false.
|
||||
@@ -179,11 +203,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.
|
||||
@@ -224,13 +251,6 @@ Don't try to change the status bar appearance when presenting a menu. When used
|
||||
## Thank You
|
||||
A special thank you to everyone that has [contributed](https://github.com/jonkykong/SideMenu/graphs/contributors) to this library to make it better. Your support is appreciated!
|
||||
|
||||
## About Me
|
||||
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!**
|
||||
|
||||
🌎 Web: [http://jonkent.me](http://jonkent.me)
|
||||
|
||||
✉️ Email: [contact@jonkent.me](mailto:contact@jonkent.me) **IMPORTANT: Before emailing me, please read [this](https://github.com/jonkykong/SideMenu/issues/58).**
|
||||
|
||||
## License
|
||||
|
||||
SideMenu is available under the MIT license. See the LICENSE file for more info.
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "SideMenu"
|
||||
s.version = "2.0.7"
|
||||
s.version = "2.3.1"
|
||||
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.
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
108F6C478E56A4CA081F8E09 /* Pods_Example_ExampleTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52C8DD7BE43A8987854CA726 /* Pods_Example_ExampleTests.framework */; };
|
||||
456193561EADC00AC505B531 /* Pods_ExampleTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1435FAD2F735E43F383BE96 /* Pods_ExampleTests.framework */; };
|
||||
65FF1B3E1DE321D8007B0845 /* SideMenu.h in Headers */ = {isa = PBXBuildFile; fileRef = 65FF1B3D1DE321D8007B0845 /* SideMenu.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
65FF1B441DE331D9007B0845 /* SideMenuManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65FF1B401DE331D9007B0845 /* SideMenuManager.swift */; };
|
||||
65FF1B451DE331D9007B0845 /* SideMenuTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65FF1B411DE331D9007B0845 /* SideMenuTransition.swift */; };
|
||||
65FF1B461DE331D9007B0845 /* UISideMenuNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65FF1B421DE331D9007B0845 /* UISideMenuNavigationController.swift */; };
|
||||
65FF1B471DE331D9007B0845 /* UITableViewVibrantCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65FF1B431DE331D9007B0845 /* UITableViewVibrantCell.swift */; };
|
||||
7B48A0D61DCB2487002990A1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B48A0D51DCB2487002990A1 /* AppDelegate.swift */; };
|
||||
7B48A0DD1DCB2487002990A1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7B48A0DC1DCB2487002990A1 /* Assets.xcassets */; };
|
||||
7B48A0EB1DCB2487002990A1 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B48A0EA1DCB2487002990A1 /* Tests.swift */; };
|
||||
@@ -22,6 +18,10 @@
|
||||
7B48A0F81DCB2518002990A1 /* SideMenuTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B48A0F51DCB2518002990A1 /* SideMenuTableView.swift */; };
|
||||
7B552D5D1DCC65830010301C /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B552D5C1DCC65830010301C /* Launch Screen.storyboard */; };
|
||||
7B5FA9B61DCB269700278DF6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B5FA9B51DCB269700278DF6 /* Main.storyboard */; };
|
||||
8461A2D31E145A08001DA4F8 /* SideMenuManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8461A2CF1E145A08001DA4F8 /* SideMenuManager.swift */; };
|
||||
8461A2D41E145A08001DA4F8 /* SideMenuTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8461A2D01E145A08001DA4F8 /* SideMenuTransition.swift */; };
|
||||
8461A2D51E145A08001DA4F8 /* UISideMenuNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8461A2D11E145A08001DA4F8 /* UISideMenuNavigationController.swift */; };
|
||||
8461A2D61E145A08001DA4F8 /* UITableViewVibrantCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8461A2D21E145A08001DA4F8 /* UITableViewVibrantCell.swift */; };
|
||||
84B489B51DD469B000D6CB43 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 84B489B21DD469B000D6CB43 /* LICENSE */; };
|
||||
84B489B71DD469B000D6CB43 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 84B489B41DD469B000D6CB43 /* README.md */; };
|
||||
84B489BA1DD469DA00D6CB43 /* SideMenu.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 84B489B91DD469DA00D6CB43 /* SideMenu.podspec */; };
|
||||
@@ -44,10 +44,6 @@
|
||||
47896ABC5C8830D88945A8D3 /* Pods_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
52C8DD7BE43A8987854CA726 /* Pods_Example_ExampleTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_ExampleTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
65FF1B3D1DE321D8007B0845 /* SideMenu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SideMenu.h; path = SideMenu/SideMenu.h; sourceTree = "<group>"; };
|
||||
65FF1B401DE331D9007B0845 /* SideMenuManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SideMenuManager.swift; path = SideMenu/SideMenuManager.swift; sourceTree = "<group>"; };
|
||||
65FF1B411DE331D9007B0845 /* SideMenuTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SideMenuTransition.swift; path = SideMenu/SideMenuTransition.swift; sourceTree = "<group>"; };
|
||||
65FF1B421DE331D9007B0845 /* UISideMenuNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UISideMenuNavigationController.swift; path = SideMenu/UISideMenuNavigationController.swift; sourceTree = "<group>"; };
|
||||
65FF1B431DE331D9007B0845 /* UITableViewVibrantCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UITableViewVibrantCell.swift; path = SideMenu/UITableViewVibrantCell.swift; sourceTree = "<group>"; };
|
||||
7B48A0D31DCB2487002990A1 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7B48A0D51DCB2487002990A1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
7B48A0DC1DCB2487002990A1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
@@ -61,6 +57,10 @@
|
||||
7B552D5C1DCC65830010301C /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = "<group>"; };
|
||||
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; };
|
||||
8461A2CF1E145A08001DA4F8 /* SideMenuManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SideMenuManager.swift; path = Pod/Classes/SideMenuManager.swift; sourceTree = "<group>"; };
|
||||
8461A2D01E145A08001DA4F8 /* SideMenuTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SideMenuTransition.swift; path = Pod/Classes/SideMenuTransition.swift; sourceTree = "<group>"; };
|
||||
8461A2D11E145A08001DA4F8 /* UISideMenuNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UISideMenuNavigationController.swift; path = Pod/Classes/UISideMenuNavigationController.swift; sourceTree = "<group>"; };
|
||||
8461A2D21E145A08001DA4F8 /* UITableViewVibrantCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UITableViewVibrantCell.swift; path = Pod/Classes/UITableViewVibrantCell.swift; sourceTree = "<group>"; };
|
||||
84B489B21DD469B000D6CB43 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
|
||||
84B489B41DD469B000D6CB43 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
84B489B91DD469DA00D6CB43 /* SideMenu.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = SideMenu.podspec; sourceTree = "<group>"; };
|
||||
@@ -109,10 +109,10 @@
|
||||
65FF1B3F1DE33097007B0845 /* Source */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
65FF1B401DE331D9007B0845 /* SideMenuManager.swift */,
|
||||
65FF1B411DE331D9007B0845 /* SideMenuTransition.swift */,
|
||||
65FF1B421DE331D9007B0845 /* UISideMenuNavigationController.swift */,
|
||||
65FF1B431DE331D9007B0845 /* UITableViewVibrantCell.swift */,
|
||||
8461A2CF1E145A08001DA4F8 /* SideMenuManager.swift */,
|
||||
8461A2D01E145A08001DA4F8 /* SideMenuTransition.swift */,
|
||||
8461A2D11E145A08001DA4F8 /* UISideMenuNavigationController.swift */,
|
||||
8461A2D21E145A08001DA4F8 /* UITableViewVibrantCell.swift */,
|
||||
65FF1B3D1DE321D8007B0845 /* SideMenu.h */,
|
||||
);
|
||||
name = Source;
|
||||
@@ -279,7 +279,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0800;
|
||||
LastUpgradeCheck = 0800;
|
||||
LastUpgradeCheck = 0830;
|
||||
ORGANIZATIONNAME = jonkykong;
|
||||
TargetAttributes = {
|
||||
7B48A0D21DCB2487002990A1 = {
|
||||
@@ -293,6 +293,7 @@
|
||||
};
|
||||
7B9DC9031DC6E8C1000D4007 = {
|
||||
CreatedOnToolsVersion = 8.0;
|
||||
LastSwiftMigration = 0820;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
@@ -464,10 +465,10 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
65FF1B441DE331D9007B0845 /* SideMenuManager.swift in Sources */,
|
||||
65FF1B451DE331D9007B0845 /* SideMenuTransition.swift in Sources */,
|
||||
65FF1B461DE331D9007B0845 /* UISideMenuNavigationController.swift in Sources */,
|
||||
65FF1B471DE331D9007B0845 /* UITableViewVibrantCell.swift in Sources */,
|
||||
8461A2D51E145A08001DA4F8 /* UISideMenuNavigationController.swift in Sources */,
|
||||
8461A2D41E145A08001DA4F8 /* SideMenuTransition.swift in Sources */,
|
||||
8461A2D61E145A08001DA4F8 /* UITableViewVibrantCell.swift in Sources */,
|
||||
8461A2D31E145A08001DA4F8 /* SideMenuManager.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -644,6 +645,7 @@
|
||||
7B9DC90D1DC6E8C1000D4007 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -657,6 +659,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jonkykong.SideMenu;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -664,6 +667,7 @@
|
||||
7B9DC90E1DC6E8C1000D4007 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0810"
|
||||
LastUpgradeVersion = "0830"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
//
|
||||
// SideMenuManager.swift
|
||||
//
|
||||
// Created by Jon Kent on 12/6/15.
|
||||
// Copyright © 2015 Jon Kent. All rights reserved.
|
||||
//
|
||||
|
||||
/* Example usage:
|
||||
// Define the menus
|
||||
SideMenuManager.menuLeftNavigationController = storyboard!.instantiateViewController(withIdentifier: "LeftMenuNavigationController") as? UISideMenuNavigationController
|
||||
SideMenuManager.menuRightNavigationController = storyboard!.instantiateViewController(withIdentifier: "RightMenuNavigationController") as? UISideMenuNavigationController
|
||||
|
||||
// Enable gestures. The left and/or right menus must be set up above for these to work.
|
||||
// Note that these continue to work on the Navigation Controller independent of the View Controller it displays!
|
||||
SideMenuManager.menuAddPanGestureToPresent(toView: self.navigationController!.navigationBar)
|
||||
SideMenuManager.menuAddScreenEdgePanGesturesToPresent(toView: self.navigationController!.view)
|
||||
*/
|
||||
|
||||
open class SideMenuManager : NSObject {
|
||||
|
||||
@objc public enum MenuPresentMode : Int {
|
||||
case menuSlideIn
|
||||
case viewSlideOut
|
||||
case viewSlideInOut
|
||||
case menuDissolveIn
|
||||
}
|
||||
|
||||
// Bounds which has been allocated for the app on the whole device screen
|
||||
internal static var appScreenRect: CGRect {
|
||||
let appWindowRect = UIApplication.shared.keyWindow?.bounds ?? UIWindow().bounds
|
||||
return appWindowRect
|
||||
}
|
||||
|
||||
/**
|
||||
The presentation mode of the menu.
|
||||
|
||||
There are four modes in MenuPresentMode:
|
||||
- menuSlideIn: Menu slides in over of the existing view.
|
||||
- viewSlideOut: The existing view slides out to reveal the menu.
|
||||
- viewSlideInOut: The existing view slides out while the menu slides in.
|
||||
- menuDissolveIn: The menu dissolves in over the existing view controller.
|
||||
*/
|
||||
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)
|
||||
|
||||
/// Duration of the animation when the menu is presented without gestures. Default is 0.35 seconds.
|
||||
open static var menuAnimationPresentDuration = 0.35
|
||||
|
||||
/// Duration of the animation when the menu is dismissed without gestures. Default is 0.35 seconds.
|
||||
open static var menuAnimationDismissDuration = 0.35
|
||||
|
||||
/// 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
|
||||
|
||||
/// The amount to scale the existing view controller or the menu view controller depending on the `menuPresentMode`. Default is 1 for no scaling. Less than 1 will shrink, greater than 1 will grow.
|
||||
open static var menuAnimationTransformScaleFactor: CGFloat = 1
|
||||
|
||||
/// The background color behind menu animations. Depending on the animation settings this may not be visible. If `menuFadeStatusBar` is true, this color is used to fade it. Default is black.
|
||||
open static var menuAnimationBackgroundColor: UIColor?
|
||||
|
||||
/// The shadow opacity around the menu view controller or existing view controller depending on the `menuPresentMode`. Default is 0.5 for 50% opacity.
|
||||
open static var menuShadowOpacity: Float = 0.5
|
||||
|
||||
/// The shadow color around the menu view controller or existing view controller depending on the `menuPresentMode`. Default is black.
|
||||
open static var menuShadowColor = UIColor.black
|
||||
|
||||
/// The radius of the shadow around the menu view controller or existing view controller depending on the `menuPresentMode`. Default is 5.
|
||||
open static var menuShadowRadius: CGFloat = 5
|
||||
|
||||
/// The left menu swipe to dismiss gesture.
|
||||
open static weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer?
|
||||
|
||||
/// The right menu swipe to dismiss gesture.
|
||||
open static weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer?
|
||||
|
||||
/// Enable or disable gestures that would swipe to present or dismiss the menu. Default is true.
|
||||
open static var menuEnableSwipeGestures: Bool = true
|
||||
|
||||
/// 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. Default is false.
|
||||
open static var menuPresentingViewControllerUserInteractionEnabled: Bool = false
|
||||
|
||||
/// The strength of the parallax effect on the existing view controller. Does not apply to `menuPresentMode` when set to `ViewSlideOut`. Default is 0.
|
||||
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
|
||||
|
||||
/// -Warning: Deprecated. Use `menuAnimationTransformScaleFactor` instead.
|
||||
@available(*, deprecated, renamed: "menuAnimationTransformScaleFactor")
|
||||
open static var menuAnimationShrinkStrength: CGFloat {
|
||||
get {
|
||||
return menuAnimationTransformScaleFactor
|
||||
}
|
||||
set {
|
||||
menuAnimationTransformScaleFactor = newValue
|
||||
}
|
||||
}
|
||||
|
||||
// prevent instantiation
|
||||
fileprivate override init() {}
|
||||
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
open static var menuBlurEffectStyle: UIBlurEffectStyle? {
|
||||
didSet {
|
||||
if oldValue != menuBlurEffectStyle {
|
||||
updateMenuBlurIfNecessary()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The left menu.
|
||||
open static var menuLeftNavigationController: UISideMenuNavigationController? {
|
||||
willSet {
|
||||
if menuLeftNavigationController?.presentingViewController == nil {
|
||||
removeMenuBlurForMenu(menuLeftNavigationController)
|
||||
}
|
||||
}
|
||||
didSet {
|
||||
guard oldValue?.presentingViewController == nil else {
|
||||
print("SideMenu Warning: menuLeftNavigationController cannot be modified while it's presented.")
|
||||
menuLeftNavigationController = oldValue
|
||||
return
|
||||
}
|
||||
setupNavigationController(menuLeftNavigationController, leftSide: true)
|
||||
}
|
||||
}
|
||||
|
||||
/// The right menu.
|
||||
open static var menuRightNavigationController: UISideMenuNavigationController? {
|
||||
willSet {
|
||||
if menuRightNavigationController?.presentingViewController == nil {
|
||||
removeMenuBlurForMenu(menuRightNavigationController)
|
||||
}
|
||||
}
|
||||
didSet {
|
||||
guard oldValue?.presentingViewController == nil else {
|
||||
print("SideMenu Warning: menuRightNavigationController cannot be modified while it's presented.")
|
||||
menuRightNavigationController = oldValue
|
||||
return
|
||||
}
|
||||
setupNavigationController(menuRightNavigationController, leftSide: false)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class func setupNavigationController(_ forMenu: UISideMenuNavigationController?, leftSide: Bool) {
|
||||
guard let forMenu = forMenu else {
|
||||
return
|
||||
}
|
||||
|
||||
let exitPanGesture = UIPanGestureRecognizer()
|
||||
exitPanGesture.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handleHideMenuPan(_:)))
|
||||
forMenu.view.addGestureRecognizer(exitPanGesture)
|
||||
forMenu.transitioningDelegate = SideMenuTransition.singleton
|
||||
forMenu.modalPresentationStyle = .overFullScreen
|
||||
forMenu.leftSide = leftSide
|
||||
if leftSide {
|
||||
menuLeftSwipeToDismissGesture = exitPanGesture
|
||||
} else {
|
||||
menuRightSwipeToDismissGesture = exitPanGesture
|
||||
}
|
||||
updateMenuBlurIfNecessary()
|
||||
}
|
||||
|
||||
fileprivate class func updateMenuBlurIfNecessary() {
|
||||
let menuBlurBlock = { (forMenu: UISideMenuNavigationController?) in
|
||||
if let forMenu = forMenu {
|
||||
setupMenuBlurForMenu(forMenu)
|
||||
}
|
||||
}
|
||||
|
||||
menuBlurBlock(menuLeftNavigationController)
|
||||
menuBlurBlock(menuRightNavigationController)
|
||||
}
|
||||
|
||||
fileprivate class func setupMenuBlurForMenu(_ forMenu: UISideMenuNavigationController?) {
|
||||
removeMenuBlurForMenu(forMenu)
|
||||
|
||||
guard let forMenu = forMenu,
|
||||
let menuBlurEffectStyle = menuBlurEffectStyle,
|
||||
let view = forMenu.visibleViewController?.view
|
||||
, !UIAccessibilityIsReduceTransparencyEnabled() else {
|
||||
return
|
||||
}
|
||||
|
||||
if forMenu.originalMenuBackgroundColor == nil {
|
||||
forMenu.originalMenuBackgroundColor = view.backgroundColor
|
||||
}
|
||||
|
||||
let blurEffect = UIBlurEffect(style: menuBlurEffectStyle)
|
||||
let blurView = UIVisualEffectView(effect: blurEffect)
|
||||
view.backgroundColor = UIColor.clear
|
||||
if let tableViewController = forMenu.visibleViewController as? UITableViewController {
|
||||
tableViewController.tableView.backgroundView = blurView
|
||||
tableViewController.tableView.separatorEffect = UIVibrancyEffect(blurEffect: blurEffect)
|
||||
tableViewController.tableView.reloadData()
|
||||
} else {
|
||||
blurView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
|
||||
blurView.frame = view.bounds
|
||||
view.insertSubview(blurView, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class func removeMenuBlurForMenu(_ forMenu: UISideMenuNavigationController?) {
|
||||
guard let forMenu = forMenu,
|
||||
let originalMenuBackgroundColor = forMenu.originalMenuBackgroundColor,
|
||||
let view = forMenu.visibleViewController?.view else {
|
||||
return
|
||||
}
|
||||
|
||||
view.backgroundColor = originalMenuBackgroundColor
|
||||
forMenu.originalMenuBackgroundColor = nil
|
||||
|
||||
if let tableViewController = forMenu.visibleViewController as? UITableViewController {
|
||||
tableViewController.tableView.backgroundView = nil
|
||||
tableViewController.tableView.separatorEffect = nil
|
||||
tableViewController.tableView.reloadData()
|
||||
} else if let blurView = view.subviews[0] as? UIVisualEffectView {
|
||||
blurView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Adds screen edge gestures to a view to present a menu.
|
||||
|
||||
- Parameter toView: The view to add gestures to.
|
||||
- Parameter forMenu: The menu (left or right) you want to add a gesture for. If unspecified, gestures will be added for both sides.
|
||||
|
||||
- Returns: The array of screen edge gestures added to `toView`.
|
||||
*/
|
||||
@discardableResult open class func menuAddScreenEdgePanGesturesToPresent(toView: UIView, forMenu:UIRectEdge? = nil) -> [UIScreenEdgePanGestureRecognizer] {
|
||||
var array = [UIScreenEdgePanGestureRecognizer]()
|
||||
|
||||
if forMenu != .right {
|
||||
let leftScreenEdgeGestureRecognizer = UIScreenEdgePanGestureRecognizer()
|
||||
leftScreenEdgeGestureRecognizer.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handlePresentMenuLeftScreenEdge(_:)))
|
||||
leftScreenEdgeGestureRecognizer.edges = .left
|
||||
leftScreenEdgeGestureRecognizer.cancelsTouchesInView = true
|
||||
toView.addGestureRecognizer(leftScreenEdgeGestureRecognizer)
|
||||
array.append(leftScreenEdgeGestureRecognizer)
|
||||
}
|
||||
|
||||
if forMenu != .left {
|
||||
let rightScreenEdgeGestureRecognizer = UIScreenEdgePanGestureRecognizer()
|
||||
rightScreenEdgeGestureRecognizer.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handlePresentMenuRightScreenEdge(_:)))
|
||||
rightScreenEdgeGestureRecognizer.edges = .right
|
||||
rightScreenEdgeGestureRecognizer.cancelsTouchesInView = true
|
||||
toView.addGestureRecognizer(rightScreenEdgeGestureRecognizer)
|
||||
array.append(rightScreenEdgeGestureRecognizer)
|
||||
}
|
||||
|
||||
return array
|
||||
}
|
||||
|
||||
/**
|
||||
Adds a pan edge gesture to a view to present menus.
|
||||
|
||||
- Parameter toView: The view to add a pan gesture to.
|
||||
|
||||
- Returns: The pan gesture added to `toView`.
|
||||
*/
|
||||
@discardableResult open class func menuAddPanGestureToPresent(toView: UIView) -> UIPanGestureRecognizer {
|
||||
let panGestureRecognizer = UIPanGestureRecognizer()
|
||||
panGestureRecognizer.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handlePresentMenuPan(_:)))
|
||||
toView.addGestureRecognizer(panGestureRecognizer)
|
||||
|
||||
return panGestureRecognizer
|
||||
}
|
||||
}
|
||||
@@ -1,460 +0,0 @@
|
||||
//
|
||||
// SideMenuTransition.swift
|
||||
// Pods
|
||||
//
|
||||
// Created by Jon Kent on 1/14/16.
|
||||
//
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
open class SideMenuTransition: UIPercentDrivenInteractiveTransition, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {
|
||||
|
||||
fileprivate var presenting = false
|
||||
fileprivate var interactive = false
|
||||
fileprivate static weak var originalSuperview: UIView?
|
||||
fileprivate static var switchMenus = false
|
||||
|
||||
internal static let singleton = SideMenuTransition()
|
||||
internal static var presentDirection: UIRectEdge = .left;
|
||||
internal static weak var tapView: UIView?
|
||||
internal static weak var statusBarView: UIView?
|
||||
|
||||
// prevent instantiation
|
||||
fileprivate override init() {}
|
||||
|
||||
fileprivate class var viewControllerForPresentedMenu: UIViewController? {
|
||||
get {
|
||||
return SideMenuManager.menuLeftNavigationController?.presentingViewController != nil ? SideMenuManager.menuLeftNavigationController?.presentingViewController : SideMenuManager.menuRightNavigationController?.presentingViewController
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class var visibleViewController: UIViewController? {
|
||||
get {
|
||||
return getVisibleViewControllerFromViewController(UIApplication.shared.keyWindow?.rootViewController)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class func getVisibleViewControllerFromViewController(_ viewController: UIViewController?) -> UIViewController? {
|
||||
if let navigationController = viewController as? UINavigationController {
|
||||
return getVisibleViewControllerFromViewController(navigationController.visibleViewController)
|
||||
} else if let tabBarController = viewController as? UITabBarController {
|
||||
return getVisibleViewControllerFromViewController(tabBarController.selectedViewController)
|
||||
} else if let presentedViewController = viewController?.presentedViewController {
|
||||
return getVisibleViewControllerFromViewController(presentedViewController)
|
||||
}
|
||||
|
||||
return viewController
|
||||
}
|
||||
|
||||
internal class func handlePresentMenuLeftScreenEdge(_ edge: UIScreenEdgePanGestureRecognizer) {
|
||||
SideMenuTransition.presentDirection = .left
|
||||
handlePresentMenuPan(edge)
|
||||
}
|
||||
|
||||
internal class func handlePresentMenuRightScreenEdge(_ edge: UIScreenEdgePanGestureRecognizer) {
|
||||
SideMenuTransition.presentDirection = .right
|
||||
handlePresentMenuPan(edge)
|
||||
}
|
||||
|
||||
internal class func handlePresentMenuPan(_ pan: UIPanGestureRecognizer) {
|
||||
guard SideMenuManager.menuEnableSwipeGestures else {
|
||||
return
|
||||
}
|
||||
|
||||
// how much distance have we panned in reference to the parent view?
|
||||
guard let view = viewControllerForPresentedMenu != nil ? viewControllerForPresentedMenu?.view : pan.view else {
|
||||
return
|
||||
}
|
||||
|
||||
let transform = view.transform
|
||||
view.transform = CGAffineTransform.identity
|
||||
let translation = pan.translation(in: pan.view!)
|
||||
view.transform = transform
|
||||
|
||||
// do some math to translate this to a percentage based value
|
||||
if !singleton.interactive {
|
||||
if translation.x == 0 {
|
||||
return // not sure which way the user is swiping yet, so do nothing
|
||||
}
|
||||
|
||||
if !(pan is UIScreenEdgePanGestureRecognizer) {
|
||||
SideMenuTransition.presentDirection = translation.x > 0 ? .left : .right
|
||||
}
|
||||
|
||||
if let menuViewController = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController : SideMenuManager.menuRightNavigationController,
|
||||
let visibleViewController = visibleViewController {
|
||||
singleton.interactive = true
|
||||
visibleViewController.present(menuViewController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
let direction: CGFloat = SideMenuTransition.presentDirection == .left ? 1 : -1
|
||||
let distance = translation.x / SideMenuManager.menuWidth
|
||||
// now lets deal with different states that the gesture recognizer sends
|
||||
switch (pan.state) {
|
||||
case .began, .changed:
|
||||
if pan is UIScreenEdgePanGestureRecognizer {
|
||||
singleton.update(min(distance * direction, 1))
|
||||
} else if distance > 0 && SideMenuTransition.presentDirection == .right && SideMenuManager.menuLeftNavigationController != nil {
|
||||
SideMenuTransition.presentDirection = .left
|
||||
switchMenus = true
|
||||
singleton.cancel()
|
||||
} else if distance < 0 && SideMenuTransition.presentDirection == .left && SideMenuManager.menuRightNavigationController != nil {
|
||||
SideMenuTransition.presentDirection = .right
|
||||
switchMenus = true
|
||||
singleton.cancel()
|
||||
} else {
|
||||
singleton.update(min(distance * direction, 1))
|
||||
}
|
||||
default:
|
||||
singleton.interactive = false
|
||||
view.transform = CGAffineTransform.identity
|
||||
let velocity = pan.velocity(in: pan.view!).x * direction
|
||||
view.transform = transform
|
||||
if velocity >= 100 || velocity >= -50 && abs(distance) >= 0.5 {
|
||||
// bug workaround: animation briefly resets after call to finishInteractiveTransition() but before animateTransition completion is called.
|
||||
if ProcessInfo().operatingSystemVersion.majorVersion == 8 && singleton.percentComplete > 1 - CGFloat(FLT_EPSILON) {
|
||||
singleton.update(0.9999)
|
||||
}
|
||||
singleton.finish()
|
||||
} else {
|
||||
singleton.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class func handleHideMenuPan(_ pan: UIPanGestureRecognizer) {
|
||||
if !SideMenuManager.menuEnableSwipeGestures {
|
||||
return
|
||||
}
|
||||
|
||||
let translation = pan.translation(in: pan.view!)
|
||||
let direction:CGFloat = SideMenuTransition.presentDirection == .left ? -1 : 1
|
||||
let distance = translation.x / SideMenuManager.menuWidth * direction
|
||||
|
||||
switch (pan.state) {
|
||||
|
||||
case .began:
|
||||
singleton.interactive = true
|
||||
viewControllerForPresentedMenu?.dismiss(animated: true, completion: nil)
|
||||
case .changed:
|
||||
singleton.update(max(min(distance, 1), 0))
|
||||
default:
|
||||
singleton.interactive = false
|
||||
let velocity = pan.velocity(in: pan.view!).x * direction
|
||||
if velocity >= 100 || velocity >= -50 && distance >= 0.5 {
|
||||
// bug workaround: animation briefly resets after call to finishInteractiveTransition() but before animateTransition completion is called.
|
||||
if ProcessInfo().operatingSystemVersion.majorVersion == 8 && singleton.percentComplete > 1 - CGFloat(FLT_EPSILON) {
|
||||
singleton.update(0.9999)
|
||||
}
|
||||
singleton.finish()
|
||||
} else {
|
||||
singleton.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class func handleHideMenuTap(_ tap: UITapGestureRecognizer) {
|
||||
viewControllerForPresentedMenu?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
internal class func hideMenuStart() {
|
||||
NotificationCenter.default.removeObserver(SideMenuTransition.singleton)
|
||||
guard let mainViewController = SideMenuTransition.viewControllerForPresentedMenu,
|
||||
let menuView = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController?.view : SideMenuManager.menuRightNavigationController?.view else {return}
|
||||
|
||||
menuView.transform = CGAffineTransform.identity
|
||||
mainViewController.view.transform = CGAffineTransform.identity
|
||||
mainViewController.view.alpha = 1
|
||||
SideMenuTransition.tapView?.frame = CGRect(x: 0, y: 0, width: mainViewController.view.frame.width, height: mainViewController.view.frame.height)
|
||||
menuView.frame.origin.y = 0
|
||||
menuView.frame.size.width = SideMenuManager.menuWidth
|
||||
menuView.frame.size.height = mainViewController.view.frame.height
|
||||
SideMenuTransition.statusBarView?.frame = UIApplication.shared.statusBarFrame
|
||||
SideMenuTransition.statusBarView?.alpha = 0
|
||||
|
||||
switch SideMenuManager.menuPresentMode {
|
||||
|
||||
case .viewSlideOut:
|
||||
menuView.alpha = 1 - SideMenuManager.menuAnimationFadeStrength
|
||||
menuView.frame.origin.x = SideMenuTransition.presentDirection == .left ? 0 : mainViewController.view.frame.width - SideMenuManager.menuWidth
|
||||
mainViewController.view.frame.origin.x = 0
|
||||
menuView.transform = CGAffineTransform(scaleX: SideMenuManager.menuAnimationTransformScaleFactor, y: SideMenuManager.menuAnimationTransformScaleFactor)
|
||||
|
||||
case .viewSlideInOut:
|
||||
menuView.alpha = 1
|
||||
menuView.frame.origin.x = SideMenuTransition.presentDirection == .left ? -menuView.frame.width : mainViewController.view.frame.width
|
||||
mainViewController.view.frame.origin.x = 0
|
||||
|
||||
case .menuSlideIn:
|
||||
menuView.alpha = 1
|
||||
menuView.frame.origin.x = SideMenuTransition.presentDirection == .left ? -menuView.frame.width : mainViewController.view.frame.width
|
||||
|
||||
case .menuDissolveIn:
|
||||
menuView.alpha = 0
|
||||
menuView.frame.origin.x = SideMenuTransition.presentDirection == .left ? 0 : mainViewController.view.frame.width - SideMenuManager.menuWidth
|
||||
mainViewController.view.frame.origin.x = 0
|
||||
}
|
||||
}
|
||||
|
||||
internal class func hideMenuComplete() {
|
||||
guard let mainViewController = SideMenuTransition.viewControllerForPresentedMenu,
|
||||
let menuView = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController?.view : SideMenuManager.menuRightNavigationController?.view else {
|
||||
return
|
||||
}
|
||||
|
||||
SideMenuTransition.tapView?.removeFromSuperview()
|
||||
SideMenuTransition.statusBarView?.removeFromSuperview()
|
||||
mainViewController.view.motionEffects.removeAll()
|
||||
mainViewController.view.layer.shadowOpacity = 0
|
||||
menuView.layer.shadowOpacity = 0
|
||||
if let topNavigationController = mainViewController as? UINavigationController {
|
||||
topNavigationController.interactivePopGestureRecognizer!.isEnabled = true
|
||||
}
|
||||
originalSuperview?.addSubview(mainViewController.view)
|
||||
}
|
||||
|
||||
internal class func presentMenuStart(forSize size: CGSize = SideMenuManager.appScreenRect.size) {
|
||||
guard let menuView = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController?.view : SideMenuManager.menuRightNavigationController?.view,
|
||||
let mainViewController = SideMenuTransition.viewControllerForPresentedMenu else {
|
||||
return
|
||||
}
|
||||
|
||||
menuView.transform = CGAffineTransform.identity
|
||||
mainViewController.view.transform = CGAffineTransform.identity
|
||||
menuView.frame.size.width = SideMenuManager.menuWidth
|
||||
menuView.frame.size.height = size.height
|
||||
menuView.frame.origin.x = SideMenuTransition.presentDirection == .left ? 0 : size.width - SideMenuManager.menuWidth
|
||||
SideMenuTransition.statusBarView?.frame = UIApplication.shared.statusBarFrame
|
||||
SideMenuTransition.statusBarView?.alpha = 1
|
||||
|
||||
switch SideMenuManager.menuPresentMode {
|
||||
|
||||
case .viewSlideOut:
|
||||
menuView.alpha = 1
|
||||
let direction:CGFloat = SideMenuTransition.presentDirection == .left ? 1 : -1
|
||||
mainViewController.view.frame.origin.x = direction * (menuView.frame.width)
|
||||
mainViewController.view.layer.shadowColor = SideMenuManager.menuShadowColor.cgColor
|
||||
mainViewController.view.layer.shadowRadius = SideMenuManager.menuShadowRadius
|
||||
mainViewController.view.layer.shadowOpacity = SideMenuManager.menuShadowOpacity
|
||||
mainViewController.view.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
|
||||
case .viewSlideInOut:
|
||||
menuView.alpha = 1
|
||||
mainViewController.view.layer.shadowColor = SideMenuManager.menuShadowColor.cgColor
|
||||
mainViewController.view.layer.shadowRadius = SideMenuManager.menuShadowRadius
|
||||
mainViewController.view.layer.shadowOpacity = SideMenuManager.menuShadowOpacity
|
||||
mainViewController.view.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
let direction:CGFloat = SideMenuTransition.presentDirection == .left ? 1 : -1
|
||||
mainViewController.view.frame = CGRect(x: direction * (menuView.frame.width), y: 0, width: size.width, height: size.height)
|
||||
mainViewController.view.transform = CGAffineTransform(scaleX: SideMenuManager.menuAnimationTransformScaleFactor, y: SideMenuManager.menuAnimationTransformScaleFactor)
|
||||
mainViewController.view.alpha = 1 - SideMenuManager.menuAnimationFadeStrength
|
||||
|
||||
case .menuSlideIn, .menuDissolveIn:
|
||||
menuView.alpha = 1
|
||||
if SideMenuManager.menuBlurEffectStyle == nil {
|
||||
menuView.layer.shadowColor = SideMenuManager.menuShadowColor.cgColor
|
||||
menuView.layer.shadowRadius = SideMenuManager.menuShadowRadius
|
||||
menuView.layer.shadowOpacity = SideMenuManager.menuShadowOpacity
|
||||
menuView.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
}
|
||||
mainViewController.view.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
|
||||
mainViewController.view.transform = CGAffineTransform(scaleX: SideMenuManager.menuAnimationTransformScaleFactor, y: SideMenuManager.menuAnimationTransformScaleFactor)
|
||||
mainViewController.view.alpha = 1 - SideMenuManager.menuAnimationFadeStrength
|
||||
}
|
||||
}
|
||||
|
||||
internal class func presentMenuComplete() {
|
||||
NotificationCenter.default.addObserver(SideMenuTransition.singleton, selector:#selector(SideMenuTransition.applicationDidEnterBackgroundNotification), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
|
||||
guard let mainViewController = SideMenuTransition.viewControllerForPresentedMenu else {
|
||||
return
|
||||
}
|
||||
|
||||
switch SideMenuManager.menuPresentMode {
|
||||
case .menuSlideIn, .menuDissolveIn, .viewSlideInOut:
|
||||
if SideMenuManager.menuParallaxStrength != 0 {
|
||||
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
|
||||
horizontal.minimumRelativeValue = -SideMenuManager.menuParallaxStrength
|
||||
horizontal.maximumRelativeValue = SideMenuManager.menuParallaxStrength
|
||||
|
||||
let vertical = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis)
|
||||
vertical.minimumRelativeValue = -SideMenuManager.menuParallaxStrength
|
||||
vertical.maximumRelativeValue = SideMenuManager.menuParallaxStrength
|
||||
|
||||
let group = UIMotionEffectGroup()
|
||||
group.motionEffects = [horizontal, vertical]
|
||||
mainViewController.view.addMotionEffect(group)
|
||||
}
|
||||
case .viewSlideOut: break;
|
||||
}
|
||||
if let topNavigationController = mainViewController as? UINavigationController {
|
||||
topNavigationController.interactivePopGestureRecognizer!.isEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: UIViewControllerAnimatedTransitioning protocol methods
|
||||
|
||||
// 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
|
||||
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)!)
|
||||
|
||||
// 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 menuView = menuViewController.view
|
||||
let topView = topViewController.view
|
||||
|
||||
// prepare menu items to slide in
|
||||
if presenting {
|
||||
var tapView: UIView?
|
||||
if !SideMenuManager.menuPresentingViewControllerUserInteractionEnabled {
|
||||
tapView = UIView()
|
||||
tapView!.autoresizingMask = [.flexibleHeight, .flexibleWidth]
|
||||
let exitPanGesture = UIPanGestureRecognizer()
|
||||
exitPanGesture.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handleHideMenuPan(_:)))
|
||||
let exitTapGesture = UITapGestureRecognizer()
|
||||
exitTapGesture.addTarget(SideMenuTransition.self, action: #selector(SideMenuTransition.handleHideMenuTap(_:)))
|
||||
tapView!.addGestureRecognizer(exitPanGesture)
|
||||
tapView!.addGestureRecognizer(exitTapGesture)
|
||||
SideMenuTransition.tapView = tapView
|
||||
}
|
||||
|
||||
SideMenuTransition.originalSuperview = topView?.superview
|
||||
|
||||
// add the both views to our view controller
|
||||
switch SideMenuManager.menuPresentMode {
|
||||
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 blackBar = UIView()
|
||||
if let menuShrinkBackgroundColor = SideMenuManager.menuAnimationBackgroundColor {
|
||||
blackBar.backgroundColor = menuShrinkBackgroundColor
|
||||
} else {
|
||||
blackBar.backgroundColor = UIColor.black
|
||||
}
|
||||
blackBar.isUserInteractionEnabled = false
|
||||
container.addSubview(blackBar)
|
||||
SideMenuTransition.statusBarView = blackBar
|
||||
}
|
||||
|
||||
SideMenuTransition.hideMenuStart() // offstage for interactive
|
||||
}
|
||||
|
||||
// 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
|
||||
if self.presenting {
|
||||
SideMenuTransition.presentMenuStart() // onstage items: slide in
|
||||
} else {
|
||||
SideMenuTransition.hideMenuStart()
|
||||
}
|
||||
menuView?.isUserInteractionEnabled = false
|
||||
}) { (finished) -> Void in
|
||||
// tell our transitionContext object that we've finished animating
|
||||
if transitionContext.transitionWasCancelled {
|
||||
let viewControllerForPresentedMenu = SideMenuTransition.viewControllerForPresentedMenu
|
||||
|
||||
if self.presenting {
|
||||
SideMenuTransition.hideMenuComplete()
|
||||
} else {
|
||||
SideMenuTransition.presentMenuComplete()
|
||||
}
|
||||
menuView?.isUserInteractionEnabled = true
|
||||
|
||||
transitionContext.completeTransition(false)
|
||||
|
||||
if SideMenuTransition.switchMenus {
|
||||
SideMenuTransition.switchMenus = false
|
||||
viewControllerForPresentedMenu?.present(SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController! : SideMenuManager.menuRightNavigationController!, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if self.presenting {
|
||||
SideMenuTransition.presentMenuComplete()
|
||||
menuView?.isUserInteractionEnabled = true
|
||||
transitionContext.completeTransition(true)
|
||||
switch SideMenuManager.menuPresentMode {
|
||||
case .viewSlideOut, .viewSlideInOut:
|
||||
container.addSubview(topView!)
|
||||
case .menuSlideIn, .menuDissolveIn:
|
||||
container.insertSubview(topView!, at: 0)
|
||||
}
|
||||
if let statusBarView = SideMenuTransition.statusBarView {
|
||||
container.bringSubview(toFront: statusBarView)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
SideMenuTransition.hideMenuComplete()
|
||||
transitionContext.completeTransition(true)
|
||||
menuView?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
// return how many seconds the transiton animation will take
|
||||
open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return presenting ? SideMenuManager.menuAnimationPresentDuration : SideMenuManager.menuAnimationDismissDuration
|
||||
}
|
||||
|
||||
// MARK: UIViewControllerTransitioningDelegate protocol methods
|
||||
|
||||
// return the animator when presenting a viewcontroller
|
||||
// rememeber that an animator (or animation controller) is any object that aheres to the UIViewControllerAnimatedTransitioning protocol
|
||||
open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
self.presenting = true
|
||||
SideMenuTransition.presentDirection = presented == SideMenuManager.menuLeftNavigationController ? .left : .right
|
||||
return self
|
||||
}
|
||||
|
||||
// return the animator used when dismissing from a viewcontroller
|
||||
open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
presenting = false
|
||||
return self
|
||||
}
|
||||
|
||||
open func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
// if our interactive flag is true, return the transition manager object
|
||||
// otherwise return nil
|
||||
return interactive ? SideMenuTransition.singleton : nil
|
||||
}
|
||||
|
||||
open func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
return interactive ? SideMenuTransition.singleton : nil
|
||||
}
|
||||
|
||||
internal func applicationDidEnterBackgroundNotification() {
|
||||
if let menuViewController: UINavigationController = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController : SideMenuManager.menuRightNavigationController {
|
||||
SideMenuTransition.hideMenuStart()
|
||||
SideMenuTransition.hideMenuComplete()
|
||||
menuViewController.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
//
|
||||
// UISideMenuNavigationController.swift
|
||||
//
|
||||
// Created by Jon Kent on 1/14/16.
|
||||
// Copyright © 2016 Jon Kent. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
open class UISideMenuNavigationController: UINavigationController {
|
||||
|
||||
internal var originalMenuBackgroundColor: UIColor?
|
||||
|
||||
open override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
// if this isn't set here, segues cause viewWillAppear and viewDidAppear to be called twice
|
||||
// likely because the transition completes and the presentingViewController is added back
|
||||
// into view for the default transition style.
|
||||
modalPresentationStyle = .overFullScreen
|
||||
}
|
||||
|
||||
/// Whether the menu appears on the right or left side of the screen. Right is the default.
|
||||
@IBInspectable open var leftSide:Bool = false {
|
||||
didSet {
|
||||
if isViewLoaded && oldValue != leftSide { // suppress warnings
|
||||
didSetSide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override open func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
didSetSide()
|
||||
}
|
||||
|
||||
fileprivate func didSetSide() {
|
||||
if leftSide {
|
||||
SideMenuManager.menuLeftNavigationController = self
|
||||
} else {
|
||||
SideMenuManager.menuRightNavigationController = self
|
||||
}
|
||||
}
|
||||
|
||||
override open func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
// we had presented a view before, so lets dismiss ourselves as already acted upon
|
||||
if view.isHidden {
|
||||
SideMenuTransition.hideMenuComplete()
|
||||
dismiss(animated: false, completion: { () -> Void in
|
||||
self.view.isHidden = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override open func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
// when presenting a view controller from the menu, the menu view gets moved into another transition view above our transition container
|
||||
// which can break the visual layout we had before. So, we move the menu view back to its original transition view to preserve it.
|
||||
if !isBeingDismissed {
|
||||
if let mainView = presentingViewController?.view {
|
||||
switch SideMenuManager.menuPresentMode {
|
||||
case .viewSlideOut, .viewSlideInOut:
|
||||
mainView.superview?.insertSubview(view, belowSubview: mainView)
|
||||
case .menuSlideIn, .menuDissolveIn:
|
||||
if let tapView = SideMenuTransition.tapView {
|
||||
mainView.superview?.insertSubview(view, aboveSubview: tapView)
|
||||
} else {
|
||||
mainView.superview?.insertSubview(view, aboveSubview: mainView)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override open func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
// we're presenting a view controller from the menu, so we need to hide the menu so it isn't g when the presented view is dismissed.
|
||||
if !isBeingDismissed {
|
||||
view.isHidden = true
|
||||
SideMenuTransition.hideMenuStart()
|
||||
}
|
||||
}
|
||||
|
||||
override open func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
|
||||
// don't bother resizing if the view isn't visible
|
||||
if view.isHidden {
|
||||
return
|
||||
}
|
||||
|
||||
SideMenuTransition.statusBarView?.isHidden = true
|
||||
coordinator.animate(alongsideTransition: { (context) -> Void in
|
||||
SideMenuTransition.presentMenuStart(forSize: size)
|
||||
}) { (context) -> Void in
|
||||
SideMenuTransition.statusBarView?.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
override open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if let menuViewController: UINavigationController = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController : SideMenuManager.menuRightNavigationController,
|
||||
let presentingViewController = menuViewController.presentingViewController as? UINavigationController {
|
||||
presentingViewController.prepare(for: segue, sender: sender)
|
||||
}
|
||||
}
|
||||
|
||||
override open func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||
if let menuViewController: UINavigationController = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController : SideMenuManager.menuRightNavigationController,
|
||||
let presentingViewController = menuViewController.presentingViewController as? UINavigationController {
|
||||
return presentingViewController.shouldPerformSegue(withIdentifier: identifier, sender: sender)
|
||||
}
|
||||
|
||||
return super.shouldPerformSegue(withIdentifier: identifier, sender: sender)
|
||||
}
|
||||
|
||||
override open func pushViewController(_ viewController: UIViewController, animated: Bool) {
|
||||
guard viewControllers.count > 0 && !SideMenuManager.menuAllowSubmenus else {
|
||||
// NOTE: pushViewController is called by init(rootViewController: UIViewController)
|
||||
// so we must perform the normal super method in this case.
|
||||
super.pushViewController(viewController, animated: true)
|
||||
return
|
||||
}
|
||||
|
||||
guard let presentingViewController = presentingViewController as? UINavigationController else {
|
||||
print("SideMenu Warning: attempt to push a View Controller from \(self.presentingViewController.self) where its navigationController == nil. It must be embedded in a Navigation Controller for this to work.")
|
||||
return
|
||||
}
|
||||
|
||||
// to avoid overlapping dismiss & pop/push calls, create a transaction block where the menu
|
||||
// is dismissed after showing the appropriate screen
|
||||
CATransaction.begin()
|
||||
CATransaction.setCompletionBlock( { () -> Void in
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
self.visibleViewController?.viewWillAppear(false) // Hack: force selection to get cleared on UITableViewControllers when reappearing using custom transitions
|
||||
})
|
||||
|
||||
UIView.animate(withDuration: SideMenuManager.menuAnimationDismissDuration, animations: { () -> Void in
|
||||
SideMenuTransition.hideMenuStart()
|
||||
})
|
||||
|
||||
if SideMenuManager.menuAllowPopIfPossible {
|
||||
for subViewController in presentingViewController.viewControllers {
|
||||
if type(of: subViewController) == type(of: viewController) {
|
||||
presentingViewController.popToViewController(subViewController, animated: animated)
|
||||
CATransaction.commit()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if let lastViewController = presentingViewController.viewControllers.last, SideMenuManager.menuAllowPushOfSameClassTwice {
|
||||
if type(of: lastViewController) == type(of: viewController) {
|
||||
CATransaction.commit()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
presentingViewController.pushViewController(viewController, animated: animated)
|
||||
CATransaction.commit()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
//
|
||||
// UITableViewVibrantCell.swift
|
||||
// Pods
|
||||
//
|
||||
// Created by Jon Kent on 1/14/16.
|
||||
//
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
open class UITableViewVibrantCell: UITableViewCell {
|
||||
|
||||
fileprivate var vibrancyView:UIVisualEffectView = UIVisualEffectView()
|
||||
fileprivate var vibrancySelectedBackgroundView:UIVisualEffectView = UIVisualEffectView()
|
||||
fileprivate var defaultSelectedBackgroundView:UIView?
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
vibrancyView.frame = bounds
|
||||
vibrancyView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
|
||||
for view in subviews {
|
||||
vibrancyView.contentView.addSubview(view)
|
||||
}
|
||||
addSubview(vibrancyView)
|
||||
|
||||
let blurSelectionEffect = UIBlurEffect(style: .light)
|
||||
vibrancySelectedBackgroundView.effect = blurSelectionEffect
|
||||
defaultSelectedBackgroundView = selectedBackgroundView
|
||||
}
|
||||
|
||||
override open func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
// shouldn't be needed but backgroundColor is set to white on iPad:
|
||||
backgroundColor = UIColor.clear
|
||||
|
||||
if !UIAccessibilityIsReduceTransparencyEnabled() && SideMenuManager.menuBlurEffectStyle != nil {
|
||||
let blurEffect = UIBlurEffect(style: SideMenuManager.menuBlurEffectStyle!)
|
||||
vibrancyView.effect = UIVibrancyEffect(blurEffect: blurEffect)
|
||||
|
||||
if selectedBackgroundView != nil && selectedBackgroundView != vibrancySelectedBackgroundView {
|
||||
vibrancySelectedBackgroundView.contentView.addSubview(selectedBackgroundView!)
|
||||
selectedBackgroundView = vibrancySelectedBackgroundView
|
||||
}
|
||||
} else {
|
||||
vibrancyView.effect = nil
|
||||
selectedBackgroundView = defaultSelectedBackgroundView
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user