diff --git a/CHANGELOG.md b/CHANGELOG.md index 640a1e5..5c1b738 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,19 @@ # Change Log All notable changes of the library will be documented in this file. +## 2.2 +### Added +- Ability to globally change the drop shadow of the current content view while the menu is open (#61) +- New sample project controller to tweak the transition settings +- Status bar color can now be updated on a controller by controller basis + +### Changed +- Changed default `contentScale` value from `0.88` to `0.86` (#53, #66, #72) +- Updated sample project to better reflect the README demo gif + +### Breaking +- Dropped support for iOS 8 + ## 2.1 - [2017-10-23] ### Added - Swift 4 support diff --git a/InteractiveSideMenu.podspec b/InteractiveSideMenu.podspec index 165e01c..22368cb 100644 --- a/InteractiveSideMenu.podspec +++ b/InteractiveSideMenu.podspec @@ -1,14 +1,14 @@ Pod::Spec.new do |s| s.name = "InteractiveSideMenu" - s.version = "2.1" + s.version = "2.2" s.summary = "Interactive Side Menu in Swift" s.homepage = "https://github.com/handsomecode/InteractiveSideMenu" s.license = "Apache 2.0 license" - s.author = { "Andrey Arzhannikov" => "andreya@handsome.is" } + s.author = { "Eric Miller" => "eric@handsome.is" } s.source = { :git => "https://github.com/handsomecode/InteractiveSideMenu.git", :tag => "#{s.version}" } - s.platform = :ios, "8.0" + s.platform = :ios, "9.0" s.source_files = "Sources/*.swift" diff --git a/InteractiveSideMenu.xcodeproj/project.pbxproj b/InteractiveSideMenu.xcodeproj/project.pbxproj index 12327eb..14e9bb8 100644 --- a/InteractiveSideMenu.xcodeproj/project.pbxproj +++ b/InteractiveSideMenu.xcodeproj/project.pbxproj @@ -12,8 +12,9 @@ 881EF0921E3101130035DEB4 /* InteractiveSideMenu.h in Headers */ = {isa = PBXBuildFile; fileRef = 881EF0901E3101130035DEB4 /* InteractiveSideMenu.h */; settings = {ATTRIBUTES = (Public, ); }; }; 881EF0971E3101B80035DEB4 /* MenuAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881EF0931E3101B80035DEB4 /* MenuAnimator.swift */; }; 881EF0981E3101B80035DEB4 /* MenuContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881EF0941E3101B80035DEB4 /* MenuContainerViewController.swift */; }; - 881EF0991E3101B80035DEB4 /* MenuItemContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881EF0951E3101B80035DEB4 /* MenuItemContentViewController.swift */; }; + 881EF0991E3101B80035DEB4 /* SideMenuItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881EF0951E3101B80035DEB4 /* SideMenuItemContent.swift */; }; 881EF09A1E3101B80035DEB4 /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881EF0961E3101B80035DEB4 /* MenuViewController.swift */; }; + D48B8AEC202CDE1A0010D9A4 /* SideMenuItemShadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48B8AEB202CDE1A0010D9A4 /* SideMenuItemShadow.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -24,8 +25,9 @@ 881EF0901E3101130035DEB4 /* InteractiveSideMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InteractiveSideMenu.h; sourceTree = ""; }; 881EF0931E3101B80035DEB4 /* MenuAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuAnimator.swift; sourceTree = ""; }; 881EF0941E3101B80035DEB4 /* MenuContainerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuContainerViewController.swift; sourceTree = ""; }; - 881EF0951E3101B80035DEB4 /* MenuItemContentViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuItemContentViewController.swift; sourceTree = ""; }; + 881EF0951E3101B80035DEB4 /* SideMenuItemContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SideMenuItemContent.swift; sourceTree = ""; }; 881EF0961E3101B80035DEB4 /* MenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = ""; }; + D48B8AEB202CDE1A0010D9A4 /* SideMenuItemShadow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuItemShadow.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,7 +64,8 @@ 84E2E6761F15406600E5FF37 /* MenuTransitioningDelegate.swift */, 881EF0931E3101B80035DEB4 /* MenuAnimator.swift */, 881EF0941E3101B80035DEB4 /* MenuContainerViewController.swift */, - 881EF0951E3101B80035DEB4 /* MenuItemContentViewController.swift */, + 881EF0951E3101B80035DEB4 /* SideMenuItemContent.swift */, + D48B8AEB202CDE1A0010D9A4 /* SideMenuItemShadow.swift */, 881EF0961E3101B80035DEB4 /* MenuViewController.swift */, 881EF08F1E3101130035DEB4 /* Info.plist */, 881EF0901E3101130035DEB4 /* InteractiveSideMenu.h */, @@ -153,7 +156,8 @@ 881EF0971E3101B80035DEB4 /* MenuAnimator.swift in Sources */, 84E2E6771F15406600E5FF37 /* MenuTransitioningDelegate.swift in Sources */, 881EF0981E3101B80035DEB4 /* MenuContainerViewController.swift in Sources */, - 881EF0991E3101B80035DEB4 /* MenuItemContentViewController.swift in Sources */, + 881EF0991E3101B80035DEB4 /* SideMenuItemContent.swift in Sources */, + D48B8AEC202CDE1A0010D9A4 /* SideMenuItemShadow.swift in Sources */, 84E2E6751F15401000E5FF37 /* Options.swift in Sources */, 881EF09A1E3101B80035DEB4 /* MenuViewController.swift in Sources */, ); @@ -274,7 +278,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = is.handsome.InteractiveSideMenu; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -296,7 +300,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = is.handsome.InteractiveSideMenu; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/README.md b/README.md index 45acf0b..126d0d2 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ override func viewWillTransition(to size: CGSize, with coordinator: UIViewContro Check out the [Sample](./Sample) project for more details and usage examples. # Known Issues -There is [an issue](https://github.com/handsomecode/InteractiveSideMenu/issues/53) associated with the scaling of a UINavigationBar in iOS 11. Status bar background is hidden during side menu closing if `contentScale < 1`. This is an *iOS 11* issue and has been reported to Apple. +There is [an issue](https://github.com/handsomecode/InteractiveSideMenu/issues/53) associated with the content controller's view not properly having the `safeAreaInsets` set. This causes the view's layout to shift when the side menu is closed. The issue appears to be tied to the transition options `contentScale` setting. Choosing a value in the range 0.87 - 0.91 causes the `safeAreaInsets.top` to be set to `0.0`. The default value of the library is no longer within this range but be mindful if changing that value for your own application. # Requirements diff --git a/Sample/Sample.xcodeproj/project.pbxproj b/Sample/Sample.xcodeproj/project.pbxproj index 4a5ceef..8cc08aa 100644 --- a/Sample/Sample.xcodeproj/project.pbxproj +++ b/Sample/Sample.xcodeproj/project.pbxproj @@ -13,10 +13,17 @@ 881EF0B21E3102E30035DEB4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 881EF0B01E3102E30035DEB4 /* LaunchScreen.storyboard */; }; 881EF0BD1E31037D0035DEB4 /* KittyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881EF0B91E31037D0035DEB4 /* KittyViewController.swift */; }; 881EF0BE1E31037D0035DEB4 /* HostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881EF0BA1E31037D0035DEB4 /* HostViewController.swift */; }; - 881EF0BF1E31037D0035DEB4 /* NavigationMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881EF0BB1E31037D0035DEB4 /* NavigationMenuViewController.swift */; }; + 881EF0BF1E31037D0035DEB4 /* SampleMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881EF0BB1E31037D0035DEB4 /* SampleMenuViewController.swift */; }; 9A15C5481EDD62BC00C4FD75 /* InteractiveSideMenu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 881EF0C71E31049C0035DEB4 /* InteractiveSideMenu.framework */; }; 9A15C5491EDD62BC00C4FD75 /* InteractiveSideMenu.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 881EF0C71E31049C0035DEB4 /* InteractiveSideMenu.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9A6755031EA8E30C00F0C71D /* TabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6755021EA8E30C00F0C71D /* TabBarViewController.swift */; }; + D4028937202CEC45009F1262 /* SampleTableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4028936202CEC45009F1262 /* SampleTableCell.swift */; }; + D4C58473202DD647006EF43A /* KittyViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D4C58472202DD647006EF43A /* KittyViewController.storyboard */; }; + D4C58476202DD6B0006EF43A /* Storyboardable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C58475202DD6B0006EF43A /* Storyboardable.swift */; }; + D4C58478202DD6DF006EF43A /* TabBarViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D4C58477202DD6DF006EF43A /* TabBarViewController.storyboard */; }; + D4C5847F202DD896006EF43A /* SampleMenuViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D4C5847E202DD896006EF43A /* SampleMenuViewController.storyboard */; }; + D4C58482202DECB4006EF43A /* TweakViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C58481202DECB4006EF43A /* TweakViewController.swift */; }; + D4C58484202DECBB006EF43A /* TweakViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D4C58483202DECBB006EF43A /* TweakViewController.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -66,9 +73,16 @@ 881EF0B31E3102E30035DEB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 881EF0B91E31037D0035DEB4 /* KittyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KittyViewController.swift; sourceTree = ""; }; 881EF0BA1E31037D0035DEB4 /* HostViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = HostViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 881EF0BB1E31037D0035DEB4 /* NavigationMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationMenuViewController.swift; sourceTree = ""; }; + 881EF0BB1E31037D0035DEB4 /* SampleMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleMenuViewController.swift; sourceTree = ""; }; 881EF0C21E31049C0035DEB4 /* InteractiveSideMenu.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = InteractiveSideMenu.xcodeproj; path = ../InteractiveSideMenu.xcodeproj; sourceTree = ""; }; 9A6755021EA8E30C00F0C71D /* TabBarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarViewController.swift; sourceTree = ""; }; + D4028936202CEC45009F1262 /* SampleTableCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleTableCell.swift; sourceTree = ""; }; + D4C58472202DD647006EF43A /* KittyViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KittyViewController.storyboard; sourceTree = ""; }; + D4C58475202DD6B0006EF43A /* Storyboardable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storyboardable.swift; sourceTree = ""; }; + D4C58477202DD6DF006EF43A /* TabBarViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = TabBarViewController.storyboard; sourceTree = ""; }; + D4C5847E202DD896006EF43A /* SampleMenuViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SampleMenuViewController.storyboard; sourceTree = ""; }; + D4C58481202DECB4006EF43A /* TweakViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TweakViewController.swift; sourceTree = ""; }; + D4C58483202DECBB006EF43A /* TweakViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = TweakViewController.storyboard; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -103,33 +117,72 @@ 881EF0A61E3102E30035DEB4 /* Sample */ = { isa = PBXGroup; children = ( - 881EF0C11E3103A80035DEB4 /* MenuContentScreens */, - 881EF0BA1E31037D0035DEB4 /* HostViewController.swift */, - 881EF0BB1E31037D0035DEB4 /* NavigationMenuViewController.swift */, - 881EF0A71E3102E30035DEB4 /* AppDelegate.swift */, 881EF0AB1E3102E30035DEB4 /* Main.storyboard */, - 881EF0AE1E3102E30035DEB4 /* Assets.xcassets */, - 881EF0B01E3102E30035DEB4 /* LaunchScreen.storyboard */, - 881EF0B31E3102E30035DEB4 /* Info.plist */, + 881EF0A71E3102E30035DEB4 /* AppDelegate.swift */, + D4C5847D202DD877006EF43A /* Controllers */, + D4C5847A202DD810006EF43A /* Menu Controllers */, + D4C5847C202DD859006EF43A /* Cells */, + D4C58474202DD6A2006EF43A /* Helpers */, + D4C5847B202DD828006EF43A /* Supporting Items */, ); path = Sample; sourceTree = ""; }; - 881EF0C11E3103A80035DEB4 /* MenuContentScreens */ = { + 881EF0C31E31049C0035DEB4 /* Products */ = { + isa = PBXGroup; + children = ( + 881EF0C71E31049C0035DEB4 /* InteractiveSideMenu.framework */, + ); + name = Products; + sourceTree = ""; + }; + D4C58474202DD6A2006EF43A /* Helpers */ = { + isa = PBXGroup; + children = ( + D4C58475202DD6B0006EF43A /* Storyboardable.swift */, + ); + path = Helpers; + sourceTree = ""; + }; + D4C5847A202DD810006EF43A /* Menu Controllers */ = { isa = PBXGroup; children = ( 881EF0B91E31037D0035DEB4 /* KittyViewController.swift */, + D4C58472202DD647006EF43A /* KittyViewController.storyboard */, 9A6755021EA8E30C00F0C71D /* TabBarViewController.swift */, + D4C58477202DD6DF006EF43A /* TabBarViewController.storyboard */, + D4C58481202DECB4006EF43A /* TweakViewController.swift */, + D4C58483202DECBB006EF43A /* TweakViewController.storyboard */, ); - name = MenuContentScreens; + path = "Menu Controllers"; sourceTree = ""; }; - 881EF0C31E31049C0035DEB4 /* Products */ = { + D4C5847B202DD828006EF43A /* Supporting Items */ = { isa = PBXGroup; children = ( - 881EF0C71E31049C0035DEB4 /* InteractiveSideMenu.framework */, + 881EF0AE1E3102E30035DEB4 /* Assets.xcassets */, + 881EF0B01E3102E30035DEB4 /* LaunchScreen.storyboard */, + 881EF0B31E3102E30035DEB4 /* Info.plist */, ); - name = Products; + name = "Supporting Items"; + sourceTree = ""; + }; + D4C5847C202DD859006EF43A /* Cells */ = { + isa = PBXGroup; + children = ( + D4028936202CEC45009F1262 /* SampleTableCell.swift */, + ); + path = Cells; + sourceTree = ""; + }; + D4C5847D202DD877006EF43A /* Controllers */ = { + isa = PBXGroup; + children = ( + 881EF0BA1E31037D0035DEB4 /* HostViewController.swift */, + 881EF0BB1E31037D0035DEB4 /* SampleMenuViewController.swift */, + D4C5847E202DD896006EF43A /* SampleMenuViewController.storyboard */, + ); + path = Controllers; sourceTree = ""; }; /* End PBXGroup section */ @@ -210,8 +263,12 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + D4C58484202DECBB006EF43A /* TweakViewController.storyboard in Resources */, + D4C5847F202DD896006EF43A /* SampleMenuViewController.storyboard in Resources */, 881EF0B21E3102E30035DEB4 /* LaunchScreen.storyboard in Resources */, + D4C58473202DD647006EF43A /* KittyViewController.storyboard in Resources */, 881EF0AF1E3102E30035DEB4 /* Assets.xcassets in Resources */, + D4C58478202DD6DF006EF43A /* TabBarViewController.storyboard in Resources */, 881EF0AD1E3102E30035DEB4 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -225,9 +282,12 @@ files = ( 881EF0BD1E31037D0035DEB4 /* KittyViewController.swift in Sources */, 881EF0BE1E31037D0035DEB4 /* HostViewController.swift in Sources */, - 881EF0BF1E31037D0035DEB4 /* NavigationMenuViewController.swift in Sources */, + D4C58476202DD6B0006EF43A /* Storyboardable.swift in Sources */, + D4028937202CEC45009F1262 /* SampleTableCell.swift in Sources */, + 881EF0BF1E31037D0035DEB4 /* SampleMenuViewController.swift in Sources */, 9A6755031EA8E30C00F0C71D /* TabBarViewController.swift in Sources */, 881EF0A81E3102E30035DEB4 /* AppDelegate.swift in Sources */, + D4C58482202DECB4006EF43A /* TweakViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -363,7 +423,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = Sample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = is.handsome.Sample; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -377,7 +437,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = Sample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = is.handsome.Sample; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Sample/Sample/Assets.xcassets/kitten_avatar.imageset/Contents.json b/Sample/Sample/Assets.xcassets/kitten_avatar.imageset/Contents.json new file mode 100644 index 0000000..10e14b7 --- /dev/null +++ b/Sample/Sample/Assets.xcassets/kitten_avatar.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "kitten_avatar.jpeg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sample/Sample/Assets.xcassets/kitten_avatar.imageset/kitten_avatar.jpeg b/Sample/Sample/Assets.xcassets/kitten_avatar.imageset/kitten_avatar.jpeg new file mode 100644 index 0000000..586426c Binary files /dev/null and b/Sample/Sample/Assets.xcassets/kitten_avatar.imageset/kitten_avatar.jpeg differ diff --git a/Sample/Sample/Base.lproj/Main.storyboard b/Sample/Sample/Base.lproj/Main.storyboard index 5ad94b9..e8ee619 100644 --- a/Sample/Sample/Base.lproj/Main.storyboard +++ b/Sample/Sample/Base.lproj/Main.storyboard @@ -1,103 +1,14 @@ - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -116,139 +27,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Sample/Sample/Cells/SampleTableCell.swift b/Sample/Sample/Cells/SampleTableCell.swift new file mode 100644 index 0000000..e1ec603 --- /dev/null +++ b/Sample/Sample/Cells/SampleTableCell.swift @@ -0,0 +1,27 @@ +// +// SampleTableCell.swift +// Sample +// +// Created by Eric Miller on 2/8/18. +// Copyright © 2018 Handsome. All rights reserved. +// + +import UIKit + +class SampleTableCell: UITableViewCell { + + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet private weak var selectedView: UIView! + + override func awakeFromNib() { + super.awakeFromNib() + selectionStyle = .none + } + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + selectedView.backgroundColor = selected ? UIColor.yellow : UIColor.clear + titleLabel.textColor = selected ? UIColor.yellow : UIColor.white + } +} diff --git a/Sample/Sample/HostViewController.swift b/Sample/Sample/Controllers/HostViewController.swift similarity index 74% rename from Sample/Sample/HostViewController.swift rename to Sample/Sample/Controllers/HostViewController.swift index 1226bc9..0abe1df 100644 --- a/Sample/Sample/HostViewController.swift +++ b/Sample/Sample/Controllers/HostViewController.swift @@ -19,7 +19,7 @@ import UIKit import InteractiveSideMenu -/* +/** HostViewController is container view controller, contains menu controller and the list of relevant view controllers. Responsible for creating and selecting menu items content controlers. @@ -27,10 +27,6 @@ import InteractiveSideMenu */ class HostViewController: MenuContainerViewController { - override var preferredStatusBarStyle: UIStatusBarStyle { - return .lightContent - } - override var prefersStatusBarHidden: Bool { return false } @@ -42,7 +38,7 @@ class HostViewController: MenuContainerViewController { self.transitionOptions = TransitionOptions(duration: 0.4, visibleContentWidth: screenSize.width / 6) // Instantiate menu view controller by identifier - self.menuViewController = self.storyboard!.instantiateViewController(withIdentifier: "NavigationMenu") as! MenuViewController + self.menuViewController = SampleMenuViewController.storyboardViewController() // Gather content items controllers self.contentViewControllers = contentControllers() @@ -54,9 +50,7 @@ class HostViewController: MenuContainerViewController { override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) - /* - Options to customize menu transition animation. - */ + // Options to customize menu transition animation. var options = TransitionOptions() // Animation duration @@ -68,18 +62,10 @@ class HostViewController: MenuContainerViewController { } private func contentControllers() -> [UIViewController] { - let controllersIdentifiers = ["Kitty", "TabBar"] - var contentList = [UIViewController]() - - /* - Instantiate items controllers from storyboard. - */ - for identifier in controllersIdentifiers { - if let viewController = self.storyboard?.instantiateViewController(withIdentifier: identifier) { - contentList.append(viewController) - } - } + let kittyController = KittyViewController.storyboardViewController() + let tabController = TabBarViewController.storyboardNavigationController() + let tweakController = TweakViewController.storyboardNavigationController() - return contentList + return [kittyController, tabController, tweakController] } } diff --git a/Sample/Sample/Controllers/SampleMenuViewController.storyboard b/Sample/Sample/Controllers/SampleMenuViewController.storyboard new file mode 100644 index 0000000..ef8d9ab --- /dev/null +++ b/Sample/Sample/Controllers/SampleMenuViewController.storyboard @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sample/Sample/Controllers/SampleMenuViewController.swift b/Sample/Sample/Controllers/SampleMenuViewController.swift new file mode 100644 index 0000000..d3a5211 --- /dev/null +++ b/Sample/Sample/Controllers/SampleMenuViewController.swift @@ -0,0 +1,111 @@ +// +// SampleMenuViewController.swift +// +// Copyright 2017 Handsome LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import InteractiveSideMenu + +/** + Menu controller is responsible for creating its content and showing/hiding menu using 'menuContainerViewController' property. + */ +class SampleMenuViewController: MenuViewController, Storyboardable { + + @IBOutlet fileprivate weak var tableView: UITableView! + @IBOutlet fileprivate weak var avatarImageView: UIImageView! + @IBOutlet fileprivate weak var avatarImageViewCenterXConstraint: NSLayoutConstraint! + private var gradientLayer = CAGradientLayer() + + private var gradientApplied: Bool = false + + override var prefersStatusBarHidden: Bool { + return false + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } + + override func viewDidLoad() { + super.viewDidLoad() + + // Select the initial row + tableView.selectRow(at: IndexPath(row: 0, section: 0), animated: false, scrollPosition: UITableViewScrollPosition.none) + + avatarImageView.layer.cornerRadius = avatarImageView.frame.size.width/2 + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + avatarImageViewCenterXConstraint.constant = -(menuContainerViewController?.transitionOptions.visibleContentWidth ?? 0.0)/2 + + if gradientLayer.superlayer != nil { + gradientLayer.removeFromSuperlayer() + } + let topColor = UIColor(red: 16.0/255.0, green: 12.0/255.0, blue: 54.0/255.0, alpha: 1.0) + let bottomColor = UIColor(red: 57.0/255.0, green: 33.0/255.0, blue: 61.0/255.0, alpha: 1.0) + gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0) + gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0) + gradientLayer.colors = [topColor.cgColor, bottomColor.cgColor] + gradientLayer.frame = view.bounds + view.layer.insertSublayer(gradientLayer, at: 0) + } + + deinit{ + print() + } +} + +extension SampleMenuViewController: UITableViewDelegate, UITableViewDataSource { + + func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return menuContainerViewController?.contentViewControllers.count ?? 0 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SampleTableCell.self), for: indexPath) as? SampleTableCell else { + preconditionFailure("Unregistered table view cell") + } + + cell.titleLabel.text = menuContainerViewController?.contentViewControllers[indexPath.row].title ?? "A Controller" + + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let menuContainerViewController = self.menuContainerViewController else { + return + } + + menuContainerViewController.selectContentViewController(menuContainerViewController.contentViewControllers[indexPath.row]) + menuContainerViewController.hideSideMenu() + } + + func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + let v = UIView() + v.backgroundColor = UIColor.clear + return v + } + + func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + return 0.5 + } +} diff --git a/Sample/Sample/Helpers/Storyboardable.swift b/Sample/Sample/Helpers/Storyboardable.swift new file mode 100644 index 0000000..cdef6d2 --- /dev/null +++ b/Sample/Sample/Helpers/Storyboardable.swift @@ -0,0 +1,50 @@ +// +// Storyboardable.swift +// bthere +// +// Created by Eric Miller on 11/27/17. +// Copyright © 2017 Handsome. All rights reserved. +// + +import UIKit + +protocol Storyboardable: class { + static var defaultStoryboardName: String { get } +} + +extension Storyboardable where Self: UIViewController { + static var defaultStoryboardName: String { + return String(describing: self) + } + + static func storyboardViewController() -> Self { + let storyboard = UIStoryboard(name: defaultStoryboardName, bundle: nil) + + guard let vc = storyboard.instantiateInitialViewController() as? Self else { + fatalError("Could not instantiate initial storyboard with name: \(defaultStoryboardName)") + } + + return vc + } + + static func storyboardNavigationController() -> UINavigationController { + let storyboard = UIStoryboard(name: defaultStoryboardName, bundle: nil) + + guard let nc = storyboard.instantiateInitialViewController() as? UINavigationController else { + fatalError("Could not instantiate initial storyboard with name: \(defaultStoryboardName)") + } + + return nc + } + + static func storyboardTabBarController() -> UITabBarController { + let storyboard = UIStoryboard(name: defaultStoryboardName, bundle: nil) + + guard let tc = storyboard.instantiateInitialViewController() as? UITabBarController else { + fatalError("Could not instantiate initial storyboard with name: \(defaultStoryboardName)") + } + + return tc + } +} + diff --git a/Sample/Sample/Menu Controllers/KittyViewController.storyboard b/Sample/Sample/Menu Controllers/KittyViewController.storyboard new file mode 100644 index 0000000..0bb8b60 --- /dev/null +++ b/Sample/Sample/Menu Controllers/KittyViewController.storyboard @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sample/Sample/KittyViewController.swift b/Sample/Sample/Menu Controllers/KittyViewController.swift similarity index 85% rename from Sample/Sample/KittyViewController.swift rename to Sample/Sample/Menu Controllers/KittyViewController.swift index 605fe76..d26bb32 100644 --- a/Sample/Sample/KittyViewController.swift +++ b/Sample/Sample/Menu Controllers/KittyViewController.swift @@ -19,13 +19,16 @@ import UIKit import InteractiveSideMenu -/* +/** KittyViewController is a controller relevant one of the side menu items. To indicate this it adopts `SideMenuItemContent` protocol. */ -class KittyViewController: UIViewController, SideMenuItemContent { +class KittyViewController: UIViewController, SideMenuItemContent, Storyboardable { - /* Show side menu on menu button click - */ + override var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } + + // Show side menu on menu button click @IBAction func openMenu(_ sender: UIButton) { showSideMenu() } diff --git a/Sample/Sample/Menu Controllers/TabBarViewController.storyboard b/Sample/Sample/Menu Controllers/TabBarViewController.storyboard new file mode 100644 index 0000000..a23db9e --- /dev/null +++ b/Sample/Sample/Menu Controllers/TabBarViewController.storyboard @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sample/Sample/TabBarViewController.swift b/Sample/Sample/Menu Controllers/TabBarViewController.swift similarity index 57% rename from Sample/Sample/TabBarViewController.swift rename to Sample/Sample/Menu Controllers/TabBarViewController.swift index 694838f..d5ff291 100644 --- a/Sample/Sample/TabBarViewController.swift +++ b/Sample/Sample/Menu Controllers/TabBarViewController.swift @@ -19,42 +19,27 @@ import UIKit import InteractiveSideMenu -/* +/** TabBarViewController is a controller relevant one of the side menu items. To indicate this it adopts `SideMenuItemContent` protocol. */ -class TabBarViewController: UITabBarController, SideMenuItemContent { +class TabBarViewController: UITabBarController, SideMenuItemContent, Storyboardable { + + override var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } + + @IBAction func openMenu(_ sender: UIBarButtonItem) { + showSideMenu() + } } -/* +/** The first controller of tab bar. */ -class FirstViewController: UIViewController { +class FirstViewController: UIViewController { } - /* - Show menu on click if connected tab bar controller adopts proper protocol. - */ - @IBAction func openMenu(_ sender: UIButton) { - - if let menuItemViewController = self.tabBarController as? SideMenuItemContent { - menuItemViewController.showSideMenu() - } - } -} - -/* +/** The second controller of tab bar. */ -class SecondViewController: UIViewController { - - /* - Show menu on click if connected tab bar controller adopts proper protocol. - */ - @IBAction func openMenu(_ sender: UIButton) { - - if let menuItemViewController = self.tabBarController as? SideMenuItemContent { - menuItemViewController.showSideMenu() - } - } - -} +class SecondViewController: UIViewController { } diff --git a/Sample/Sample/Menu Controllers/TweakViewController.storyboard b/Sample/Sample/Menu Controllers/TweakViewController.storyboard new file mode 100644 index 0000000..91f4837 --- /dev/null +++ b/Sample/Sample/Menu Controllers/TweakViewController.storyboard @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sample/Sample/Menu Controllers/TweakViewController.swift b/Sample/Sample/Menu Controllers/TweakViewController.swift new file mode 100644 index 0000000..4b3d11b --- /dev/null +++ b/Sample/Sample/Menu Controllers/TweakViewController.swift @@ -0,0 +1,61 @@ +// +// TweakViewController.swift +// Sample +// +// Created by Eric Miller on 2/9/18. +// Copyright © 2018 Handsome. All rights reserved. +// + +import UIKit +import InteractiveSideMenu + +class TweakViewController: UIViewController, SideMenuItemContent, Storyboardable { + + @IBOutlet private weak var animationDurationValueLabel: UILabel! + @IBOutlet private weak var contentScaleValueLabel: UILabel! + @IBOutlet private weak var visibilityValueLabel: UILabel! + @IBOutlet private weak var visibilitySlider: UISlider! + + override var preferredStatusBarStyle: UIStatusBarStyle { + return .default + } + + override func viewDidLoad() { + super.viewDidLoad() + + visibilitySlider.maximumValue = Float(UIScreen.main.bounds.width) + if let navController = parent as? UINavigationController, + let menuContainerController = navController.parent as? MenuContainerViewController { + visibilitySlider.value = Float(menuContainerController.transitionOptions.visibleContentWidth) + visibilityValueLabel.text = "\(menuContainerController.transitionOptions.visibleContentWidth)" + } + } + + @IBAction func openMenu(_ sender: UIBarButtonItem) { + showSideMenu() + } + + @IBAction func animationDurationDidChange(_ slider: UISlider) { + animationDurationValueLabel.text = "\(TimeInterval(slider.value))" + if let navController = parent as? UINavigationController, + let menuContainerController = navController.parent as? MenuContainerViewController { + menuContainerController.transitionOptions.duration = TimeInterval(slider.value) + } + } + + @IBAction func contentScaleDidChange(_ slider: UISlider) { + contentScaleValueLabel.text = "\(CGFloat(slider.value))" + if let navController = parent as? UINavigationController, + let menuContainerController = navController.parent as? MenuContainerViewController { + menuContainerController.transitionOptions.contentScale = CGFloat(slider.value) + } + } + + @IBAction func visibilityDidChange(_ slider: UISlider) { + visibilityValueLabel.text = "\(CGFloat(slider.value))" + if let navController = parent as? UINavigationController, + let menuContainerController = navController.parent as? MenuContainerViewController { + menuContainerController.transitionOptions.visibleContentWidth = CGFloat(slider.value) + } + } +} diff --git a/Sample/Sample/NavigationMenuViewController.swift b/Sample/Sample/NavigationMenuViewController.swift deleted file mode 100644 index bb58a54..0000000 --- a/Sample/Sample/NavigationMenuViewController.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// NavigationMenuViewController.swift -// -// Copyright 2017 Handsome LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import UIKit -import InteractiveSideMenu - -/* - Menu controller is responsible for creating its content and showing/hiding menu using 'menuContainerViewController' property. - */ -class NavigationMenuViewController: MenuViewController { - - let kCellReuseIdentifier = "MenuCell" - let menuItems = ["Kitty", "TabBar cats"] - - @IBOutlet weak var tableView: UITableView! - - override var prefersStatusBarHidden: Bool { - return false - } - - override func viewDidLoad() { - super.viewDidLoad() - - // Select the initial row - tableView.selectRow(at: IndexPath(row: 0, section: 0), animated: false, scrollPosition: UITableViewScrollPosition.none) - } -} - -/* - Extention of `NavigationMenuViewController` class, implements table view delegates methods. - */ -extension NavigationMenuViewController: UITableViewDelegate, UITableViewDataSource { - - func numberOfSections(in tableView: UITableView) -> Int { - return 1 - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return menuItems.count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: kCellReuseIdentifier, for: indexPath) - cell.textLabel?.text = menuItems[indexPath.row] - - return cell - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - - guard let menuContainerViewController = self.menuContainerViewController else { - return - } - - menuContainerViewController.selectContentViewController(menuContainerViewController.contentViewControllers[indexPath.row]) - menuContainerViewController.hideSideMenu() - } -} diff --git a/Sources/MenuAnimator.swift b/Sources/MenuAnimator.swift index 2059fed..4cecd4f 100644 --- a/Sources/MenuAnimator.swift +++ b/Sources/MenuAnimator.swift @@ -25,10 +25,9 @@ final class MenuInteractiveTransition: NSObject { typealias Action = () -> () - //MARK: - Properties - // var present = false var interactionInProgress = false + var shadowOptions: SideMenuItemShadow var options = TransitionOptions() fileprivate let presentAction: Action @@ -42,15 +41,25 @@ final class MenuInteractiveTransition: NSObject { fileprivate var tapRecognizer: UITapGestureRecognizer? fileprivate var panRecognizer: UIPanGestureRecognizer? - required init(presentAction: @escaping Action, dismissAction: @escaping Action) { + required init(shadowOptions: SideMenuItemShadow, presentAction: @escaping Action, dismissAction: @escaping Action) { + self.shadowOptions = shadowOptions self.presentAction = presentAction self.dismissAction = dismissAction super.init() } + + @objc func handlePanPresentation(recognizer: UIPanGestureRecognizer) { + present = true + handlePan(recognizer: recognizer) + } + + @objc func handlePanDismission(recognizer: UIPanGestureRecognizer) { + present = false + handlePan(recognizer: recognizer) + } } //MARK: - UIViewControllerInteractiveTransitioning -// extension MenuInteractiveTransition: UIViewControllerInteractiveTransitioning { func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) { @@ -59,7 +68,6 @@ extension MenuInteractiveTransition: UIViewControllerInteractiveTransitioning { } //MARK: - UIViewControllerAnimatedTransitioning -// extension MenuInteractiveTransition: UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { @@ -73,22 +81,8 @@ extension MenuInteractiveTransition: UIViewControllerAnimatedTransitioning { } //MARK: - Private methods -// -extension MenuInteractiveTransition { - - @objc func handlePanPresentation(recognizer: UIPanGestureRecognizer) { - present = true - - handlePan(recognizer: recognizer) - } - - @objc func handlePanDismission(recognizer: UIPanGestureRecognizer) { - present = false - - handlePan(recognizer: recognizer) - } - - fileprivate func startTransition(transitionContext: UIViewControllerContextTransitioning) { +private extension MenuInteractiveTransition { + func startTransition(transitionContext: UIViewControllerContextTransitioning) { transitionStarted = true self.transitionContext = transitionContext @@ -141,7 +135,7 @@ extension MenuInteractiveTransition { fromViewController.view.isUserInteractionEnabled = false } - fileprivate func finishTransition(currentPercentComplete : CGFloat) { + func finishTransition(currentPercentComplete : CGFloat) { transitionStarted = false let animation: () -> Void = { [unowned self] in self.updateTransition(percentComplete: 1.0) } @@ -199,7 +193,7 @@ extension MenuInteractiveTransition { } } - private func createSnapshotView(from: UIView) -> UIView { + func createSnapshotView(from: UIView) -> UIView { guard let snapshotView = from.snapshotView(afterScreenUpdates: true) else { print("Invalid snapshot view. Default color will be used") let placeholderView = UIView(frame: from.frame) @@ -211,17 +205,19 @@ extension MenuInteractiveTransition { return snapshotView } - private func addShadow(to view: UIView) { - view.layer.shadowColor = UIColor.black.cgColor - view.layer.shadowOpacity = 0.3 - view.layer.shadowOffset = CGSize(width: -5, height: 5) + func addShadow(to view: UIView) { + view.layer.shadowColor = shadowOptions.color?.cgColor + view.layer.shadowOpacity = Float(shadowOptions.opacity) + view.layer.shadowOffset = shadowOptions.offset } - private func removeShadow(from view: UIView) { + func removeShadow(from view: UIView) { + view.layer.shadowColor = nil + view.layer.shadowOpacity = 0.0 view.layer.shadowOffset = .zero } - private func cancelTransition(currentPercentComplete : CGFloat) { + func cancelTransition(currentPercentComplete : CGFloat) { transitionStarted = false let animation : () -> Void = { [unowned self] in self.updateTransition(percentComplete: 0) } @@ -270,7 +266,7 @@ extension MenuInteractiveTransition { } } - private func handlePan(recognizer: UIPanGestureRecognizer) { + func handlePan(recognizer: UIPanGestureRecognizer) { guard let recognizerView = recognizer.view else { fatalError("Invalid recognizer view value") } @@ -340,7 +336,7 @@ extension MenuInteractiveTransition { } } - private func updateTransition(percentComplete: CGFloat) { + func updateTransition(percentComplete: CGFloat) { guard let transitionContext = self.transitionContext else { fatalError("Invalid `transitionContext` value. This property should not be nil") } diff --git a/Sources/MenuContainerViewController.swift b/Sources/MenuContainerViewController.swift index b7b221b..3feddff 100644 --- a/Sources/MenuContainerViewController.swift +++ b/Sources/MenuContainerViewController.swift @@ -23,8 +23,23 @@ import UIKit */ open class MenuContainerViewController: UIViewController { - // Describes if side menu is shown or not. - var isShown = false + open override var preferredStatusBarStyle: UIStatusBarStyle { + return currentContentViewController?.preferredStatusBarStyle ?? .lightContent + } + + fileprivate weak var currentContentViewController: UIViewController? + fileprivate var navigationMenuTransitionDelegate: MenuTransitioningDelegate! + + /** + Flag indicating if the side menu is being shown. + */ + fileprivate var isShown = false + + public var shadowOptions = SideMenuItemShadow() { + didSet { + navigationMenuTransitionDelegate?.shadowOptions = shadowOptions + } + } /** The view controller for side menu. @@ -58,51 +73,23 @@ open class MenuContainerViewController: UIViewController { */ public var contentViewControllers = [UIViewController]() - /** - Shows left side menu. - */ - public func showSideMenu() { - presentNavigationMenu() - } - - /** - Hides left side menu. - Controller from the right side will be visible. - */ - public func hideSideMenu() { - dismissNavigationMenu() - } - - /** - Embeds menu item content view controller. - - - parameter selectedContentVC: The view controller to be embedded. - */ - public func selectContentViewController(_ selectedContentVC: UIViewController) { - if let currentContentVC = currentContentViewController { - if currentContentVC != selectedContentVC { - currentContentVC.view.removeFromSuperview() - currentContentVC.removeFromParentViewController() - setCurrentView(selectedContentVC) - } - } else { - setCurrentView(selectedContentVC) - } - } // MARK: - Controller lifecycle // override open func viewDidLoad() { super.viewDidLoad() - navigationMenuTransitionDelegate = MenuTransitioningDelegate(interactiveTransition: MenuInteractiveTransition( + let interactiveTransition = MenuInteractiveTransition( + shadowOptions: shadowOptions, presentAction: { [unowned self] in self.presentNavigationMenu() }, dismissAction: { [unowned self] in self.dismissNavigationMenu() } - )) + ) + + navigationMenuTransitionDelegate = MenuTransitioningDelegate(interactiveTransition: interactiveTransition) let screenEdgePanRecognizer = UIScreenEdgePanGestureRecognizer( target: navigationMenuTransitionDelegate.interactiveTransition, @@ -131,18 +118,53 @@ open class MenuContainerViewController: UIViewController { } }, completion: nil) } +} - // MARK: - Private - // - private weak var currentContentViewController: UIViewController? - private var navigationMenuTransitionDelegate: MenuTransitioningDelegate! +// MARK: - Public +extension MenuContainerViewController { + + /** + Shows left side menu. + */ + public func showSideMenu() { + presentNavigationMenu() + } + + /** + Hides left side menu. + Controller from the right side will be visible. + */ + public func hideSideMenu() { + dismissNavigationMenu() + } + + /** + Embeds menu item content view controller. + + - parameter selectedContentVC: The view controller to be embedded. + */ + public func selectContentViewController(_ selectedContentVC: UIViewController) { + if let currentContentVC = currentContentViewController { + if currentContentVC != selectedContentVC { + currentContentVC.view.removeFromSuperview() + currentContentVC.removeFromParentViewController() + + setCurrentView(selectedContentVC) + } + } else { + setCurrentView(selectedContentVC) + } + } +} +// MARK: - Private +fileprivate extension MenuContainerViewController { /** Adds proper content view controller as a child. - parameter selectedContentVC: The view controller to be added. */ - private func setCurrentView(_ selectedContentVC: UIViewController) { + func setCurrentView(_ selectedContentVC: UIViewController) { addChildViewController(selectedContentVC) view.addSubviewWithFullSizeConstraints(view: selectedContentVC.view) currentContentViewController = selectedContentVC @@ -151,7 +173,7 @@ open class MenuContainerViewController: UIViewController { /** Presents left side menu. */ - private func presentNavigationMenu() { + func presentNavigationMenu() { if menuViewController == nil { fatalError("Invalid `menuViewController` value. It should not be nil") } @@ -162,7 +184,7 @@ open class MenuContainerViewController: UIViewController { /** Dismisses left side menu. */ - private func dismissNavigationMenu() { + func dismissNavigationMenu() { self.dismiss(animated: true, completion: nil) isShown = false } @@ -177,7 +199,10 @@ extension UIView { view.translatesAutoresizingMaskIntoConstraints = false insertSubview(view, at: atIndex) - addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["view": view])) - addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["view": view])) + let top = view.topAnchor.constraint(equalTo: self.topAnchor) + let leading = view.leadingAnchor.constraint(equalTo: self.leadingAnchor) + let trailing = self.trailingAnchor.constraint(equalTo: view.trailingAnchor) + let bottom = self.bottomAnchor.constraint(equalTo: view.bottomAnchor) + NSLayoutConstraint.activate([top, leading, trailing, bottom]) } } diff --git a/Sources/MenuTransitioningDelegate.swift b/Sources/MenuTransitioningDelegate.swift index 06fb327..1715f07 100644 --- a/Sources/MenuTransitioningDelegate.swift +++ b/Sources/MenuTransitioningDelegate.swift @@ -25,6 +25,12 @@ final class MenuTransitioningDelegate: NSObject { let interactiveTransition: MenuInteractiveTransition + public var shadowOptions = SideMenuItemShadow() { + didSet { + interactiveTransition.shadowOptions = shadowOptions + } + } + init(interactiveTransition: MenuInteractiveTransition) { self.interactiveTransition = interactiveTransition } diff --git a/Sources/Options.swift b/Sources/Options.swift index 98a305e..e77836f 100644 --- a/Sources/Options.swift +++ b/Sources/Options.swift @@ -97,7 +97,7 @@ public struct TransitionOptions { /// Regular view animation options. Default value is `curveEaseInOut`. public var animationOptions: UIViewAnimationOptions = .curveEaseInOut - public init(duration: TimeInterval = 0.5, contentScale: CGFloat = 0.88, visibleContentWidth: CGFloat = 56.0) { + public init(duration: TimeInterval = 0.5, contentScale: CGFloat = 0.86, visibleContentWidth: CGFloat = 56.0) { self.duration = duration self.contentScale = contentScale self.visibleContentWidth = visibleContentWidth diff --git a/Sources/MenuItemContentViewController.swift b/Sources/SideMenuItemContent.swift similarity index 84% rename from Sources/MenuItemContentViewController.swift rename to Sources/SideMenuItemContent.swift index 3a56d80..39bece2 100644 --- a/Sources/MenuItemContentViewController.swift +++ b/Sources/SideMenuItemContent.swift @@ -37,6 +37,9 @@ extension SideMenuItemContent where Self: UIViewController { public func showSideMenu() { if let menuContainerViewController = parent as? MenuContainerViewController { menuContainerViewController.showSideMenu() + } else if let navController = parent as? UINavigationController, + let menuContainerViewController = navController.parent as? MenuContainerViewController { + menuContainerViewController.showSideMenu() } } } diff --git a/Sources/SideMenuItemShadow.swift b/Sources/SideMenuItemShadow.swift new file mode 100644 index 0000000..b0d06fe --- /dev/null +++ b/Sources/SideMenuItemShadow.swift @@ -0,0 +1,35 @@ +// +// SideMenuItemShadow.swift +// InteractiveSideMenu +// +// Created by Eric Miller on 2/8/18. +// Copyright © 2018 Handsome. All rights reserved. +// + +import Foundation + +/** + Defines the parameters for the shadow that is shown under to content controller when the side menu is open. +*/ +public struct SideMenuItemShadow { + + struct Defaults { + static var defaultColor: UIColor = UIColor.black + static var defaultOpacity: CGFloat = 0.3 + static var defaultSize: CGSize = CGSize(width: -5, height: 5) + } + + public var color: UIColor? = Defaults.defaultColor + public var opacity: CGFloat = Defaults.defaultOpacity + public var offset: CGSize = Defaults.defaultSize + + public init() { } + + public init(color: UIColor? = Defaults.defaultColor, + opacity: CGFloat = Defaults.defaultOpacity, + offset: CGSize = Defaults.defaultSize) { + self.color = color + self.opacity = opacity + self.offset = offset + } +}