Compare commits

...

59 Commits

Author SHA1 Message Date
jonkykong a2f299bb5f Updated podspec. 2017-10-11 14:43:08 -07:00
jonkykong 031f97d770 Update README for Cocoapods. 2017-10-11 14:40:45 -07:00
jonkykong 977f9a5b70 README correction. 2017-10-11 14:39:34 -07:00
jonkykong 3a14516905 Better logic for managing UISideMenuNavigationController properties across interface builder and programmatic instantiations. 2017-10-11 14:39:22 -07:00
jonkykong ddefba9c87 Revert README for Github. 2017-10-10 18:37:00 -07:00
jonkykong d3f59a45a7 Merge tag '3.1'
* tag '3.1':
  Updated README for Cocoapods.
  Updated podspec.
  Update README.md
  Updated README.
  Fix for non-animated dismissal of menu causing a black screen.
  Renamed class and refactored how blurring works in UITableViewVibrantCell.
  Major project update to support multiple SideMenuManager instances beyond the primary instance, if necessary.
  Added isHidden property.
  Support for delegate methods on SideMenu appearing/disappearing.
  Refactor.
  Refactoring.
  Fix for menuDismissOnPush as it was incorrectly applied.
  Fix for regression on enums to objective-C.
2017-10-10 18:36:29 -07:00
jonkykong 1d2f233e7f Updated README for Cocoapods. 2017-10-10 18:33:31 -07:00
jonkykong 2a650bec7f no message 2017-10-10 18:31:05 -07:00
jonkykong a8cb38c798 Updated podspec. 2017-10-10 18:27:15 -07:00
Jon Kent b7386851df Update README.md 2017-10-10 18:26:53 -07:00
jonkykong 6e1cb822bf Updated README. 2017-10-10 17:37:13 -07:00
jonkykong 10197c53cf Fix for non-animated dismissal of menu causing a black screen. 2017-10-10 17:37:00 -07:00
jonkykong 90e252dc78 Renamed class and refactored how blurring works in UITableViewVibrantCell. 2017-10-10 17:36:29 -07:00
jonkykong 553cdeae98 Major project update to support multiple SideMenuManager instances beyond the primary instance, if necessary. 2017-10-10 17:24:07 -07:00
jonkykong 26b26c4862 Added isHidden property. 2017-10-06 16:56:40 -07:00
jonkykong 05f8012c66 Support for delegate methods on SideMenu appearing/disappearing. 2017-10-06 16:56:25 -07:00
jonkykong 45586dd398 Refactor. 2017-10-06 16:55:05 -07:00
jonkykong 92375186c8 Refactoring. 2017-10-06 16:52:57 -07:00
jonkykong c63f86feae Fix for menuDismissOnPush as it was incorrectly applied. 2017-10-06 16:51:39 -07:00
jonkykong 3e01179dec Fix for regression on enums to objective-C. 2017-10-06 16:43:01 -07:00
jonkykong 8a3ebd672e Revert README for Github. 2017-09-26 23:50:54 -07:00
jonkykong c6a04437b6 Merge tag '3.0.2'
* tag '3.0.2':
  Updated podspec and README for cocoapods.
  Corrected logic for default menu width.
2017-09-26 23:50:08 -07:00
jonkykong bf241ec554 Updated podspec and README for cocoapods. 2017-09-26 23:46:56 -07:00
jonkykong d867a67899 Corrected logic for default menu width. 2017-09-26 23:42:32 -07:00
Jon Kent 80213f5e88 Merge pull request #261 from jonkykong/add-code-of-conduct-1-1
Create CODE_OF_CONDUCT.md
2017-09-21 13:41:10 -07:00
Jon Kent 66cc6cad35 Create CODE_OF_CONDUCT.md 2017-09-21 13:40:46 -07:00
Jon Kent db97060777 Update README.md 2017-09-21 13:31:08 -07:00
Jon Kent 4d32054356 Update README.md 2017-09-21 04:49:43 -07:00
jonkykong 432dfe988f Updated README for Github. 2017-09-21 04:47:27 -07:00
jonkykong 1e8f091bb0 Merge tag '3.0.1'
* tag '3.0.1':
  Updated podspec.
  Fixed hack to more specific logic.
2017-09-21 04:41:45 -07:00
jonkykong f2df2e28d8 Updated podspec. 2017-09-21 04:41:11 -07:00
jonkykong b6a2466dec Fixed hack to more specific logic. 2017-09-21 04:40:42 -07:00
jonkykong 8bbe75d5c7 Merge tag '3.0.0'
* tag '3.0.0':
  Updated podspec.
  Additional Swift4 support updates.
  Fix for Swift 4 and version 2.3.4
2017-09-21 04:25:21 -07:00
jonkykong ad462962fd Updated podspec. 2017-09-21 04:23:19 -07:00
jonkykong 6ed444f433 Merge branch 'pr/253' into 3.0.0
* pr/253:
  Additional Swift4 support updates.
  Fix for Swift 4 and version 2.3.4
2017-09-21 04:12:50 -07:00
jonkykong a65e26cb47 Additional Swift4 support updates. 2017-09-21 02:59:40 -07:00
jonkykong 94610771b4 Merge tag '2.3.4'
* tag '2.3.4':
  Updated README and podspec.
  Added comment.
  Changed menuAnimationCompleteGestureDuration default.
  Changing individual menu width properties in favor of a property on UISideMenuNavigationController.
  Refactor.
  Formatting.
  Support for UISplitViewControllers when pushing view controllers.
  These methods will never be called as they are handled by the topViewController.
  Neglected to use menuDismissOnPush switch in code.
  Fixes to maintain iOS 11 animation smoothness.
  Refactor.
  Changed MainViewController's scrollview to align to the topLayoutGuide.
  Add missing designated initialiser to UITableViewVibrantCell
  Set right menu width equal half of left menu width in example
  Add supporting of different left and right menu width
2017-09-21 02:04:58 -07:00
jonkykong 5599796e9d Updated README and podspec. 2017-09-21 02:04:06 -07:00
jonkykong 33449c84a0 Merge branch 'pr/248' into 2.3.4
* pr/248:
  Added comment.
  Add missing designated initialiser to UITableViewVibrantCell
2017-09-21 01:53:02 -07:00
jonkykong 458b246013 Added comment. 2017-09-21 01:52:44 -07:00
jonkykong a58a170979 Changed menuAnimationCompleteGestureDuration default. 2017-09-21 01:35:46 -07:00
jonkykong e4c43569de Merge branch 'pr/228' into 2.3.4
* pr/228:
  Changing individual menu width properties in favor of a property on UISideMenuNavigationController.
  Set right menu width equal half of left menu width in example
  Add supporting of different left and right menu width

# Conflicts:
#	Pod/Classes/SideMenuTransition.swift
2017-09-21 01:34:34 -07:00
jonkykong 966e1c9976 Changing individual menu width properties in favor of a property on UISideMenuNavigationController. 2017-09-21 01:30:58 -07:00
jonkykong 1241215e53 Refactor. 2017-09-20 23:25:19 -07:00
jonkykong 09070849d8 Formatting. 2017-09-20 22:54:04 -07:00
jonkykong 8705078fa5 Support for UISplitViewControllers when pushing view controllers. 2017-09-20 22:51:40 -07:00
jonkykong 48a39b4573 These methods will never be called as they are handled by the topViewController. 2017-09-20 22:50:23 -07:00
jonkykong 3611a6f5b8 Neglected to use menuDismissOnPush switch in code. 2017-09-20 22:49:30 -07:00
jonkykong 701d482714 Fixes to maintain iOS 11 animation smoothness. 2017-09-20 22:48:39 -07:00
jonkykong 9574c37479 Refactor. 2017-09-20 22:48:14 -07:00
jonkykong 52faf9a7e1 Changed MainViewController's scrollview to align to the topLayoutGuide. 2017-09-20 22:47:56 -07:00
Ranjithkumar Matheswaran 05a3659255 Fix for Swift 4 and version 2.3.4 2017-08-31 18:30:48 +05:30
Mark Woollard ef0c5fceed Add missing designated initialiser to UITableViewVibrantCell
So cell can be registered with UITableView without subclassing for simple use cases. Without this initialiser dequeuing instance of the cell will cause a crash.
2017-08-18 10:36:38 +01:00
Alex Kozin eab7e274e8 Set right menu width equal half of left menu width in example 2017-07-20 18:16:59 +07:00
Alex Kozin f9ec208e36 Add supporting of different left and right menu width 2017-07-20 18:16:01 +07:00
Jon Kent 6b9607fc9a Update README.md 2017-07-02 12:33:44 -07:00
jonkykong 8ad4b3a4d9 Revert README for Github. 2017-06-30 16:56:34 -07:00
jonkykong b0357d3f54 Merge tag '2.3.3'
* tag '2.3.3':
  Updated podspec and README.
  Added menuDismissOnPush property.
  Fix for single menu setups losing ability to swipe menu if wrong side is swiped.
2017-06-30 16:51:36 -07:00
Jon Kent 2e54a9d21a Create ISSUE_TEMPLATE.md 2017-06-08 01:45:53 -07:00
15 changed files with 1145 additions and 579 deletions
+1 -2
View File
@@ -1,5 +1,4 @@
<!--- 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 -->
<!--- IF YOU DELETE OR IGNORE THIS TEMPLATE YOUR ISSUE WILL BE IGNORED AND CLOSED -->
## 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:
+46
View File
@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at yo@massappeal.co. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
+10 -12
View File
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="yAA-s6-Bam">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13196" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="yAA-s6-Bam">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13173"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -15,7 +15,7 @@
<objects>
<navigationController id="yAA-s6-Bam" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" id="Bue-4e-bCJ">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<gestureRecognizers/>
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -42,7 +42,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LFJ-SB-Zp9">
<rect key="frame" x="0.0" y="0.0" width="375" height="635"/>
<rect key="frame" x="0.0" y="64" width="375" height="571"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DMn-tw-NTB">
<rect key="frame" x="0.0" y="0.0" width="375" height="469"/>
@@ -205,9 +205,9 @@
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="LFJ-SB-Zp9" firstAttribute="top" secondItem="0gg-po-Ih1" secondAttribute="topMargin" id="0T7-Dc-CaB"/>
<constraint firstItem="LFJ-SB-Zp9" firstAttribute="leading" secondItem="0gg-po-Ih1" secondAttribute="leading" id="OsA-4z-Y7t"/>
<constraint firstItem="ma4-O6-wOT" firstAttribute="top" secondItem="LFJ-SB-Zp9" secondAttribute="bottom" constant="8" id="UUi-lo-0RA"/>
<constraint firstItem="LFJ-SB-Zp9" firstAttribute="top" secondItem="Rm7-bv-OCN" secondAttribute="bottom" id="W6M-QX-I2w"/>
<constraint firstItem="ma4-O6-wOT" firstAttribute="centerX" secondItem="0gg-po-Ih1" secondAttribute="centerX" id="aia-PT-OH3"/>
<constraint firstItem="LW0-9z-RHu" firstAttribute="top" secondItem="ma4-O6-wOT" secondAttribute="bottom" constant="8" id="d5Q-Sj-0Xz"/>
<constraint firstItem="LFJ-SB-Zp9" firstAttribute="top" secondItem="Rm7-bv-OCN" secondAttribute="bottom" id="ssi-ps-a2k"/>
@@ -252,7 +252,6 @@
<navigationController storyboardIdentifier="LeftMenuNavigationController" navigationBarHidden="YES" id="DuX-EW-0mP" customClass="UISideMenuNavigationController" customModule="SideMenu" sceneMemberID="viewController">
<navigationItem key="navigationItem" id="ipz-Lx-Wgf"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="35F-wh-r6h">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<userDefinedRuntimeAttributes>
@@ -266,10 +265,10 @@
</objects>
<point key="canvasLocation" x="961.875" y="8.4507042253521121"/>
</scene>
<!--Side Menu Table View-->
<!--Side Menu Table View Controller-->
<scene sceneID="L2w-o7-jEN">
<objects>
<tableViewController id="V6g-oO-Tbm" customClass="SideMenuTableView" customModule="Example" customModuleProvider="target" sceneMemberID="viewController">
<tableViewController id="V6g-oO-Tbm" customClass="SideMenuTableViewController" customModule="Example" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" scrollEnabled="NO" dataMode="static" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="Dqh-bs-m2M">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -387,7 +386,6 @@
<objects>
<navigationController storyboardIdentifier="RightMenuNavigationController" navigationBarHidden="YES" id="z7k-fk-pfc" customClass="UISideMenuNavigationController" customModule="SideMenu" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" id="qOd-yQ-2i8">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
@@ -398,10 +396,10 @@
</objects>
<point key="canvasLocation" x="961.875" y="698.23943661971828"/>
</scene>
<!--Side Menu Table View-->
<!--Side Menu Table View Controller-->
<scene sceneID="l9t-dY-QwR">
<objects>
<tableViewController id="Ol4-YR-KUL" customClass="SideMenuTableView" customModule="Example" customModuleProvider="target" sceneMemberID="viewController">
<tableViewController id="Ol4-YR-KUL" customClass="SideMenuTableViewController" customModule="Example" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" scrollEnabled="NO" dataMode="static" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="49z-eA-JB7">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -589,7 +587,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="a17-pq-WAQ">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
<state key="normal" title="Dismiss">
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
+41 -20
View File
@@ -26,47 +26,47 @@ class MainViewController: UIViewController {
fileprivate func setupSideMenu() {
// Define the menus
SideMenuManager.menuLeftNavigationController = storyboard!.instantiateViewController(withIdentifier: "LeftMenuNavigationController") as? UISideMenuNavigationController
SideMenuManager.menuRightNavigationController = storyboard!.instantiateViewController(withIdentifier: "RightMenuNavigationController") as? UISideMenuNavigationController
SideMenuManager.default.menuLeftNavigationController = storyboard!.instantiateViewController(withIdentifier: "LeftMenuNavigationController") as? UISideMenuNavigationController
SideMenuManager.default.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)
SideMenuManager.default.menuAddPanGestureToPresent(toView: self.navigationController!.navigationBar)
SideMenuManager.default.menuAddScreenEdgePanGesturesToPresent(toView: self.navigationController!.view)
// Set up a cool background image for demo purposes
SideMenuManager.menuAnimationBackgroundColor = UIColor(patternImage: UIImage(named: "background")!)
SideMenuManager.default.menuAnimationBackgroundColor = UIColor(patternImage: UIImage(named: "background")!)
}
fileprivate func setDefaults() {
let modes:[SideMenuManager.MenuPresentMode] = [.menuSlideIn, .viewSlideOut, .menuDissolveIn]
presentModeSegmentedControl.selectedSegmentIndex = modes.index(of: SideMenuManager.menuPresentMode)!
presentModeSegmentedControl.selectedSegmentIndex = modes.index(of: SideMenuManager.default.menuPresentMode)!
let styles:[UIBlurEffectStyle] = [.dark, .light, .extraLight]
if let menuBlurEffectStyle = SideMenuManager.menuBlurEffectStyle {
if let menuBlurEffectStyle = SideMenuManager.default.menuBlurEffectStyle {
blurSegmentControl.selectedSegmentIndex = styles.index(of: menuBlurEffectStyle) ?? 0
} else {
blurSegmentControl.selectedSegmentIndex = 0
}
darknessSlider.value = Float(SideMenuManager.menuAnimationFadeStrength)
shadowOpacitySlider.value = Float(SideMenuManager.menuShadowOpacity)
shrinkFactorSlider.value = Float(SideMenuManager.menuAnimationTransformScaleFactor)
screenWidthSlider.value = Float(SideMenuManager.menuWidth / view.frame.width)
blackOutStatusBar.isOn = SideMenuManager.menuFadeStatusBar
darknessSlider.value = Float(SideMenuManager.default.menuAnimationFadeStrength)
shadowOpacitySlider.value = Float(SideMenuManager.default.menuShadowOpacity)
shrinkFactorSlider.value = Float(SideMenuManager.default.menuAnimationTransformScaleFactor)
screenWidthSlider.value = Float(SideMenuManager.default.menuWidth / view.frame.width)
blackOutStatusBar.isOn = SideMenuManager.default.menuFadeStatusBar
}
@IBAction fileprivate func changeSegment(_ segmentControl: UISegmentedControl) {
switch segmentControl {
case presentModeSegmentedControl:
let modes:[SideMenuManager.MenuPresentMode] = [.menuSlideIn, .viewSlideOut, .viewSlideInOut, .menuDissolveIn]
SideMenuManager.menuPresentMode = modes[segmentControl.selectedSegmentIndex]
SideMenuManager.default.menuPresentMode = modes[segmentControl.selectedSegmentIndex]
case blurSegmentControl:
if segmentControl.selectedSegmentIndex == 0 {
SideMenuManager.menuBlurEffectStyle = nil
SideMenuManager.default.menuBlurEffectStyle = nil
} else {
let styles:[UIBlurEffectStyle] = [.dark, .light, .extraLight]
SideMenuManager.menuBlurEffectStyle = styles[segmentControl.selectedSegmentIndex - 1]
SideMenuManager.default.menuBlurEffectStyle = styles[segmentControl.selectedSegmentIndex - 1]
}
default: break;
}
@@ -75,18 +75,39 @@ class MainViewController: UIViewController {
@IBAction fileprivate func changeSlider(_ slider: UISlider) {
switch slider {
case darknessSlider:
SideMenuManager.menuAnimationFadeStrength = CGFloat(slider.value)
SideMenuManager.default.menuAnimationFadeStrength = CGFloat(slider.value)
case shadowOpacitySlider:
SideMenuManager.menuShadowOpacity = slider.value
SideMenuManager.default.menuShadowOpacity = slider.value
case shrinkFactorSlider:
SideMenuManager.menuAnimationTransformScaleFactor = CGFloat(slider.value)
SideMenuManager.default.menuAnimationTransformScaleFactor = CGFloat(slider.value)
case screenWidthSlider:
SideMenuManager.menuWidth = view.frame.width * CGFloat(slider.value)
SideMenuManager.default.menuWidth = view.frame.width * CGFloat(slider.value)
default: break;
}
}
@IBAction fileprivate func changeSwitch(_ switchControl: UISwitch) {
SideMenuManager.menuFadeStatusBar = switchControl.isOn
SideMenuManager.default.menuFadeStatusBar = switchControl.isOn
}
}
extension MainViewController: UISideMenuNavigationControllerDelegate {
func sideMenuWillAppear(menu: UISideMenuNavigationController, animated: Bool) {
print("SideMenu Appearing! (animated: \(animated))")
}
func sideMenuDidAppear(menu: UISideMenuNavigationController, animated: Bool) {
print("SideMenu Appeared! (animated: \(animated))")
}
func sideMenuWillDisappear(menu: UISideMenuNavigationController, animated: Bool) {
print("SideMenu Disappearing! (animated: \(animated))")
}
func sideMenuDidDisappear(menu: UISideMenuNavigationController, animated: Bool) {
print("SideMenu Disappeared! (animated: \(animated))")
}
}
-49
View File
@@ -1,49 +0,0 @@
//
// SideMenuTableView.swift
// SideMenu
//
// Created by Jon Kent on 4/5/16.
// Copyright © 2016 CocoaPods. All rights reserved.
//
import Foundation
import SideMenu
class SideMenuTableView: UITableViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("SideMenu Appearing!")
// this will be non-nil if a blur effect is applied
guard tableView.backgroundView == nil else {
return
}
// Set up a cool background image for demo purposes
let imageView = UIImageView(image: UIImage(named: "saturn"))
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = UIColor.black.withAlphaComponent(0.2)
tableView.backgroundView = imageView
}
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!")
}
}
+39
View File
@@ -0,0 +1,39 @@
//
// SideMenuTableViewController.swift
// SideMenu
//
// Created by Jon Kent on 4/5/16.
// Copyright © 2016 CocoaPods. All rights reserved.
//
import Foundation
import SideMenu
class SideMenuTableViewController: UITableViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// refresh cell blur effect in case it changed
tableView.reloadData()
guard SideMenuManager.default.menuBlurEffectStyle == nil else {
return
}
// Set up a cool background image for demo purposes
let imageView = UIImageView(image: UIImage(named: "saturn"))
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = UIColor.black.withAlphaComponent(0.2)
tableView.backgroundView = imageView
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = super.tableView(tableView, cellForRowAt: indexPath) as! UITableViewVibrantCell
cell.blurEffectStyle = SideMenuManager.default.menuBlurEffectStyle
return cell
}
}
+394 -125
View File
@@ -51,7 +51,7 @@ open class SideMenuManager : NSObject {
- 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
open var menuPushStyle: MenuPushStyle = .defaultBehavior
/**
The presentation mode of the menu.
@@ -62,126 +62,86 @@ open class SideMenuManager : NSObject {
- 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
open 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
/// 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)
open var menuAllowPushOfSameClassTwice = true
/**
Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is 75% of the screen width or 240 points, whichever is smaller.
Note that each menu's width can be overridden using the `menuWidth` property on any `UISideMenuNavigationController` instance.
*/
open var menuWidth: CGFloat = min(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: Double = 0.35
open 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: Double = 0.35
open 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
/// Duration of the remaining animation when the menu is partially dismissed with gestures. Default is 0.35 seconds.
open var menuAnimationCompleteGestureDuration: Double = 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
open 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
open 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?
open 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
open 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
open 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
open var menuShadowRadius: CGFloat = 5
/// 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
open 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
open var menuParallaxStrength: Int = 0
/// Draws the `menuAnimationBackgroundColor` behind the status bar. Default is true.
open static var menuFadeStatusBar = true
open var menuFadeStatusBar = true
/// The animation options when a menu is displayed. Ignored when displayed with a gesture.
open static var menuAnimationOptions: UIViewAnimationOptions = .curveEaseInOut
open var menuAnimationOptions: UIViewAnimationOptions = .curveEaseInOut
/// The animation spring damping when a menu is displayed. Ignored when displayed with a gesture.
open static var menuAnimationUsingSpringWithDamping: CGFloat = 1
open 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
open var menuAnimationInitialSpringVelocity: CGFloat = 1
/**
/**
Automatically dismisses the menu when another view is pushed from it.
Note: to prevent the menu from dismissing when presenting, set modalPresentationStyle = .overFullScreen
of the view controller being presented in storyboard or during its initalization.
*/
open static var menuDismissOnPush = true
open var menuDismissOnPush = true
/// -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
}
}
/// Default instance of SideMenuManager.
open static let `default` = SideMenuManager()
internal var transition: SideMenuTransition!
public override init() {
super.init()
transition = SideMenuTransition(sideMenuManager: self)
}
/// -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")
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? {
open var menuBlurEffectStyle: UIBlurEffectStyle? {
didSet {
if oldValue != menuBlurEffectStyle {
updateMenuBlurIfNecessary()
@@ -190,30 +150,40 @@ open class SideMenuManager : NSObject {
}
/// The left menu.
open static var menuLeftNavigationController: UISideMenuNavigationController? {
open var menuLeftNavigationController: UISideMenuNavigationController? {
willSet {
if menuLeftNavigationController?.presentingViewController == nil {
removeMenuBlurForMenu(menuLeftNavigationController)
guard menuLeftNavigationController != newValue, menuLeftNavigationController?.presentingViewController == nil else {
return
}
menuLeftNavigationController?.locked = false
removeMenuBlurForMenu(menuLeftNavigationController)
}
didSet {
guard menuLeftNavigationController != oldValue else {
return
}
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? {
open var menuRightNavigationController: UISideMenuNavigationController? {
willSet {
if menuRightNavigationController?.presentingViewController == nil {
removeMenuBlurForMenu(menuRightNavigationController)
guard menuRightNavigationController != newValue, menuRightNavigationController?.presentingViewController == nil else {
return
}
removeMenuBlurForMenu(menuRightNavigationController)
}
didSet {
guard menuRightNavigationController != oldValue else {
return
}
guard oldValue?.presentingViewController == nil else {
print("SideMenu Warning: menuRightNavigationController cannot be modified while it's presented.")
menuRightNavigationController = oldValue
@@ -224,7 +194,7 @@ open class SideMenuManager : NSObject {
}
/// The left menu swipe to dismiss gesture.
open static weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer? {
open weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer? {
didSet {
oldValue?.view?.removeGestureRecognizer(oldValue!)
setupGesture(gesture: menuLeftSwipeToDismissGesture)
@@ -232,26 +202,41 @@ open class SideMenuManager : NSObject {
}
/// The right menu swipe to dismiss gesture.
open static weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer? {
open weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer? {
didSet {
oldValue?.view?.removeGestureRecognizer(oldValue!)
setupGesture(gesture: menuRightSwipeToDismissGesture)
}
}
fileprivate class func setupGesture(gesture: UIPanGestureRecognizer?) {
fileprivate func setupGesture(gesture: UIPanGestureRecognizer?) {
guard let gesture = gesture else {
return
}
gesture.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handleHideMenuPan(_:)))
gesture.addTarget(transition, action:#selector(SideMenuTransition.handleHideMenuPan(_:)))
}
fileprivate class func setupNavigationController(_ forMenu: UISideMenuNavigationController?, leftSide: Bool) {
fileprivate func setupNavigationController(_ forMenu: UISideMenuNavigationController?, leftSide: Bool) {
guard let forMenu = forMenu else {
return
}
forMenu.transitioningDelegate = transition
forMenu.modalPresentationStyle = .overFullScreen
forMenu.leftSide = leftSide
if forMenu.sideMenuManager != self {
if forMenu.sideMenuManager?.menuLeftNavigationController == forMenu {
print("SideMenu Warning: \(String(describing: forMenu.self)) was already assigned to the menuLeftNavigationController of \(String(describing: forMenu.sideMenuManager!.self)). When using multiple SideMenuManagers you may want to use new instances of UISideMenuNavigationController instead of existing instances to avoid crashes if the menu is presented more than once.")
} else if forMenu.sideMenuManager?.menuRightNavigationController == forMenu {
print("SideMenu Warning: \(String(describing: forMenu.self)) was already assigned to the menuRightNavigationController of \(String(describing: forMenu.sideMenuManager!.self)). When using multiple SideMenuManagers you may want to use new instances of UISideMenuNavigationController instead of existing instances to avoid crashes if the menu is presented more than once.")
}
forMenu.sideMenuManager = self
}
forMenu.locked = true
if menuEnableSwipeGestures {
let exitPanGesture = UIPanGestureRecognizer()
forMenu.view.addGestureRecognizer(exitPanGesture)
@@ -261,14 +246,15 @@ open class SideMenuManager : NSObject {
menuRightSwipeToDismissGesture = exitPanGesture
}
}
forMenu.transitioningDelegate = SideMenuTransition.singleton
forMenu.modalPresentationStyle = .overFullScreen
forMenu.leftSide = leftSide
// Ensures minimal lag when revealing the menu for the first time using gestures by loading the view:
let _ = forMenu.topViewController?.view
updateMenuBlurIfNecessary()
}
/// Enable or disable gestures that would swipe to dismiss the menu. Default is true.
open static var menuEnableSwipeGestures: Bool = true {
open var menuEnableSwipeGestures: Bool = true {
didSet {
menuLeftSwipeToDismissGesture?.view?.removeGestureRecognizer(menuLeftSwipeToDismissGesture!)
menuRightSwipeToDismissGesture?.view?.removeGestureRecognizer(menuRightSwipeToDismissGesture!)
@@ -277,24 +263,22 @@ open class SideMenuManager : NSObject {
}
}
fileprivate class func updateMenuBlurIfNecessary() {
let menuBlurBlock = { (forMenu: UISideMenuNavigationController?) in
if let forMenu = forMenu {
setupMenuBlurForMenu(forMenu)
}
fileprivate func updateMenuBlurIfNecessary() {
if let menuLeftNavigationController = self.menuLeftNavigationController {
setupMenuBlurForMenu(menuLeftNavigationController)
}
if let menuRightNavigationController = self.menuRightNavigationController {
setupMenuBlurForMenu(menuRightNavigationController)
}
menuBlurBlock(menuLeftNavigationController)
menuBlurBlock(menuRightNavigationController)
}
fileprivate class func setupMenuBlurForMenu(_ forMenu: UISideMenuNavigationController?) {
fileprivate func setupMenuBlurForMenu(_ forMenu: UISideMenuNavigationController?) {
removeMenuBlurForMenu(forMenu)
guard let forMenu = forMenu,
let menuBlurEffectStyle = menuBlurEffectStyle,
let view = forMenu.visibleViewController?.view
, !UIAccessibilityIsReduceTransparencyEnabled() else {
let view = forMenu.topViewController?.view,
!UIAccessibilityIsReduceTransparencyEnabled() else {
return
}
@@ -305,7 +289,7 @@ open class SideMenuManager : NSObject {
let blurEffect = UIBlurEffect(style: menuBlurEffectStyle)
let blurView = UIVisualEffectView(effect: blurEffect)
view.backgroundColor = UIColor.clear
if let tableViewController = forMenu.visibleViewController as? UITableViewController {
if let tableViewController = forMenu.topViewController as? UITableViewController {
tableViewController.tableView.backgroundView = blurView
tableViewController.tableView.separatorEffect = UIVibrancyEffect(blurEffect: blurEffect)
tableViewController.tableView.reloadData()
@@ -316,17 +300,17 @@ open class SideMenuManager : NSObject {
}
}
fileprivate class func removeMenuBlurForMenu(_ forMenu: UISideMenuNavigationController?) {
fileprivate func removeMenuBlurForMenu(_ forMenu: UISideMenuNavigationController?) {
guard let forMenu = forMenu,
let originalMenuBackgroundColor = forMenu.originalMenuBackgroundColor,
let view = forMenu.visibleViewController?.view else {
let view = forMenu.topViewController?.view else {
return
}
view.backgroundColor = originalMenuBackgroundColor
forMenu.originalMenuBackgroundColor = nil
if let tableViewController = forMenu.visibleViewController as? UITableViewController {
if let tableViewController = forMenu.topViewController as? UITableViewController {
tableViewController.tableView.backgroundView = nil
tableViewController.tableView.separatorEffect = nil
tableViewController.tableView.reloadData()
@@ -343,32 +327,34 @@ open class SideMenuManager : NSObject {
- Returns: The array of screen edge gestures added to `toView`.
*/
@discardableResult open class func menuAddScreenEdgePanGesturesToPresent(toView: UIView, forMenu:UIRectEdge? = nil) -> [UIScreenEdgePanGestureRecognizer] {
@discardableResult open func menuAddScreenEdgePanGesturesToPresent(toView: UIView, forMenu:UIRectEdge? = nil) -> [UIScreenEdgePanGestureRecognizer] {
var array = [UIScreenEdgePanGestureRecognizer]()
let newScreenEdgeGesture = { () -> UIScreenEdgePanGestureRecognizer in
let screenEdgeGestureRecognizer = UIScreenEdgePanGestureRecognizer()
screenEdgeGestureRecognizer.cancelsTouchesInView = true
toView.addGestureRecognizer(screenEdgeGestureRecognizer)
array.append(screenEdgeGestureRecognizer)
return screenEdgeGestureRecognizer
}
if forMenu != .right {
let leftScreenEdgeGestureRecognizer = UIScreenEdgePanGestureRecognizer()
leftScreenEdgeGestureRecognizer.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handlePresentMenuLeftScreenEdge(_:)))
let leftScreenEdgeGestureRecognizer = newScreenEdgeGesture()
leftScreenEdgeGestureRecognizer.addTarget(transition, action:#selector(SideMenuTransition.handlePresentMenuLeftScreenEdge(_:)))
leftScreenEdgeGestureRecognizer.edges = .left
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 menuLeftNavigationController == nil {
print("SideMenu Warning: menuAddScreenEdgePanGesturesToPresent was called before menuLeftNavigationController was set. The gesture will not work without a menu. Use menuAddScreenEdgePanGesturesToPresent(toView:forMenu:) to add gestures for only one menu.")
}
}
if forMenu != .left {
let rightScreenEdgeGestureRecognizer = UIScreenEdgePanGestureRecognizer()
rightScreenEdgeGestureRecognizer.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handlePresentMenuRightScreenEdge(_:)))
let rightScreenEdgeGestureRecognizer = newScreenEdgeGesture()
rightScreenEdgeGestureRecognizer.addTarget(transition, action:#selector(SideMenuTransition.handlePresentMenuRightScreenEdge(_:)))
rightScreenEdgeGestureRecognizer.edges = .right
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.")
if menuRightNavigationController == nil {
print("SideMenu Warning: menuAddScreenEdgePanGesturesToPresent was called before menuRightNavigationController was set. The gesture will not work without a menu. Use menuAddScreenEdgePanGesturesToPresent(toView:forMenu:) to add gestures for only one menu.")
}
}
@@ -382,15 +368,298 @@ open class SideMenuManager : NSObject {
- Returns: The pan gesture added to `toView`.
*/
@discardableResult open class func menuAddPanGestureToPresent(toView: UIView) -> UIPanGestureRecognizer {
@discardableResult open func menuAddPanGestureToPresent(toView: UIView) -> UIPanGestureRecognizer {
let panGestureRecognizer = UIPanGestureRecognizer()
panGestureRecognizer.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handlePresentMenuPan(_:)))
panGestureRecognizer.addTarget(transition, action:#selector(SideMenuTransition.handlePresentMenuPan(_:)))
toView.addGestureRecognizer(panGestureRecognizer)
if SideMenuManager.menuLeftNavigationController ?? SideMenuManager.menuRightNavigationController == nil {
if menuLeftNavigationController ?? menuRightNavigationController == nil {
print("SideMenu Warning: menuAddPanGestureToPresent called before menuLeftNavigationController or menuRightNavigationController have been defined. Gestures will not work without a menu.")
}
return panGestureRecognizer
}
}
// Deprecations, to be removed at a future date.
extension SideMenuManager {
@available(*, deprecated, renamed: "default.menuPushStyle", message: "SideMenuManager class methods deprecated.")
open static var menuPushStyle: MenuPushStyle {
get {
return `default`.menuPushStyle
}
set {
`default`.menuPushStyle = newValue
}
}
@available(*, deprecated, renamed: "default.menuPresentMode", message: "SideMenuManager class methods deprecated.")
open static var menuPresentMode: MenuPresentMode {
get {
return `default`.menuPresentMode
}
set {
`default`.menuPresentMode = newValue
}
}
@available(*, deprecated, renamed: "default.menuAllowPushOfSameClassTwice", message: "SideMenuManager class methods deprecated.")
open static var menuAllowPushOfSameClassTwice: Bool {
get {
return `default`.menuAllowPushOfSameClassTwice
}
set {
`default`.menuAllowPushOfSameClassTwice = newValue
}
}
@available(*, deprecated, renamed: "default.menuWidth", message: "SideMenuManager class methods deprecated.")
open static var menuWidth: CGFloat {
get {
return `default`.menuWidth
}
set {
`default`.menuWidth = newValue
}
}
@available(*, deprecated, renamed: "default.menuAnimationPresentDuration", message: "SideMenuManager class methods deprecated.")
open static var menuAnimationPresentDuration: Double {
get {
return `default`.menuAnimationPresentDuration
}
set {
`default`.menuAnimationPresentDuration = newValue
}
}
@available(*, deprecated, renamed: "default.menuAnimationDismissDuration", message: "SideMenuManager class methods deprecated.")
open static var menuAnimationDismissDuration: Double {
get {
return `default`.menuAnimationDismissDuration
}
set {
`default`.menuAnimationDismissDuration = newValue
}
}
@available(*, deprecated, renamed: "default.menuAnimationCompleteGestureDuration", message: "SideMenuManager class methods deprecated.")
open static var menuAnimationCompleteGestureDuration: Double {
get {
return `default`.menuAnimationCompleteGestureDuration
}
set {
`default`.menuAnimationCompleteGestureDuration = newValue
}
}
@available(*, deprecated, renamed: "default.menuAnimationFadeStrength", message: "SideMenuManager class methods deprecated.")
open static var menuAnimationFadeStrength: CGFloat {
get {
return `default`.menuAnimationFadeStrength
}
set {
`default`.menuAnimationFadeStrength = newValue
}
}
@available(*, deprecated, renamed: "default.menuAnimationTransformScaleFactor", message: "SideMenuManager class methods deprecated.")
open static var menuAnimationTransformScaleFactor: CGFloat {
get {
return `default`.menuAnimationTransformScaleFactor
}
set {
`default`.menuAnimationTransformScaleFactor = newValue
}
}
@available(*, deprecated, renamed: "default.menuAnimationBackgroundColor", message: "SideMenuManager class methods deprecated.")
open static var menuAnimationBackgroundColor: UIColor? {
get {
return `default`.menuAnimationBackgroundColor
}
set {
`default`.menuAnimationBackgroundColor = newValue
}
}
@available(*, deprecated, renamed: "default.menuShadowOpacity", message: "SideMenuManager class methods deprecated.")
open static var menuShadowOpacity: Float {
get {
return `default`.menuShadowOpacity
}
set {
`default`.menuShadowOpacity = newValue
}
}
@available(*, deprecated, renamed: "default.menuShadowColor", message: "SideMenuManager class methods deprecated.")
open static var menuShadowColor: UIColor {
get {
return `default`.menuShadowColor
}
set {
`default`.menuShadowColor = newValue
}
}
@available(*, deprecated, renamed: "default.menuShadowRadius", message: "SideMenuManager class methods deprecated.")
open static var menuShadowRadius: CGFloat {
get {
return `default`.menuShadowRadius
}
set {
`default`.menuShadowRadius = newValue
}
}
@available(*, deprecated, renamed: "default.menuPresentingViewControllerUserInteractionEnabled", message: "SideMenuManager class methods deprecated.")
open static var menuPresentingViewControllerUserInteractionEnabled: Bool {
get {
return `default`.menuPresentingViewControllerUserInteractionEnabled
}
set {
`default`.menuPresentingViewControllerUserInteractionEnabled = newValue
}
}
@available(*, deprecated, renamed: "default.menuParallaxStrength", message: "SideMenuManager class methods deprecated.")
open static var menuParallaxStrength: Int {
get {
return `default`.menuParallaxStrength
}
set {
`default`.menuParallaxStrength = newValue
}
}
@available(*, deprecated, renamed: "default.menuFadeStatusBar", message: "SideMenuManager class methods deprecated.")
open static var menuFadeStatusBar: Bool {
get {
return `default`.menuFadeStatusBar
}
set {
`default`.menuFadeStatusBar = newValue
}
}
@available(*, deprecated, renamed: "default.menuAnimationOptions", message: "SideMenuManager class methods deprecated.")
open static var menuAnimationOptions: UIViewAnimationOptions {
get {
return `default`.menuAnimationOptions
}
set {
`default`.menuAnimationOptions = newValue
}
}
@available(*, deprecated, renamed: "default.menuAnimationUsingSpringWithDamping", message: "SideMenuManager class methods deprecated.")
open static var menuAnimationUsingSpringWithDamping: CGFloat {
get {
return `default`.menuAnimationUsingSpringWithDamping
}
set {
`default`.menuAnimationUsingSpringWithDamping = newValue
}
}
@available(*, deprecated, renamed: "default.menuAnimationInitialSpringVelocity", message: "SideMenuManager class methods deprecated.")
open static var menuAnimationInitialSpringVelocity: CGFloat {
get {
return `default`.menuAnimationInitialSpringVelocity
}
set {
`default`.menuAnimationInitialSpringVelocity = newValue
}
}
@available(*, deprecated, renamed: "default.menuDismissOnPush", message: "SideMenuManager class methods deprecated.")
open static var menuDismissOnPush: Bool {
get {
return `default`.menuDismissOnPush
}
set {
`default`.menuDismissOnPush = newValue
}
}
/// -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
}
}
}
@available(*, deprecated, renamed: "default.menuBlurEffectStyle", message: "SideMenuManager class methods deprecated.")
open static var menuBlurEffectStyle: UIBlurEffectStyle? {
get {
return `default`.menuBlurEffectStyle
}
set {
`default`.menuBlurEffectStyle = newValue
}
}
@available(*, deprecated, renamed: "default.menuLeftNavigationController", message: "SideMenuManager class methods deprecated.")
open static var menuLeftNavigationController: UISideMenuNavigationController? {
get {
return `default`.menuLeftNavigationController
}
set {
`default`.menuLeftNavigationController = newValue
}
}
@available(*, deprecated, renamed: "default.menuRightNavigationController", message: "SideMenuManager class methods deprecated.")
open static var menuRightNavigationController: UISideMenuNavigationController? {
get {
return `default`.menuRightNavigationController
}
set {
`default`.menuRightNavigationController = newValue
}
}
@available(*, deprecated, renamed: "default.menuLeftSwipeToDismissGesture", message: "SideMenuManager class methods deprecated.")
open static weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer? {
get {
return `default`.menuLeftSwipeToDismissGesture
}
set {
`default`.menuLeftSwipeToDismissGesture = newValue
}
}
@available(*, deprecated, renamed: "default.menuRightSwipeToDismissGesture", message: "SideMenuManager class methods deprecated.")
open static weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer? {
get {
return `default`.menuRightSwipeToDismissGesture
}
set {
`default`.menuRightSwipeToDismissGesture = newValue
}
}
@available(*, deprecated, renamed: "default.menuEnableSwipeGestures", message: "SideMenuManager class methods deprecated.")
open static var menuEnableSwipeGestures: Bool {
get {
return `default`.menuEnableSwipeGestures
}
set {
`default`.menuEnableSwipeGestures = newValue
}
}
@available(*, deprecated, renamed: "default.menuAddScreenEdgePanGesturesToPresent", message: "SideMenuManager class methods deprecated.")
@discardableResult open class func menuAddScreenEdgePanGesturesToPresent(toView: UIView, forMenu:UIRectEdge? = nil) -> [UIScreenEdgePanGestureRecognizer] {
return `default`.menuAddScreenEdgePanGesturesToPresent(toView: toView, forMenu: forMenu)
}
@available(*, deprecated, renamed: "default.menuAddPanGestureToPresent", message: "SideMenuManager class methods deprecated.")
@discardableResult open class func menuAddPanGestureToPresent(toView: UIView) -> UIPanGestureRecognizer {
return `default`.menuAddPanGestureToPresent(toView: toView)
}
}
+230 -219
View File
@@ -12,13 +12,33 @@ 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? {
fileprivate weak var originalSuperview: UIView?
fileprivate weak var activeGesture: UIGestureRecognizer?
fileprivate var switchMenus = false {
didSet {
if switchMenus {
cancel()
}
}
}
fileprivate var menuWidth: CGFloat {
get {
let overriddenWidth = menuViewController?.menuWidth ?? 0
if overriddenWidth > CGFloat.ulpOfOne {
return overriddenWidth
}
return sideMenuManager.menuWidth
}
}
internal weak var sideMenuManager: SideMenuManager!
internal weak var mainViewController: UIViewController?
internal weak var menuViewController: UISideMenuNavigationController? {
get {
return presentDirection == .left ? sideMenuManager.menuLeftNavigationController : sideMenuManager.menuRightNavigationController
}
}
internal var presentDirection: UIRectEdge = .left
internal weak var tapView: UIView? {
didSet {
guard let tapView = tapView else {
return
@@ -26,80 +46,70 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition {
tapView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
let exitPanGesture = UIPanGestureRecognizer()
exitPanGesture.addTarget(SideMenuTransition.self, action:#selector(SideMenuTransition.handleHideMenuPan(_:)))
exitPanGesture.addTarget(self, action:#selector(SideMenuTransition.handleHideMenuPan(_:)))
let exitTapGesture = UITapGestureRecognizer()
exitTapGesture.addTarget(SideMenuTransition.self, action: #selector(SideMenuTransition.handleHideMenuTap(_:)))
exitTapGesture.addTarget(self, action: #selector(SideMenuTransition.handleHideMenuTap(_:)))
tapView.addGestureRecognizer(exitPanGesture)
tapView.addGestureRecognizer(exitTapGesture)
}
}
internal static weak var statusBarView: UIView? {
internal 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.backgroundColor = sideMenuManager.menuAnimationBackgroundColor ?? UIColor.black
statusBarView.isUserInteractionEnabled = false
}
}
// prevent instantiation
fileprivate override init() {
required public init(sideMenuManager: SideMenuManager) {
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)
NotificationCenter.default.addObserver(self, selector:#selector(handleNotification), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
NotificationCenter.default.addObserver(self, selector:#selector(handleNotification), name: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil)
self.sideMenuManager = sideMenuManager
}
deinit {
NotificationCenter.default.removeObserver(SideMenuTransition.singleton)
NotificationCenter.default.removeObserver(self)
}
fileprivate class var presentingViewControllerForMenu: UIViewController? {
fileprivate static var visibleViewController: UIViewController? {
get {
return SideMenuManager.menuLeftNavigationController?.presentingViewController ?? SideMenuManager.menuRightNavigationController?.presentingViewController
return getVisibleViewController(forViewController: UIApplication.shared.keyWindow?.rootViewController)
}
}
fileprivate class var viewControllerForMenu: UISideMenuNavigationController? {
get {
return SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController : SideMenuManager.menuRightNavigationController
fileprivate class func getVisibleViewController(forViewController: UIViewController?) -> UIViewController? {
if let navigationController = forViewController as? UINavigationController {
return getVisibleViewController(forViewController: navigationController.visibleViewController)
}
}
fileprivate class var visibleViewController: UIViewController? {
get {
return getVisibleViewControllerFromViewController(UIApplication.shared.keyWindow?.rootViewController)
if let tabBarController = forViewController as? UITabBarController {
return getVisibleViewController(forViewController: tabBarController.selectedViewController)
}
}
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)
if let splitViewController = forViewController as? UISplitViewController {
return getVisibleViewController(forViewController: splitViewController.viewControllers.last)
}
if let presentedViewController = forViewController?.presentedViewController {
return getVisibleViewController(forViewController: presentedViewController)
}
return viewController
return forViewController
}
internal class func handlePresentMenuLeftScreenEdge(_ edge: UIScreenEdgePanGestureRecognizer) {
SideMenuTransition.presentDirection = .left
@objc internal func handlePresentMenuLeftScreenEdge(_ edge: UIScreenEdgePanGestureRecognizer) {
presentDirection = .left
handlePresentMenuPan(edge)
}
internal class func handlePresentMenuRightScreenEdge(_ edge: UIScreenEdgePanGestureRecognizer) {
SideMenuTransition.presentDirection = .right
@objc internal func handlePresentMenuRightScreenEdge(_ edge: UIScreenEdgePanGestureRecognizer) {
presentDirection = .right
handlePresentMenuPan(edge)
}
internal class func handlePresentMenuPan(_ pan: UIPanGestureRecognizer) {
@objc internal func handlePresentMenuPan(_ pan: UIPanGestureRecognizer) {
if activeGesture == nil {
activeGesture = pan
} else if pan != activeGesture {
@@ -111,7 +121,7 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition {
}
// how much distance have we panned in reference to the parent view?
guard let view = presentingViewControllerForMenu?.view ?? pan.view else {
guard let view = mainViewController?.view ?? pan.view else {
return
}
@@ -121,59 +131,57 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition {
view.transform = transform
// do some math to translate this to a percentage based value
if !singleton.interactive {
if !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
presentDirection = translation.x > 0 ? .left : .right
}
if let menuViewController = viewControllerForMenu, let visibleViewController = visibleViewController {
singleton.interactive = true
if let menuViewController = menuViewController, let visibleViewController = SideMenuTransition.visibleViewController {
interactive = true
visibleViewController.present(menuViewController, animated: true, completion: nil)
} else {
return
}
}
let direction: CGFloat = SideMenuTransition.presentDirection == .left ? 1 : -1
let distance = translation.x / SideMenuManager.menuWidth
let direction: CGFloat = presentDirection == .left ? 1 : -1
let distance = translation.x / 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
update(min(distance * direction, 1))
} else if distance > 0 && presentDirection == .right && sideMenuManager.menuLeftNavigationController != nil {
presentDirection = .left
switchMenus = true
singleton.cancel()
} else if distance < 0 && SideMenuTransition.presentDirection == .left && SideMenuManager.menuRightNavigationController != nil {
SideMenuTransition.presentDirection = .right
} else if distance < 0 && presentDirection == .left && sideMenuManager.menuRightNavigationController != nil {
presentDirection = .right
switchMenus = true
singleton.cancel()
} else {
singleton.update(min(distance * direction, 1))
update(min(distance * direction, 1))
}
default:
singleton.interactive = false
interactive = false
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.ulpOfOne {
singleton.update(0.9999)
if ProcessInfo().operatingSystemVersion.majorVersion == 8 && percentComplete > 1 - CGFloat.ulpOfOne {
update(0.9999)
}
singleton.finish()
finish()
} else {
singleton.cancel()
cancel()
}
}
}
internal class func handleHideMenuPan(_ pan: UIPanGestureRecognizer) {
@objc internal func handleHideMenuPan(_ pan: UIPanGestureRecognizer) {
if activeGesture == nil {
activeGesture = pan
} else if pan != activeGesture {
@@ -183,216 +191,212 @@ open class SideMenuTransition: UIPercentDrivenInteractiveTransition {
}
let translation = pan.translation(in: pan.view!)
let direction:CGFloat = SideMenuTransition.presentDirection == .left ? -1 : 1
let distance = translation.x / SideMenuManager.menuWidth * direction
let direction:CGFloat = presentDirection == .left ? -1 : 1
let distance = translation.x / menuWidth * direction
switch (pan.state) {
case .began:
singleton.interactive = true
presentingViewControllerForMenu?.dismiss(animated: true, completion: nil)
interactive = true
mainViewController?.dismiss(animated: true, completion: nil)
case .changed:
singleton.update(max(min(distance, 1), 0))
update(max(min(distance, 1), 0))
default:
singleton.interactive = false
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.ulpOfOne {
singleton.update(0.9999)
if ProcessInfo().operatingSystemVersion.majorVersion == 8 && percentComplete > 1 - CGFloat.ulpOfOne {
update(0.9999)
}
singleton.finish()
finish()
activeGesture = nil
} else {
singleton.cancel()
cancel()
activeGesture = nil
}
}
}
internal class func handleHideMenuTap(_ tap: UITapGestureRecognizer) {
presentingViewControllerForMenu?.dismiss(animated: true, completion: nil)
@objc internal func handleHideMenuTap(_ tap: UITapGestureRecognizer) {
menuViewController?.dismiss(animated: true, completion: nil)
}
internal class func hideMenuStart() {
guard let mainViewController = presentingViewControllerForMenu,
let menuView = SideMenuTransition.presentDirection == .left ? SideMenuManager.menuLeftNavigationController?.view : SideMenuManager.menuRightNavigationController?.view else {
return
}
@discardableResult internal func hideMenuStart() -> SideMenuTransition {
let menuView = menuViewController?.view
let mainView = mainViewController?.view
mainViewController.view.transform = .identity
mainViewController.view.alpha = 1
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 // in case status bar height changed
mainView?.transform = .identity
mainView?.alpha = 1
mainView?.frame.origin = .zero
menuView?.transform = .identity
menuView?.frame.origin.y = 0
menuView?.frame.size.width = menuWidth
menuView?.frame.size.height = mainView?.frame.height ?? 0 // in case status bar height changed
var statusBarFrame = UIApplication.shared.statusBarFrame
let statusBarOffset = SideMenuManager.appScreenRect.size.height - mainViewController.view.frame.maxY
let statusBarOffset = SideMenuManager.appScreenRect.size.height - (mainView?.frame.maxY ?? 0)
// 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
statusBarView?.frame = statusBarFrame
statusBarView?.alpha = 0
switch SideMenuManager.menuPresentMode {
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)
menuView?.alpha = 1 - sideMenuManager.menuAnimationFadeStrength
menuView?.frame.origin.x = presentDirection == .left ? 0 : (mainView?.frame.width ?? 0) - menuWidth
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
menuView?.alpha = 1
menuView?.frame.origin.x = presentDirection == .left ? -menuView!.frame.width : mainView!.frame.width
case .menuSlideIn:
menuView.alpha = 1
menuView.frame.origin.x = SideMenuTransition.presentDirection == .left ? -menuView.frame.width : mainViewController.view.frame.width
menuView?.alpha = 1
menuView?.frame.origin.x = presentDirection == .left ? -menuView!.frame.width : mainView!.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
menuView?.alpha = 0
menuView?.frame.origin.x = presentDirection == .left ? 0 : mainView!.frame.width - menuWidth
}
return self
}
internal class func hideMenuComplete() {
guard let mainViewController = presentingViewControllerForMenu,
let menuView = viewControllerForMenu?.view else {
return
}
@discardableResult internal func hideMenuComplete() -> SideMenuTransition {
let menuView = menuViewController?.view
let mainView = mainViewController?.view
SideMenuTransition.tapView?.removeFromSuperview()
SideMenuTransition.statusBarView?.removeFromSuperview()
mainViewController.view.motionEffects.removeAll()
mainViewController.view.layer.shadowOpacity = 0
menuView.layer.shadowOpacity = 0
tapView?.removeFromSuperview()
statusBarView?.removeFromSuperview()
mainView?.motionEffects.removeAll()
mainView?.layer.shadowOpacity = 0
menuView?.layer.shadowOpacity = 0
if let topNavigationController = mainViewController as? UINavigationController {
topNavigationController.interactivePopGestureRecognizer!.isEnabled = true
}
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() {
guard let menuView = viewControllerForMenu?.view,
let mainViewController = presentingViewControllerForMenu else {
return
if let originalSuperview = originalSuperview, let mainView = mainViewController?.view {
originalSuperview.addSubview(mainView)
let y = originalSuperview.bounds.height - mainView.frame.size.height
mainView.frame.origin.y = max(y, 0)
}
menuView.alpha = 1
menuView.transform = .identity
menuView.frame.size.width = SideMenuManager.menuWidth
originalSuperview = nil
mainViewController = nil
return self
}
@discardableResult internal func presentMenuStart() -> SideMenuTransition {
let menuView = menuViewController?.view
let mainView = mainViewController?.view
menuView?.alpha = 1
menuView?.transform = .identity
menuView?.frame.size.width = menuWidth
let size = SideMenuManager.appScreenRect.size
menuView.frame.origin.x = SideMenuTransition.presentDirection == .left ? 0 : size.width - SideMenuManager.menuWidth
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
menuView?.frame.origin.x = presentDirection == .left ? 0 : size.width - menuWidth
mainView?.transform = .identity
mainView?.frame.size.width = size.width
let statusBarOffset = size.height - (menuView?.bounds.height ?? 0)
mainView?.bounds.size.height = size.height - max(statusBarOffset, 0)
mainView?.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
tapView?.transform = .identity
tapView?.bounds = mainView!.bounds
statusBarView?.frame = statusBarFrame
statusBarView?.alpha = 1
switch SideMenuManager.menuPresentMode {
switch sideMenuManager.menuPresentMode {
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)
mainView?.layer.shadowColor = sideMenuManager.menuShadowColor.cgColor
mainView?.layer.shadowRadius = sideMenuManager.menuShadowRadius
mainView?.layer.shadowOpacity = sideMenuManager.menuShadowOpacity
mainView?.layer.shadowOffset = CGSize(width: 0, height: 0)
let direction:CGFloat = presentDirection == .left ? 1 : -1
mainView?.frame.origin.x = direction * (menuView!.frame.width)
case .menuSlideIn, .menuDissolveIn:
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)
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.origin.x = 0
mainView?.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
if sideMenuManager.menuPresentMode != .viewSlideOut {
mainView?.transform = CGAffineTransform(scaleX: sideMenuManager.menuAnimationTransformScaleFactor, y: sideMenuManager.menuAnimationTransformScaleFactor)
if sideMenuManager.menuAnimationTransformScaleFactor > 1 {
tapView?.transform = mainView!.transform
}
mainViewController.view.alpha = 1 - SideMenuManager.menuAnimationFadeStrength
mainView?.alpha = 1 - sideMenuManager.menuAnimationFadeStrength
}
return self
}
internal class func presentMenuComplete() {
guard let mainViewController = presentingViewControllerForMenu else {
return
}
switch SideMenuManager.menuPresentMode {
@discardableResult internal func presentMenuComplete() -> SideMenuTransition {
switch sideMenuManager.menuPresentMode {
case .menuSlideIn, .menuDissolveIn, .viewSlideInOut:
if SideMenuManager.menuParallaxStrength != 0 {
if let mainView = mainViewController?.view, sideMenuManager.menuParallaxStrength != 0 {
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
horizontal.minimumRelativeValue = -SideMenuManager.menuParallaxStrength
horizontal.maximumRelativeValue = SideMenuManager.menuParallaxStrength
horizontal.minimumRelativeValue = -sideMenuManager.menuParallaxStrength
horizontal.maximumRelativeValue = sideMenuManager.menuParallaxStrength
let vertical = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis)
vertical.minimumRelativeValue = -SideMenuManager.menuParallaxStrength
vertical.maximumRelativeValue = SideMenuManager.menuParallaxStrength
vertical.minimumRelativeValue = -sideMenuManager.menuParallaxStrength
vertical.maximumRelativeValue = sideMenuManager.menuParallaxStrength
let group = UIMotionEffectGroup()
group.motionEffects = [horizontal, vertical]
mainViewController.view.addMotionEffect(group)
mainView.addMotionEffect(group)
}
case .viewSlideOut: break;
}
if let topNavigationController = mainViewController as? UINavigationController {
topNavigationController.interactivePopGestureRecognizer!.isEnabled = false
}
return self
}
internal func handleNotification(notification: NSNotification) {
guard let mainViewController = SideMenuTransition.presentingViewControllerForMenu,
let menuViewController = SideMenuTransition.viewControllerForMenu,
menuViewController.presentedViewController == nil && menuViewController.presentingViewController != nil else {
@objc internal func handleNotification(notification: NSNotification) {
guard menuViewController?.presentedViewController == nil &&
menuViewController?.presentingViewController != nil else {
return
}
if let originalSuperview = SideMenuTransition.originalSuperview {
if let originalSuperview = originalSuperview, let mainViewController = mainViewController {
originalSuperview.addSubview(mainViewController.view)
}
if notification.name == NSNotification.Name.UIApplicationDidEnterBackground {
SideMenuTransition.hideMenuStart()
SideMenuTransition.hideMenuComplete()
menuViewController.dismiss(animated: false, completion: nil)
hideMenuStart().hideMenuComplete()
menuViewController?.dismiss(animated: false, completion: nil)
return
}
UIView.animate(withDuration: SideMenuManager.menuAnimationDismissDuration,
UIView.animate(withDuration: sideMenuManager.menuAnimationDismissDuration,
delay: 0,
usingSpringWithDamping: SideMenuManager.menuAnimationUsingSpringWithDamping,
initialSpringVelocity: SideMenuManager.menuAnimationInitialSpringVelocity,
options: SideMenuManager.menuAnimationOptions,
usingSpringWithDamping: sideMenuManager.menuAnimationUsingSpringWithDamping,
initialSpringVelocity: sideMenuManager.menuAnimationInitialSpringVelocity,
options: sideMenuManager.menuAnimationOptions,
animations: {
SideMenuTransition.hideMenuStart()
self.hideMenuStart()
}) { (finished) -> Void in
SideMenuTransition.hideMenuComplete()
menuViewController.dismiss(animated: false, completion: nil)
self.hideMenuComplete()
self.menuViewController?.dismiss(animated: false, completion: nil)
}
}
@@ -408,7 +412,7 @@ extension SideMenuTransition: UIViewControllerAnimatedTransitioning {
// prevent any other menu gestures from firing
container.isUserInteractionEnabled = false
if let menuBackgroundColor = SideMenuManager.menuAnimationBackgroundColor {
if let menuBackgroundColor = sideMenuManager.menuAnimationBackgroundColor {
container.backgroundColor = menuBackgroundColor
}
@@ -417,18 +421,17 @@ extension SideMenuTransition: UIViewControllerAnimatedTransitioning {
// 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 ? toViewController : fromViewController
let topViewController = presenting ? fromViewController : toViewController
mainViewController = presenting ? fromViewController : toViewController
let menuView = menuViewController.view!
let topView = topViewController.view!
let menuView = menuViewController!.view!
let topView = mainViewController!.view!
// prepare menu items to slide in
if presenting {
SideMenuTransition.originalSuperview = topView.superview
originalSuperview = topView.superview
// add the both views to our view controller
switch SideMenuManager.menuPresentMode {
switch sideMenuManager.menuPresentMode {
case .viewSlideOut, .viewSlideInOut:
container.addSubview(menuView)
container.addSubview(topView)
@@ -437,20 +440,20 @@ extension SideMenuTransition: UIViewControllerAnimatedTransitioning {
container.addSubview(menuView)
}
if SideMenuManager.menuFadeStatusBar {
if sideMenuManager.menuFadeStatusBar {
let statusBarView = UIView()
SideMenuTransition.statusBarView = statusBarView
self.statusBarView = statusBarView
container.addSubview(statusBarView)
}
SideMenuTransition.hideMenuStart()
hideMenuStart()
}
let animate = {
if self.presenting {
SideMenuTransition.presentMenuStart()
self.presentMenuStart()
} else {
SideMenuTransition.hideMenuStart()
self.hideMenuStart()
}
}
@@ -459,51 +462,51 @@ extension SideMenuTransition: UIViewControllerAnimatedTransitioning {
// tell our transitionContext object that we've finished animating
if transitionContext.transitionWasCancelled {
let viewControllerForPresentedMenu = SideMenuTransition.presentingViewControllerForMenu
let viewControllerForPresentedMenu = self.mainViewController
if self.presenting {
SideMenuTransition.hideMenuComplete()
self.hideMenuComplete()
} else {
SideMenuTransition.presentMenuComplete()
self.presentMenuComplete()
}
transitionContext.completeTransition(false)
if SideMenuTransition.switchMenus {
SideMenuTransition.switchMenus = false
viewControllerForPresentedMenu?.present(SideMenuTransition.viewControllerForMenu!, animated: true, completion: nil)
if self.switchMenus {
self.switchMenus = false
viewControllerForPresentedMenu?.present(self.menuViewController!, animated: true, completion: nil)
}
return
}
if self.presenting {
SideMenuTransition.presentMenuComplete()
self.presentMenuComplete()
transitionContext.completeTransition(true)
switch SideMenuManager.menuPresentMode {
switch self.sideMenuManager.menuPresentMode {
case .viewSlideOut, .viewSlideInOut:
container.addSubview(topView)
case .menuSlideIn, .menuDissolveIn:
container.insertSubview(topView, at: 0)
}
if !SideMenuManager.menuPresentingViewControllerUserInteractionEnabled {
if !self.sideMenuManager.menuPresentingViewControllerUserInteractionEnabled {
let tapView = UIView()
container.insertSubview(tapView, aboveSubview: topView)
tapView.bounds = container.bounds
tapView.center = topView.center
if SideMenuManager.menuAnimationTransformScaleFactor > 1 {
if self.sideMenuManager.menuAnimationTransformScaleFactor > 1 {
tapView.transform = topView.transform
}
SideMenuTransition.tapView = tapView
self.tapView = tapView
}
if let statusBarView = SideMenuTransition.statusBarView {
if let statusBarView = self.statusBarView {
container.bringSubview(toFront: statusBarView)
}
return
}
SideMenuTransition.hideMenuComplete()
self.hideMenuComplete()
transitionContext.completeTransition(true)
menuView.removeFromSuperview()
}
@@ -512,7 +515,7 @@ extension SideMenuTransition: UIViewControllerAnimatedTransitioning {
let duration = transitionDuration(using: transitionContext)
if interactive {
UIView.animate(withDuration: duration,
delay: 0,
delay: duration, // HACK: If zero, the animation briefly flashes in iOS 11. UIViewPropertyAnimators (iOS 10+) may resolve this.
options: .curveLinear,
animations: {
animate()
@@ -522,9 +525,9 @@ extension SideMenuTransition: UIViewControllerAnimatedTransitioning {
} else {
UIView.animate(withDuration: duration,
delay: 0,
usingSpringWithDamping: SideMenuManager.menuAnimationUsingSpringWithDamping,
initialSpringVelocity: SideMenuManager.menuAnimationInitialSpringVelocity,
options: SideMenuManager.menuAnimationOptions,
usingSpringWithDamping: sideMenuManager.menuAnimationUsingSpringWithDamping,
initialSpringVelocity: sideMenuManager.menuAnimationInitialSpringVelocity,
options: sideMenuManager.menuAnimationOptions,
animations: {
animate()
}) { (finished) -> Void in
@@ -536,9 +539,17 @@ extension SideMenuTransition: UIViewControllerAnimatedTransitioning {
// return how many seconds the transiton animation will take
open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
if interactive {
return SideMenuManager.menuAnimationCompleteGestureDuration
return sideMenuManager.menuAnimationCompleteGestureDuration
}
return presenting ? SideMenuManager.menuAnimationPresentDuration : SideMenuManager.menuAnimationDismissDuration
return presenting ? sideMenuManager.menuAnimationPresentDuration : sideMenuManager.menuAnimationDismissDuration
}
open override func update(_ percentComplete: CGFloat) {
guard !switchMenus else {
return
}
super.update(percentComplete)
}
}
@@ -549,7 +560,7 @@ extension SideMenuTransition: UIViewControllerTransitioningDelegate {
// 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
presentDirection = presented == sideMenuManager.menuLeftNavigationController ? .left : .right
return self
}
@@ -562,11 +573,11 @@ extension SideMenuTransition: UIViewControllerTransitioningDelegate {
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
return interactive ? self : nil
}
open func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactive ? SideMenuTransition.singleton : nil
return interactive ? self : nil
}
}
+177 -67
View File
@@ -7,32 +7,109 @@
import UIKit
public protocol UISideMenuNavigationControllerDelegate: class {
func sideMenuWillAppear(menu: UISideMenuNavigationController, animated: Bool)
func sideMenuDidAppear(menu: UISideMenuNavigationController, animated: Bool)
func sideMenuWillDisappear(menu: UISideMenuNavigationController, animated: Bool)
func sideMenuDidDisappear(menu: UISideMenuNavigationController, animated: Bool)
}
// This makes adherance to the protocol optional:
extension UIViewController {
func sideMenuWillAppear(menu: UISideMenuNavigationController, animated: Bool) {}
func sideMenuDidAppear(menu: UISideMenuNavigationController, animated: Bool) {}
func sideMenuWillDisappear(menu: UISideMenuNavigationController, animated: Bool) {}
func sideMenuDidDisappear(menu: UISideMenuNavigationController, animated: Bool) {}
}
open class UISideMenuNavigationController: UINavigationController {
fileprivate weak var temporarySideMenuDelegate: UISideMenuNavigationControllerDelegate?
fileprivate func findDelegate(forViewController: UIViewController?) -> UISideMenuNavigationControllerDelegate? {
if let navigationController = forViewController as? UINavigationController {
return findDelegate(forViewController: navigationController.topViewController)
}
if let tabBarController = forViewController as? UITabBarController {
return findDelegate(forViewController: tabBarController.selectedViewController)
}
if let splitViewController = forViewController as? UISplitViewController {
return findDelegate(forViewController: splitViewController.viewControllers.last)
}
temporarySideMenuDelegate = forViewController as? UISideMenuNavigationControllerDelegate
return temporarySideMenuDelegate
}
fileprivate var usingInterfaceBuilder = false
internal var locked = false
internal var originalMenuBackgroundColor: UIColor?
internal var transition: SideMenuTransition {
get {
return sideMenuManager.transition
}
}
internal var sideMenuDelegate: UISideMenuNavigationControllerDelegate? {
get {
guard !view.isHidden else {
return nil
}
return temporarySideMenuDelegate ?? findDelegate(forViewController: presentingViewController)
}
}
/// SideMenuManager instance associated with this menu. Default is `SideMenuManager.default`. This property cannot be changed after the menu has loaded.
open weak var sideMenuManager: SideMenuManager! = SideMenuManager.default {
didSet {
if locked && oldValue != nil {
print("SideMenu Warning: a menu's sideMenuManager property cannot be changed after it has loaded.")
sideMenuManager = oldValue
}
}
}
/// Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is zero. When zero, `sideMenuManager.menuWidth` is used. This property cannot be changed while the isHidden property is false.
@IBInspectable open var menuWidth: CGFloat = 0 {
didSet {
if !isHidden && oldValue != menuWidth {
print("SideMenu Warning: a menu's width property can only be changed when it is hidden.")
menuWidth = oldValue
}
}
}
/// Whether the menu appears on the right or left side of the screen. Right is the default. This property cannot be changed after the menu has loaded.
@IBInspectable open var leftSide: Bool = false {
didSet {
if locked && leftSide != oldValue {
print("SideMenu Warning: a menu's leftSide property cannot be changed after it has loaded.")
leftSide = oldValue
}
}
}
/// Indicates if the menu is anywhere in the view hierarchy, even if covered by another view controller.
open var isHidden: Bool {
get {
return self.presentingViewController == nil
}
}
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()
}
}
usingInterfaceBuilder = true
}
override open func viewDidLoad() {
super.viewDidLoad()
didSetSide()
if !locked && usingInterfaceBuilder {
if leftSide {
sideMenuManager.menuLeftNavigationController = self
} else {
sideMenuManager.menuRightNavigationController = self
}
}
}
open override func viewWillAppear(_ animated: Bool) {
@@ -40,14 +117,9 @@ open class UISideMenuNavigationController: UINavigationController {
// Dismiss keyboard to prevent weird keyboard animations from occurring during transition
presentingViewController?.view.endEditing(true)
}
fileprivate func didSetSide() {
if leftSide {
SideMenuManager.menuLeftNavigationController = self
} else {
SideMenuManager.menuRightNavigationController = self
}
temporarySideMenuDelegate = nil
sideMenuDelegate?.sideMenuWillAppear(menu: self, animated: animated)
}
override open func viewDidAppear(_ animated: Bool) {
@@ -55,12 +127,16 @@ open class UISideMenuNavigationController: UINavigationController {
// We had presented a view before, so lets dismiss ourselves as already acted upon
if view.isHidden {
SideMenuTransition.hideMenuComplete()
transition.hideMenuComplete()
dismiss(animated: false, completion: { () -> Void in
self.view.isHidden = false
})
return
}
sideMenuDelegate?.sideMenuDidAppear(menu: self, animated: animated)
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.")
}
@@ -69,15 +145,19 @@ open class UISideMenuNavigationController: UINavigationController {
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
// 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 {
guard let sideMenuManager = sideMenuManager else {
return
}
if let mainView = transition.mainViewController?.view {
switch sideMenuManager.menuPresentMode {
case .viewSlideOut, .viewSlideInOut:
mainView.superview?.insertSubview(view, belowSubview: mainView)
case .menuSlideIn, .menuDissolveIn:
if let tapView = SideMenuTransition.tapView {
if let tapView = transition.tapView {
mainView.superview?.insertSubview(view, aboveSubview: tapView)
} else {
mainView.superview?.insertSubview(view, aboveSubview: mainView)
@@ -86,24 +166,49 @@ open class UISideMenuNavigationController: UINavigationController {
}
// 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.
UIView.animate(withDuration: SideMenuManager.menuAnimationDismissDuration,
UIView.animate(withDuration: animated ? sideMenuManager.menuAnimationDismissDuration : 0,
delay: 0,
usingSpringWithDamping: SideMenuManager.menuAnimationUsingSpringWithDamping,
initialSpringVelocity: SideMenuManager.menuAnimationInitialSpringVelocity,
options: SideMenuManager.menuAnimationOptions,
usingSpringWithDamping: sideMenuManager.menuAnimationUsingSpringWithDamping,
initialSpringVelocity: sideMenuManager.menuAnimationInitialSpringVelocity,
options: sideMenuManager.menuAnimationOptions,
animations: {
SideMenuTransition.hideMenuStart()
self.transition.hideMenuStart()
self.sideMenuDelegate?.sideMenuWillDisappear(menu: self, animated: animated)
}) { (finished) -> Void in
self.sideMenuDelegate?.sideMenuDidDisappear(menu: self, animated: animated)
self.view.isHidden = true
}
return
}
sideMenuDelegate?.sideMenuWillDisappear(menu: self, animated: animated)
}
override open func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// Hack: force selection to get cleared on UITableViewControllers when reappearing using custom transitions
visibleViewController?.viewWillAppear(false)
// Work-around: if the menu is dismissed without animation the transition logic is never called to restore the
// the view hierarchy leaving the screen black/empty. This is because the transition moves views within a container
// view, but dismissing without animation removes the container view before the original hierarchy is restored.
// This check corrects that.
if let sideMenuDelegate = sideMenuDelegate as? UIViewController, sideMenuDelegate.view.window == nil {
transition.hideMenuStart().hideMenuComplete()
}
sideMenuDelegate?.sideMenuDidDisappear(menu: self, animated: animated)
// Clear selecton on UITableViewControllers when reappearing using custom transitions
guard let tableViewController = topViewController as? UITableViewController,
let tableView = tableViewController.tableView,
let indexPaths = tableView.indexPathsForSelectedRows,
tableViewController.clearsSelectionOnViewWillAppear else {
return
}
for indexPath in indexPaths {
tableView.deselectRow(at: indexPath, animated: false)
}
}
override open func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
@@ -114,62 +219,67 @@ open class UISideMenuNavigationController: UINavigationController {
return
}
NotificationCenter.default.removeObserver(SideMenuTransition.singleton, name: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil)
NotificationCenter.default.removeObserver(self.transition, name: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil)
coordinator.animate(alongsideTransition: { (context) in
SideMenuTransition.presentMenuStart()
self.transition.presentMenuStart()
}) { (context) in
NotificationCenter.default.addObserver(SideMenuTransition.singleton, selector:#selector(SideMenuTransition.handleNotification), name: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil)
NotificationCenter.default.addObserver(self.transition, selector:#selector(SideMenuTransition.handleNotification), name: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil)
}
}
override open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let presentingViewController = presentingViewController {
presentingViewController.prepare(for: segue, sender: sender)
}
}
override open func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
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.menuPushStyle != .subMenu else {
guard let sideMenuManager = sideMenuManager, 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
}
let splitViewController = presentingViewController as? UISplitViewController
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.")
let potentialNavigationController = (splitViewController?.viewControllers.first ?? tabBarController?.selectedViewController) ?? presentingViewController
guard let navigationController = potentialNavigationController as? UINavigationController else {
print("SideMenu Warning: attempt to push a View Controller from \(String(describing: potentialNavigationController.self)) where its navigationController == nil. It must be embedded in a Navigation Controller for this to work.")
return
}
let sideMenuDelegate = self.sideMenuDelegate
temporarySideMenuDelegate = nil
// 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)
})
if sideMenuManager.menuDismissOnPush {
CATransaction.setCompletionBlock( { () -> Void in
sideMenuDelegate?.sideMenuDidDisappear(menu: self, animated: animated)
if !animated {
self.transition.hideMenuStart().hideMenuComplete()
}
self.dismiss(animated: animated, completion: nil)
})
let areAnimationsEnabled = UIView.areAnimationsEnabled
UIView.setAnimationsEnabled(true)
UIView.animate(withDuration: SideMenuManager.menuAnimationDismissDuration, animations: { () -> Void in
SideMenuTransition.hideMenuStart()
})
UIView.setAnimationsEnabled(areAnimationsEnabled)
if animated {
let areAnimationsEnabled = UIView.areAnimationsEnabled
UIView.setAnimationsEnabled(true)
UIView.animate(withDuration: sideMenuManager.menuAnimationDismissDuration,
delay: 0,
usingSpringWithDamping: sideMenuManager.menuAnimationUsingSpringWithDamping,
initialSpringVelocity: sideMenuManager.menuAnimationInitialSpringVelocity,
options: sideMenuManager.menuAnimationOptions,
animations: {
sideMenuDelegate?.sideMenuWillDisappear(menu: self, animated: animated)
self.transition.hideMenuStart()
})
UIView.setAnimationsEnabled(areAnimationsEnabled)
}
}
if let lastViewController = navigationController.viewControllers.last, !SideMenuManager.menuAllowPushOfSameClassTwice && type(of: lastViewController) == type(of: viewController) {
if let lastViewController = navigationController.viewControllers.last, !sideMenuManager.menuAllowPushOfSameClassTwice && type(of: lastViewController) == type(of: viewController) {
CATransaction.commit()
return
}
switch SideMenuManager.menuPushStyle {
switch sideMenuManager.menuPushStyle {
case .subMenu, .defaultBehavior: break // .subMenu handled earlier, .defaultBehavior falls through to end
case .popWhenPossible:
for subViewController in navigationController.viewControllers.reversed() {
@@ -184,7 +294,7 @@ open class UISideMenuNavigationController: UINavigationController {
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 {
if sideMenuManager.menuPushStyle == .preserveAndHideBackButton {
preservedViewController.navigationItem.hidesBackButton = true
}
viewControllers.append(preservedViewController)
@@ -192,7 +302,7 @@ open class UISideMenuNavigationController: UINavigationController {
CATransaction.commit()
return
}
if SideMenuManager.menuPushStyle == .preserveAndHideBackButton {
if sideMenuManager.menuPushStyle == .preserveAndHideBackButton {
viewController.navigationItem.hidesBackButton = true
}
case .replace:
+15 -5
View File
@@ -13,6 +13,16 @@ open class UITableViewVibrantCell: UITableViewCell {
fileprivate var vibrancyView:UIVisualEffectView = UIVisualEffectView()
fileprivate var vibrancySelectedBackgroundView:UIVisualEffectView = UIVisualEffectView()
fileprivate var defaultSelectedBackgroundView:UIView?
open var blurEffectStyle: UIBlurEffectStyle? {
didSet {
updateBlur()
}
}
// For registering with UITableView without subclassing otherwise dequeuing instance of the cell causes an exception
public override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
@@ -27,16 +37,16 @@ open class UITableViewVibrantCell: UITableViewCell {
let blurSelectionEffect = UIBlurEffect(style: .light)
vibrancySelectedBackgroundView.effect = blurSelectionEffect
defaultSelectedBackgroundView = selectedBackgroundView
updateBlur()
}
override open func layoutSubviews() {
super.layoutSubviews()
internal func updateBlur() {
// 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!)
if let blurEffectStyle = blurEffectStyle, !UIAccessibilityIsReduceTransparencyEnabled() {
let blurEffect = UIBlurEffect(style: blurEffectStyle)
vibrancyView.effect = UIVibrancyEffect(blurEffect: blurEffect)
if selectedBackgroundView != nil && selectedBackgroundView != vibrancySelectedBackgroundView {
+28 -3
View File
@@ -328,7 +328,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0700;
LastUpgradeCheck = 0900;
};
buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */;
compatibilityVersion = "Xcode 3.2";
@@ -405,20 +405,30 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGNING_REQUIRED = NO;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"POD_CONFIGURATION_DEBUG=1",
@@ -466,7 +476,7 @@
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
@@ -482,19 +492,29 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGNING_REQUIRED = NO;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"POD_CONFIGURATION_RELEASE=1",
"$(inherited)",
@@ -508,6 +528,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/;
STRIP_INSTALLED_PRODUCT = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SYMROOT = "${SRCROOT}/../build";
VALIDATE_PRODUCT = YES;
};
@@ -517,6 +538,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 3324698B7EF536D06111969551D7A984 /* Pods-Example.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
@@ -553,6 +575,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = E84078875951590B4A5503E807E1791E /* Pods-Example.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
@@ -588,6 +611,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7CAC7C8AA53EBC987714BAC240E7DE89 /* Pods-Example-ExampleTests.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
@@ -624,6 +648,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 36CF7D6DA1B99FEA132A604D0BF8FB58 /* Pods-Example-ExampleTests.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
@@ -680,7 +705,7 @@
PRODUCT_NAME = SideMenu;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
+134 -63
View File
@@ -4,25 +4,25 @@
[![License](https://img.shields.io/cocoapods/l/SideMenu.svg?style=flat)](http://cocoapods.org/pods/SideMenu)
[![Platform](https://img.shields.io/cocoapods/p/SideMenu.svg?style=flat)](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:yo@massappeal.co?subject=SideMenu%20in%20action!) me a link to your app in the app store!
> Hi, I'm Jon Kent and I am an iOS designer, developer, and mobile strategist. I love coffee and play the drums.
> * [**Hire me**](mailto:yo@massappeal.co?subject=Let's%20build%20something%20amazing) 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://massappeal.co).
> * Check out my [website](http://massappeal.co) to see some of my other projects.
> * 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&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted).
## Overview
SideMenu is a simple and versatile side menu control written in Swift.
* **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.
- [x] **It can be implemented in storyboard without a single line of [code](#code-less-storyboard-implementation).**
- [x] Four standard animation styles to choose from (there's even a parallax effect if you want to get weird).
- [x] 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](https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html).
- [x] Animations use your view controllers, not snapshots.
- [x] Properly handles screen rotation and in-call status bar height changes.
Check out the example project to see it in action!
### Preview Samples
@@ -31,7 +31,7 @@ Check out the example project to see it in action!
| ![](etc/SlideOut.gif) | ![](etc/SlideIn.gif) | ![](etc/Dissolve.gif) | ![](etc/InOut.gif) |
## Requirements
* iOS 8 or higher
- [x] iOS 8 or higher.
## Installation
### CocoaPods
@@ -51,8 +51,8 @@ use_frameworks!
pod 'SideMenu'
# For Swift 2.3 (no longer maintained), use:
# pod 'SideMenu', '~> 1.2.1'
# For Swift 3 (no longer maintained), use:
# pod 'SideMenu', '~> 2.3.4'
```
Then, run the following command:
@@ -100,36 +100,33 @@ In your view controller's `viewDidLoad` event, do something like this (**IMPORTA
``` swift
// Define the menus
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. If you're using storyboards, you'll want to do something like:
// let menuLeftNavigationController = storyboard!.instantiateViewController(withIdentifier: "LeftMenuNavigationController") as! UISideMenuNavigationController
SideMenuManager.menuLeftNavigationController = menuLeftNavigationController
SideMenuManager.default.menuLeftNavigationController = menuLeftNavigationController
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
SideMenuManager.default.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!
SideMenuManager.menuAddPanGestureToPresent(toView: self.navigationController!.navigationBar)
SideMenuManager.menuAddScreenEdgePanGesturesToPresent(toView: self.navigationController!.view)
SideMenuManager.default.menuAddPanGestureToPresent(toView: self.navigationController!.navigationBar)
SideMenuManager.default.menuAddScreenEdgePanGesturesToPresent(toView: self.navigationController!.view)
```
Then from a button, do something like this:
``` swift
present(SideMenuManager.menuLeftNavigationController!, animated: true, completion: nil)
present(SideMenuManager.default.menuLeftNavigationController!, animated: true, completion: nil)
// 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):
#### SideMenuManager
Just type ` SideMenuManager.default.menu...` and code completion will show you everything you can customize (defaults are shown below for reference):
``` swift
/**
The push style of the menu.
@@ -142,7 +139,7 @@ There are six modes in MenuPushStyle:
- 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
open var menuPushStyle: MenuPushStyle = .defaultBehavior
/**
The presentation mode of the menu.
@@ -153,64 +150,71 @@ There are four modes in MenuPresentMode:
- 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
open 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
open var menuAllowPushOfSameClassTwice = true
/// 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)
/**
Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is 75% of the screen width.
Note that each menu's width can be overridden using the `menuWidth` property on any `UISideMenuNavigationController` instance.
*/
open 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 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 var menuAnimationDismissDuration: Double = 0.35
/// Duration of the remaining animation when the menu is partially dismissed with gestures. Default is 0.35 seconds.
open var menuAnimationCompleteGestureDuration: Double = 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
open 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
open 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?
open 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
open 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
open 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
open var menuShadowRadius: CGFloat = 5
/// The left menu swipe to dismiss gesture.
open static weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer?
open weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer?
/// The right menu swipe to dismiss gesture.
open static weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer?
open weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer?
/// Enable or disable gestures that would swipe to dismiss the menu. Default is true.
open static var menuEnableSwipeGestures: Bool = true
open 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
open 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
open var menuParallaxStrength: Int = 0
/// Draws the `menuAnimationBackgroundColor` behind the status bar. Default is true.
open static var menuFadeStatusBar = true
open var menuFadeStatusBar = true
/// The animation options when a menu is displayed. Ignored when displayed with a gesture.
open static var menuAnimationOptions: UIViewAnimationOptions = .curveEaseInOut
open var menuAnimationOptions: UIViewAnimationOptions = .curveEaseInOut
/// The animation spring damping when a menu is displayed. Ignored when displayed with a gesture.
open static var menuAnimationUsingSpringWithDamping: CGFloat = 1
open 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
open var menuAnimationInitialSpringVelocity: CGFloat = 1
/**
Automatically dismisses the menu when another view is pushed from it.
@@ -218,41 +222,108 @@ Automatically dismisses the menu when another view is pushed from it.
Note: to prevent the menu from dismissing when presenting, set modalPresentationStyle = .overFullScreen
of the view controller being presented in storyboard or during its initalization.
*/
open static var menuDismissOnPush = true
open var menuDismissOnPush = true
/**
The blur effect style of the menu if the menu's root view controller is a UITableViewController or UICollectionViewController.
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?
- Note: If you want cells in a UITableViewController menu to show vibrancy, make them a subclass of UITableViewVibrantCell and set the `blurEffectStyle` of each cell to SideMenuManager.default.menuBlurEffectStyle.
*/
open var menuBlurEffectStyle: UIBlurEffectStyle?
/// The left menu.
open static var menuLeftNavigationController: UISideMenuNavigationController?
open var menuLeftNavigationController: UISideMenuNavigationController?
/// The right menu.
open static var menuRightNavigationController: UISideMenuNavigationController?
open var menuRightNavigationController: UISideMenuNavigationController?
/**
Adds screen edge gestures to a view to present a menu.
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, gestur=es will be added for both sides.
- 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]
- Returns: The array of screen edge gestures added to `toView`.
*/
@discardableResult open func menuAddScreenEdgePanGesturesToPresent(toView: UIView, forMenu:UIRectEdge? = nil) -> [UIScreenEdgePanGestureRecognizer]
/**
Adds a pan edge gesture to a view to present menus.
Adds a pan edge gesture to a view to present menus.
- Parameter toView: The view to add a pan gesture to.
- 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
- Returns: The pan gesture added to `toView`.
*/
@discardableResult open func menuAddPanGestureToPresent(toView: UIView) -> UIPanGestureRecognizer
```
#### UISideMenuNavigationController
`UISideMenuNavigationController` supports the following customizations and properties:
``` swift
/// SideMenuManager instance associated with this menu. Default is `SideMenuManager.default`. This property cannot be changed after the menu has loaded.
open weak var sideMenuManager: SideMenuManager! = SideMenuManager.default
/// Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is zero. When zero, `sideMenuManager.menuWidth` is used. This property cannot be changed while the isHidden property is false.
@IBInspectable open var menuWidth: CGFloat = 0
/// Whether the menu appears on the right or left side of the screen. Right is the default. This property cannot be changed after the menu has loaded.
@IBInspectable open var leftSide: Bool = false
/// Indicates if the menu is anywhere in the view hierarchy, even if covered by another view controller.
open var isHidden: Bool
```
#### UISideMenuNavigationControllerDelegate
To receive notifications when a menu is displayed from a view controller, have it adhere to the `UISideMenuNavigationControllerDelegate` protocol:
``` swift
extension MyViewController: UISideMenuNavigationControllerDelegate {
func sideMenuWillAppear(menu: UISideMenuNavigationController, animated: Bool) {
print("SideMenu Appearing! (animated: \(animated))")
}
func sideMenuDidAppear(menu: UISideMenuNavigationController, animated: Bool) {
print("SideMenu Appeared! (animated: \(animated))")
}
func sideMenuWillDisappear(menu: UISideMenuNavigationController, animated: Bool) {
print("SideMenu Disappearing! (animated: \(animated))")
}
func sideMenuDidDisappear(menu: UISideMenuNavigationController, animated: Bool) {
print("SideMenu Disappeared! (animated: \(animated))")
}
}
```
### Advanced
For simplicity, `SideMenuManager.default` serves as the primary instance as most projects will only need one menu across all screens. If you need to show a different SideMenu, such as from a modal view controller presented from a previous SideMenu, do the following:
1. Declare a variable containing your custom `SideMenuManager` instance. You may want it to define it globally and configure it in your app delegate if menus will be used on multiple screens.
``` swift
let customSideMenuManager = SideMenuManager()
```
2. Setup and display menus with your custom instance the same as you would with the `SideMenuManager.default` instance.
3. If using Storyboards, subclass your instance of `UISideMenuNavigationController` and set its `sideMenuManager` property to your custom instance. This must be done before `viewDidLoad` is called:
``` swift
class MySideMenuNavigationController: UISideMenuNavigationController {
let customSideMenuManager = SideMenuManager()
override func awakeFromNib() {
super.awakeFromNib()
sideMenuManager = customSideMenuManager
}
}
```
Alternatively, you can set `sideMenuManager` from the view controller that segues to your UISideMenuNavigationController:
``` swift
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let sideMenuNavigationController = segue.destination as? UISideMenuNavigationController {
sideMenuNavigationController.sideMenuManager = customSideMenuManager
}
}
```
*Important: displaying SideMenu instances directly over each other is not supported. Use `menuPushStyle = .subMenu` instead.*
## Known Issues
Don't try to change the status bar appearance when presenting a menu. When used with quick gestures/animations, it causes the presentation animation to not complete properly and locks the UI. This was fixed in iOS 9.3. See [radar 21961293](http://www.openradar.me/21961293) for more information.
+1 -1
View File
@@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = "SideMenu"
s.version = "2.3.3"
s.version = "3.1.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.
+26 -12
View File
@@ -15,7 +15,7 @@
7B48A0EB1DCB2487002990A1 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B48A0EA1DCB2487002990A1 /* Tests.swift */; };
7B48A0F61DCB2518002990A1 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B48A0F31DCB2518002990A1 /* MainViewController.swift */; };
7B48A0F71DCB2518002990A1 /* PresentedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B48A0F41DCB2518002990A1 /* PresentedViewController.swift */; };
7B48A0F81DCB2518002990A1 /* SideMenuTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B48A0F51DCB2518002990A1 /* SideMenuTableView.swift */; };
7B48A0F81DCB2518002990A1 /* SideMenuTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B48A0F51DCB2518002990A1 /* SideMenuTableViewController.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 */; };
@@ -53,7 +53,7 @@
7B48A0EC1DCB2487002990A1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
7B48A0F31DCB2518002990A1 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
7B48A0F41DCB2518002990A1 /* PresentedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentedViewController.swift; sourceTree = "<group>"; };
7B48A0F51DCB2518002990A1 /* SideMenuTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SideMenuTableView.swift; sourceTree = "<group>"; };
7B48A0F51DCB2518002990A1 /* SideMenuTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SideMenuTableViewController.swift; sourceTree = "<group>"; };
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; };
@@ -124,10 +124,10 @@
65FF1B391DE31DA3007B0845 /* SupportingFiles */,
7B48A0D51DCB2487002990A1 /* AppDelegate.swift */,
7B48A0F31DCB2518002990A1 /* MainViewController.swift */,
7B48A0F51DCB2518002990A1 /* SideMenuTableViewController.swift */,
7B48A0F41DCB2518002990A1 /* PresentedViewController.swift */,
7B552D5C1DCC65830010301C /* Launch Screen.storyboard */,
7B5FA9B51DCB269700278DF6 /* Main.storyboard */,
7B48A0F51DCB2518002990A1 /* SideMenuTableView.swift */,
7B48A0DC1DCB2487002990A1 /* Assets.xcassets */,
7B48A0E11DCB2487002990A1 /* Info.plist */,
);
@@ -279,21 +279,23 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0800;
LastUpgradeCheck = 0830;
LastUpgradeCheck = 0900;
ORGANIZATIONNAME = jonkykong;
TargetAttributes = {
7B48A0D21DCB2487002990A1 = {
CreatedOnToolsVersion = 8.0;
LastSwiftMigration = 0900;
ProvisioningStyle = Automatic;
};
7B48A0E51DCB2487002990A1 = {
CreatedOnToolsVersion = 8.0;
LastSwiftMigration = 0900;
ProvisioningStyle = Automatic;
TestTargetID = 7B48A0D21DCB2487002990A1;
};
7B9DC9031DC6E8C1000D4007 = {
CreatedOnToolsVersion = 8.0;
LastSwiftMigration = 0820;
LastSwiftMigration = 0900;
ProvisioningStyle = Automatic;
};
};
@@ -446,7 +448,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7B48A0F81DCB2518002990A1 /* SideMenuTableView.swift in Sources */,
7B48A0F81DCB2518002990A1 /* SideMenuTableViewController.swift in Sources */,
7B48A0F61DCB2518002990A1 /* MainViewController.swift in Sources */,
7B48A0F71DCB2518002990A1 /* PresentedViewController.swift in Sources */,
7B48A0D61DCB2487002990A1 /* AppDelegate.swift in Sources */,
@@ -493,7 +495,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.jonkykong.Example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
};
name = Debug;
};
@@ -507,7 +509,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.jonkykong.Example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
};
name = Release;
};
@@ -521,7 +523,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.jonkykong.ExampleTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example";
};
name = Debug;
@@ -536,7 +538,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.jonkykong.ExampleTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example";
};
name = Release;
@@ -550,7 +552,9 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
@@ -558,7 +562,11 @@
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
@@ -604,7 +612,9 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
@@ -612,7 +622,11 @@
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
@@ -660,7 +674,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
};
name = Debug;
};
@@ -681,7 +695,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.jonkykong.SideMenu;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 4.0;
};
name = Release;
};
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0830"
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
@@ -36,6 +37,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"