├── .gitignore
├── Demo
├── NonStoryboard
│ ├── LGSideMenuControllerDemo.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── LGSideMenuControllerDemo.xcscheme
│ └── LGSideMenuControllerDemo
│ │ └── Info.plist
├── Storyboard
│ ├── LGSideMenuControllerDemo.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── LGSideMenuControllerDemo.xcscheme
│ └── LGSideMenuControllerDemo
│ │ ├── AsContainerForNavigationController.storyboard
│ │ ├── AsContainerForTabBarController.storyboard
│ │ ├── Info.plist
│ │ ├── InsideNavigationController.storyboard
│ │ └── InsideTabBarController.storyboard
└── _shared_files
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── imageLeft.imageset
│ │ ├── Contents.json
│ │ └── imageLeft.jpg
│ ├── imageRight.imageset
│ │ ├── Contents.json
│ │ └── imageRight.jpg
│ ├── imageRoot0.imageset
│ │ ├── Contents.json
│ │ └── imageRoot0.jpg
│ ├── imageRoot1.imageset
│ │ ├── Contents.json
│ │ └── imageRoot1.jpg
│ ├── imageRoot2.imageset
│ │ ├── Contents.json
│ │ └── imageRoot2.jpg
│ └── imageRoot3.imageset
│ │ ├── Contents.json
│ │ └── imageRoot3.jpg
│ ├── ChooseViewController
│ ├── Choose.storyboard
│ ├── ChooseNavigationController.swift
│ └── ChooseTableViewController.swift
│ ├── DemoEntries.swift
│ ├── Helper.swift
│ ├── LaunchScreen.storyboard
│ └── SideMenuController
│ ├── LeftViewController
│ ├── LeftViewCell.swift
│ ├── LeftViewCellBackgroundView.swift
│ └── LeftViewController.swift
│ ├── MainViewController.swift
│ ├── RightViewController
│ ├── RightViewCell.swift
│ ├── RightViewCellHeaderView.swift
│ └── RightViewController.swift
│ └── RootViewController
│ ├── RootNavigationController.swift
│ ├── RootTabBarController.swift
│ ├── RootViewController.swift
│ ├── RootViewControllerWithScrollView.swift
│ ├── RootViewControllerWithTableView.swift
│ └── RootViewControllerWithTextLabel.swift
├── Framework
├── LGSideMenuControllerFramework.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── LGSideMenuControllerFramework.xcscheme
└── LGSideMenuControllerFramework
│ ├── Info.plist
│ ├── LGSideMenuControllerFramework.h
│ └── module.modulemap
├── LGSideMenuController.podspec
├── LGSideMenuController
├── Extensions
│ ├── LGSideMenuController+Callbacks.swift
│ ├── LGSideMenuController+GesturesHandler.swift
│ ├── LGSideMenuController+Helpers.swift
│ ├── LGSideMenuController+Layouting.swift
│ ├── LGSideMenuController+LeftViewActions.swift
│ ├── LGSideMenuController+RightViewActions.swift
│ ├── LGSideMenuController+Rotating.swift
│ ├── LGSideMenuController+States.swift
│ ├── LGSideMenuController+StatusBarHandler.swift
│ ├── LGSideMenuController+ViewsRemoving.swift
│ └── Validators
│ │ ├── LGSideMenuController+ValidatingUserInteraction.swift
│ │ ├── LGSideMenuController+ValidatingViewsFrames.swift
│ │ ├── LGSideMenuController+ValidatingViewsHierarchy.swift
│ │ ├── LGSideMenuController+ValidatingViewsInit.swift
│ │ ├── LGSideMenuController+ValidatingViewsStyles.swift
│ │ ├── LGSideMenuController+ValidatingViewsTransforms.swift
│ │ └── LGSideMenuController+ValidatingViewsVisibility.swift
├── LGSideMenuBackgroundDecorationView.swift
├── LGSideMenuBackgroundShadowView.swift
├── LGSideMenuController.swift
├── LGSideMenuDelegate.swift
├── LGSideMenuHelper.swift
├── LGSideMenuSegue.swift
├── LGSideMenuStatusBarBackgroundView.swift
├── LGSideMenuWrapperView.swift
└── UIViewController+LGSideMenuController.swift
├── LICENSE
├── Package.swift
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | build/
3 | .build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 | .swiftpm
20 |
21 | # CocoaPods
22 | Pods/
23 |
24 | # Carthage
25 | Carthage/Build
26 |
27 | # Mercurial
28 | .hgignore
29 |
30 | # MacOS
31 | .DS_Store
32 |
33 | # Other
34 | ._*
35 |
--------------------------------------------------------------------------------
/Demo/NonStoryboard/LGSideMenuControllerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demo/NonStoryboard/LGSideMenuControllerDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Demo/NonStoryboard/LGSideMenuControllerDemo.xcodeproj/xcshareddata/xcschemes/LGSideMenuControllerDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/Demo/NonStoryboard/LGSideMenuControllerDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIcons
10 |
11 | CFBundleIcons~ipad
12 |
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | $(PRODUCT_NAME)
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | 1.0
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | 1
27 | IsStoryboardBased
28 |
29 | LSRequiresIPhoneOS
30 |
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIMainStoryboardFile
34 | Choose
35 | UIRequiredDeviceCapabilities
36 |
37 | armv7
38 |
39 | UIRequiresFullScreen
40 |
41 | UIStatusBarHidden
42 |
43 | UIStatusBarStyle
44 | UIStatusBarStyleDefault
45 | UISupportedInterfaceOrientations
46 |
47 | UIInterfaceOrientationPortrait
48 | UIInterfaceOrientationLandscapeLeft
49 | UIInterfaceOrientationLandscapeRight
50 |
51 | UISupportedInterfaceOrientations~ipad
52 |
53 | UIInterfaceOrientationPortrait
54 | UIInterfaceOrientationLandscapeLeft
55 | UIInterfaceOrientationLandscapeRight
56 | UIInterfaceOrientationPortraitUpsideDown
57 |
58 | UIViewControllerBasedStatusBarAppearance
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/Demo/Storyboard/LGSideMenuControllerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demo/Storyboard/LGSideMenuControllerDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Demo/Storyboard/LGSideMenuControllerDemo.xcodeproj/xcshareddata/xcschemes/LGSideMenuControllerDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/Demo/Storyboard/LGSideMenuControllerDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIcons
10 |
11 | CFBundleIcons~ipad
12 |
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | $(PRODUCT_NAME)
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | 1.0
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | 1
27 | IsStoryboardBased
28 |
29 | LSRequiresIPhoneOS
30 |
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIMainStoryboardFile
34 | Choose
35 | UIRequiredDeviceCapabilities
36 |
37 | armv7
38 |
39 | UIRequiresFullScreen
40 |
41 | UIStatusBarHidden
42 |
43 | UIStatusBarStyle
44 | UIStatusBarStyleDefault
45 | UISupportedInterfaceOrientations
46 |
47 | UIInterfaceOrientationPortrait
48 | UIInterfaceOrientationLandscapeLeft
49 | UIInterfaceOrientationLandscapeRight
50 |
51 | UISupportedInterfaceOrientations~ipad
52 |
53 | UIInterfaceOrientationPortrait
54 | UIInterfaceOrientationLandscapeLeft
55 | UIInterfaceOrientationLandscapeRight
56 | UIInterfaceOrientationPortraitUpsideDown
57 |
58 | UIViewControllerBasedStatusBarAppearance
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/Demo/_shared_files/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | @main
10 | class AppDelegate: UIResponder, UIApplicationDelegate {
11 |
12 | var window: UIWindow?
13 |
14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
15 | return true
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/imageLeft.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "imageLeft.jpg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/imageLeft.imageset/imageLeft.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Friend-LGA/LGSideMenuController/153733fceb6cc8f4c530f03bbc209562279846c7/Demo/_shared_files/Assets.xcassets/imageLeft.imageset/imageLeft.jpg
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/imageRight.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "imageRight.jpg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/imageRight.imageset/imageRight.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Friend-LGA/LGSideMenuController/153733fceb6cc8f4c530f03bbc209562279846c7/Demo/_shared_files/Assets.xcassets/imageRight.imageset/imageRight.jpg
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/imageRoot0.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "imageRoot0.jpg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/imageRoot0.imageset/imageRoot0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Friend-LGA/LGSideMenuController/153733fceb6cc8f4c530f03bbc209562279846c7/Demo/_shared_files/Assets.xcassets/imageRoot0.imageset/imageRoot0.jpg
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/imageRoot1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "imageRoot1.jpg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/imageRoot1.imageset/imageRoot1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Friend-LGA/LGSideMenuController/153733fceb6cc8f4c530f03bbc209562279846c7/Demo/_shared_files/Assets.xcassets/imageRoot1.imageset/imageRoot1.jpg
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/imageRoot2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "imageRoot2.jpg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/imageRoot2.imageset/imageRoot2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Friend-LGA/LGSideMenuController/153733fceb6cc8f4c530f03bbc209562279846c7/Demo/_shared_files/Assets.xcassets/imageRoot2.imageset/imageRoot2.jpg
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/imageRoot3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "imageRoot3.jpg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Demo/_shared_files/Assets.xcassets/imageRoot3.imageset/imageRoot3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Friend-LGA/LGSideMenuController/153733fceb6cc8f4c530f03bbc209562279846c7/Demo/_shared_files/Assets.xcassets/imageRoot3.imageset/imageRoot3.jpg
--------------------------------------------------------------------------------
/Demo/_shared_files/ChooseViewController/Choose.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/Demo/_shared_files/ChooseViewController/ChooseNavigationController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChooseNavigationController.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | class ChooseNavigationController: UINavigationController {
10 |
11 | override var shouldAutorotate : Bool {
12 | return true
13 | }
14 |
15 | override var prefersStatusBarHidden : Bool {
16 | return UIApplication.shared.statusBarOrientation.isLandscape && UIDevice.current.userInterfaceIdiom == .phone
17 | }
18 |
19 | override var preferredStatusBarStyle : UIStatusBarStyle {
20 | return .lightContent
21 | }
22 |
23 | override var preferredStatusBarUpdateAnimation : UIStatusBarAnimation {
24 | return .none
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/Demo/_shared_files/Helper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Helper.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | private let isStoryboardBasedKey = "IsStoryboardBased"
10 |
11 | private let backgroundImageNames: [String] = ["imageRoot0", "imageRoot1", "imageRoot2", "imageRoot3"]
12 | private var currentBackgroundImageNameIndex = 0
13 |
14 | let menuIconImage: UIImage = {
15 | let size = CGSize(width: 24.0, height: 16.0)
16 | let renderer = UIGraphicsImageRenderer(size: size)
17 | return renderer.image { context in
18 | let ctx = context.cgContext
19 | let lineHeight: CGFloat = 2.0
20 |
21 | ctx.setFillColor(UIColor.black.cgColor)
22 | ctx.setStrokeColor(UIColor.black.cgColor)
23 | ctx.setLineWidth(lineHeight)
24 | ctx.setLineCap(.round)
25 |
26 | ctx.beginPath()
27 |
28 | ctx.move(to: CGPoint(x: lineHeight, y: lineHeight / 2.0))
29 | ctx.addLine(to: CGPoint(x: size.width - lineHeight, y: lineHeight / 2.0))
30 |
31 | ctx.move(to: CGPoint(x: lineHeight, y: size.height / 2.0))
32 | ctx.addLine(to: CGPoint(x: size.width - lineHeight, y: size.height / 2.0))
33 |
34 | ctx.move(to: CGPoint(x: lineHeight, y: size.height - lineHeight / 2.0))
35 | ctx.addLine(to: CGPoint(x: size.width - lineHeight, y: size.height - lineHeight / 2.0))
36 |
37 | ctx.strokePath()
38 | }
39 | }()
40 |
41 | func tabBarIconImage(_ title: String) -> UIImage {
42 | let fontSize: CGFloat = 24.0
43 | let insetVertical: CGFloat = 4.0
44 | let size = CGSize(width: fontSize, height: fontSize + insetVertical)
45 | let renderer = UIGraphicsImageRenderer(size: size)
46 | return renderer.image { context in
47 | let paragraphStyle = NSMutableParagraphStyle()
48 | paragraphStyle.alignment = .center
49 | let attrs = [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: fontSize),
50 | NSAttributedString.Key.paragraphStyle: paragraphStyle]
51 | title.draw(with: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height),
52 | options: .usesLineFragmentOrigin,
53 | attributes: attrs,
54 | context: nil)
55 | }
56 | }
57 |
58 | func isStoryboardBasedDemo() -> Bool {
59 | guard let dict = Bundle.main.infoDictionary else { return false }
60 | return dict[isStoryboardBasedKey] as! Bool
61 | }
62 |
63 | func isLightTheme() -> Bool {
64 | if #available(iOS 13.0, *) {
65 | let currentStyle = UITraitCollection.current.userInterfaceStyle
66 | return currentStyle == .light || currentStyle == .unspecified
67 | }
68 | else {
69 | return true
70 | }
71 | }
72 |
73 | func getBackgroundImageNameDefault() -> String {
74 | currentBackgroundImageNameIndex = 0
75 | return backgroundImageNames[currentBackgroundImageNameIndex]
76 | }
77 |
78 | func getBackgroundImageNameRandom() -> String {
79 | var newIndex = currentBackgroundImageNameIndex
80 | while newIndex == currentBackgroundImageNameIndex {
81 | newIndex = Int.random(in: 0...3)
82 | }
83 | currentBackgroundImageNameIndex = newIndex
84 | return backgroundImageNames[currentBackgroundImageNameIndex]
85 | }
86 |
87 | func getKeyWindow() -> UIWindow? {
88 | if #available(iOS 13.0, *) {
89 | return UIApplication.shared.windows.first(where: { $0.isKeyWindow })
90 | } else {
91 | return UIApplication.shared.keyWindow
92 | }
93 | }
94 |
95 | func getStatusBarFrame() -> CGRect {
96 | if #available(iOS 13.0, *) {
97 | return getKeyWindow()?.windowScene?.statusBarManager?.statusBarFrame ?? .zero
98 | } else {
99 | return UIApplication.shared.statusBarFrame
100 | }
101 | }
102 |
103 | func getTitle(for presentationStyle: LGSideMenuController.PresentationStyle) -> String {
104 | switch presentationStyle {
105 | case .scaleFromBig:
106 | return "Scale From Big"
107 | case .scaleFromLittle:
108 | return "Scale From Little"
109 | case .slideBelow:
110 | return "Slide Below"
111 | case .slideBelowShifted:
112 | return "Slide Below Shifted"
113 | case .slideAbove:
114 | return "Slide Above"
115 | case .slideAboveBlurred:
116 | return "Slide Above Blurred"
117 | case .slideAside:
118 | return "Slide Aside"
119 | }
120 | }
121 |
122 | extension UIView {
123 | class func fromNib() -> Self {
124 | return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)!.first as! Self
125 | }
126 | }
127 |
128 | extension UIColor {
129 | convenience init(hexString: String, alpha: CGFloat = 1.0) {
130 | let hexint = Int(Self.intFromHexString(hexStr: hexString))
131 | let red = CGFloat((hexint & 0xff0000) >> 16) / 255.0
132 | let green = CGFloat((hexint & 0xff00) >> 8) / 255.0
133 | let blue = CGFloat((hexint & 0xff) >> 0) / 255.0
134 | self.init(red: red, green: green, blue: blue, alpha: alpha)
135 | }
136 |
137 | static private func intFromHexString(hexStr: String) -> UInt32 {
138 | var hexInt: UInt32 = 0
139 | let scanner: Scanner = Scanner(string: hexStr)
140 | scanner.charactersToBeSkipped = CharacterSet(charactersIn: "#")
141 | scanner.scanHexInt32(&hexInt)
142 | return hexInt
143 | }
144 | }
145 |
146 | enum SideViewCellItem: Equatable {
147 | case close
148 | case openLeft
149 | case openRight
150 | case changeRootVC
151 | case pushVC(title: String)
152 |
153 | var description: String {
154 | switch self {
155 | case .close:
156 | return "Close"
157 | case .openLeft:
158 | return "Open Left View"
159 | case .openRight:
160 | return "Open Right View"
161 | case .changeRootVC:
162 | return "Change Root VC"
163 | case let .pushVC(title):
164 | return title
165 | }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/Demo/_shared_files/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Demo/_shared_files/SideMenuController/LeftViewController/LeftViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LeftViewCell.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | private let textFont = UIFont.boldSystemFont(ofSize: 16.0)
10 | private let fillColorNormal = UIColor(white: 0.0, alpha: 0.2)
11 | private let fillColorNormalInverted = UIColor(white: 1.0, alpha: 0.2)
12 | private let fillColorHighlighted = UIColor(white: 1.0, alpha: 0.6)
13 | private let textColorNormal: UIColor = .white
14 | private let textColorHighlighted: UIColor = .black
15 |
16 | class LeftViewCell: UITableViewCell {
17 |
18 | var isFirst: Bool = false {
19 | didSet {
20 | guard let backgroundView = self.backgroundView as? LeftViewCellBackgroundView else { return }
21 | backgroundView.isFirstCell = isFirst
22 | }
23 | }
24 |
25 | var isLast: Bool = false {
26 | didSet {
27 | guard let backgroundView = self.backgroundView as? LeftViewCellBackgroundView else { return }
28 | backgroundView.isLastCell = isLast
29 | }
30 | }
31 |
32 | var isFillColorInverted: Bool = false
33 |
34 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
35 | super.init(style: style, reuseIdentifier: reuseIdentifier)
36 |
37 | selectionStyle = .none
38 | backgroundColor = .clear
39 |
40 | backgroundView = LeftViewCellBackgroundView()
41 | setBackgroundColor(getFillColorNormal())
42 |
43 | textLabel!.font = textFont
44 | textLabel!.textColor = textColorNormal
45 | textLabel!.numberOfLines = 1
46 | textLabel!.textAlignment = .left
47 | }
48 |
49 | required init?(coder: NSCoder) {
50 | fatalError("init(coder:) has not been implemented")
51 | }
52 |
53 | override func layoutSubviews() {
54 | super.layoutSubviews()
55 |
56 | backgroundView!.frame = CGRect(x: 0.0,
57 | y: 0.0,
58 | width: bounds.width - 16.0,
59 | height: bounds.height)
60 |
61 | textLabel!.frame = CGRect(x: 8.0,
62 | y: 0.0,
63 | width: bounds.width - 16.0 - 16.0,
64 | height: bounds.height)
65 | }
66 |
67 | override func setHighlighted(_ highlighted: Bool, animated: Bool) {
68 | super.setHighlighted(highlighted, animated: animated)
69 | setBackgroundColor(highlighted ? fillColorHighlighted : getFillColorNormal())
70 | textLabel!.textColor = highlighted ? textColorHighlighted : textColorNormal
71 | }
72 |
73 | func getFillColorNormal() -> UIColor {
74 | return isFillColorInverted ? fillColorNormalInverted : fillColorNormal
75 | }
76 |
77 | func setBackgroundColor(_ color: UIColor) {
78 | let backgroundView = self.backgroundView as! LeftViewCellBackgroundView
79 | backgroundView.fillColor = color
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/Demo/_shared_files/SideMenuController/LeftViewController/LeftViewCellBackgroundView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LeftViewCellBackgroundView.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | private func rad(_ deg: CGFloat) -> CGFloat {
10 | return (deg - 90.0) * .pi / 180.0
11 | }
12 |
13 | class LeftViewCellBackgroundView: UIView {
14 |
15 | var isFirstCell: Bool = false {
16 | didSet {
17 | setNeedsDisplay()
18 | }
19 | }
20 |
21 | var isLastCell: Bool = false {
22 | didSet {
23 | setNeedsDisplay()
24 | }
25 | }
26 |
27 | var fillColor: UIColor = .clear {
28 | didSet {
29 | setNeedsDisplay()
30 | }
31 | }
32 |
33 | init() {
34 | super.init(frame: .zero)
35 | backgroundColor = .clear
36 | }
37 |
38 | required init?(coder: NSCoder) {
39 | fatalError("init(coder:) has not been implemented")
40 | }
41 |
42 | override func draw(_ rect: CGRect) {
43 | super.draw(rect)
44 |
45 | guard let context = UIGraphicsGetCurrentContext() else { return }
46 |
47 | context.clear(rect)
48 | context.setFillColor(fillColor.cgColor)
49 |
50 | var drawingRect = getDrawingRect(from: rect)
51 |
52 | do {
53 | drawingRect.origin.y += 1.0
54 | drawingRect.size.height -= 2.0
55 | drawingRect.size.width -= 1.0
56 |
57 | let path = getPath(inside: drawingRect)
58 |
59 | path.close()
60 | context.beginPath()
61 | context.addPath(path.cgPath)
62 | context.fillPath()
63 | }
64 | }
65 |
66 | private func getDrawingRect(from rect: CGRect) -> CGRect {
67 | var result = CGRect(origin: CGPoint(x: 0.0, y: 0.0),
68 | size: CGSize(width: rect.width - 1.0, height: rect.height))
69 | if isFirstCell {
70 | result.origin.y += 1.0
71 | result.size.height -= 1.0
72 | }
73 | if isLastCell {
74 | result.size.height -= 1.0
75 | }
76 | return result
77 | }
78 |
79 | private func getPath(inside rect: CGRect) -> UIBezierPath {
80 | let cornerRadius: CGFloat = 16.0
81 | let path = UIBezierPath()
82 |
83 | path.move(to: CGPoint(x: rect.minX, y: rect.minY))
84 | path.addLine(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.minY))
85 |
86 | if isFirstCell {
87 | path.addArc(withCenter: CGPoint(x: rect.maxX - cornerRadius, y: rect.minY + cornerRadius),
88 | radius: cornerRadius,
89 | startAngle: rad(0.0),
90 | endAngle: rad(90.0),
91 | clockwise: true)
92 | }
93 | else {
94 | path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
95 | }
96 |
97 | path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - cornerRadius))
98 |
99 | if isLastCell {
100 | path.addArc(withCenter: CGPoint(x: rect.maxX - cornerRadius, y: rect.maxY - cornerRadius),
101 | radius: cornerRadius,
102 | startAngle: rad(90.0),
103 | endAngle: rad(180.0),
104 | clockwise: true)
105 | }
106 | else {
107 | path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
108 | }
109 |
110 | path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
111 |
112 | return path
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/Demo/_shared_files/SideMenuController/LeftViewController/LeftViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LeftViewController.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | private let cellIdentifier = "cell"
10 | private let tableViewInset: CGFloat = 44.0 * 2.0
11 | private let cellHeight: CGFloat = 44.0
12 |
13 | class LeftViewController: UITableViewController {
14 |
15 | private var type: DemoType?
16 |
17 | private let sections: [[SideViewCellItem]] = [
18 | [.close, .openRight],
19 | [.changeRootVC],
20 | [.pushVC(title: "Profile"),
21 | .pushVC(title: "News"),
22 | .pushVC(title: "Friends"),
23 | .pushVC(title: "Music"),
24 | .pushVC(title: "Video"),
25 | .pushVC(title: "Articles")]
26 | ]
27 |
28 | // For NonStoryboard Demo
29 | init(type: DemoType) {
30 | self.type = type
31 | super.init(style: .grouped)
32 | }
33 |
34 | required init?(coder: NSCoder) {
35 | super.init(coder: coder)
36 | }
37 |
38 | // For Storyboard Demo
39 | func setup(type: DemoType) {
40 | self.type = type
41 | }
42 |
43 | // MARK: - Lifecycle -
44 |
45 | override func viewDidLoad() {
46 | super.viewDidLoad()
47 | struct Counter { static var count = 0 }
48 | Counter.count += 1
49 | print("LeftViewController.viewDidLoad(), counter: \(Counter.count)")
50 |
51 | // For iOS < 11.0 use this property to avoid artefacts if necessary
52 | // automaticallyAdjustsScrollViewInsets = false
53 |
54 | tableView.register(LeftViewCell.self, forCellReuseIdentifier: cellIdentifier)
55 | tableView.separatorStyle = .none
56 | tableView.contentInset = UIEdgeInsets(top: tableViewInset, left: 0.0, bottom: tableViewInset, right: 0.0)
57 | tableView.showsVerticalScrollIndicator = false
58 | tableView.backgroundColor = .clear
59 | tableView.contentInsetAdjustmentBehavior = .never
60 | tableView.contentOffset = CGPoint(x: 0.0, y: -tableViewInset)
61 | }
62 |
63 | // MARK: - Status Bar -
64 |
65 | override var prefersStatusBarHidden: Bool {
66 | switch type?.demoRow {
67 | case .statusBarHidden,
68 | .statusBarOnlyRoot:
69 | return true
70 | default:
71 | return false
72 | }
73 | }
74 |
75 | override var preferredStatusBarStyle: UIStatusBarStyle {
76 | switch type?.demoRow {
77 | case .statusBarDifferentStyles:
78 | if isLightTheme() {
79 | return .lightContent
80 | } else {
81 | if #available(iOS 13.0, *) {
82 | return .darkContent
83 | } else {
84 | return .default
85 | }
86 | }
87 | default:
88 | return .default
89 | }
90 | }
91 |
92 | override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
93 | return .fade
94 | }
95 |
96 | // MARK: - UITableViewDataSource -
97 |
98 | override func numberOfSections(in tableView: UITableView) -> Int {
99 | return sections.count
100 | }
101 |
102 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
103 | return sections[section].count
104 | }
105 |
106 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
107 | let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! LeftViewCell
108 | let item = sections[indexPath.section][indexPath.row]
109 |
110 | cell.textLabel!.text = item.description
111 | cell.isFirst = (indexPath.row == 0)
112 | cell.isLast = (indexPath.row == sections[indexPath.section].count - 1)
113 | cell.isFillColorInverted = sideMenuController?.leftViewPresentationStyle == .slideAboveBlurred
114 |
115 | return cell
116 | }
117 |
118 | // MARK: - UITableViewDelegate -
119 |
120 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
121 | guard let sideMenuController = sideMenuController else { return }
122 | let item = sections[indexPath.section][indexPath.row]
123 |
124 | func getNavigationController() -> UINavigationController {
125 | if type?.demoRow == .usageInsideNavigationController {
126 | return sideMenuController.parent as! RootNavigationController
127 | }
128 | else if type?.demoRow == .usageAsContainerForTabBarController {
129 | let tabBarController = sideMenuController.rootViewController as! RootTabBarController
130 | return tabBarController.selectedViewController as! RootNavigationController
131 | }
132 | else {
133 | return sideMenuController.rootViewController as! RootNavigationController
134 | }
135 | }
136 |
137 | switch item {
138 | case .close:
139 | sideMenuController.hideLeftView(animated: true)
140 | case .openRight:
141 | sideMenuController.showRightView(animated: true)
142 | case .changeRootVC:
143 | let viewController = RootViewController(imageName: getBackgroundImageNameRandom())
144 | if type?.demoRow == .usageInsideNavigationController {
145 | sideMenuController.rootViewController = viewController
146 | UIView.transition(with: sideMenuController.rootViewWrapperView!,
147 | duration: sideMenuController.leftViewAnimationDuration,
148 | options: [.transitionCrossDissolve],
149 | animations: nil)
150 | }
151 | else {
152 | let navigationController = getNavigationController()
153 | navigationController.setViewControllers([viewController], animated: false)
154 | UIView.transition(with: navigationController.view,
155 | duration: sideMenuController.leftViewAnimationDuration,
156 | options: [.transitionCrossDissolve],
157 | animations: nil)
158 | }
159 | sideMenuController.hideLeftView(animated: true)
160 | case .pushVC(let title):
161 | let viewController = RootViewControllerWithTextLabel(title: title,
162 | imageName: getBackgroundImageNameRandom())
163 | if type?.demoRow == .usageInsideNavigationController {
164 | sideMenuController.rootViewController = viewController
165 | UIView.transition(with: sideMenuController.rootViewWrapperView!,
166 | duration: sideMenuController.leftViewAnimationDuration,
167 | options: [.transitionCrossDissolve],
168 | animations: nil)
169 | }
170 | else {
171 | let navigationController = getNavigationController()
172 | navigationController.pushViewController(viewController, animated: true)
173 | }
174 | sideMenuController.hideLeftView(animated: true)
175 | default:
176 | return
177 | }
178 | }
179 |
180 | override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
181 | return cellHeight
182 | }
183 |
184 | override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
185 | if section == 0 {
186 | return 0.0
187 | }
188 | return cellHeight / 2.0
189 | }
190 |
191 | override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
192 | return 0.0
193 | }
194 |
195 | override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
196 | return nil
197 | }
198 |
199 | override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
200 | return nil
201 | }
202 |
203 | // MARK: - Logging -
204 |
205 | deinit {
206 | struct Counter { static var count = 0 }
207 | Counter.count += 1
208 | print("LeftViewController.deinit(), counter: \(Counter.count)")
209 | }
210 |
211 | override func viewWillAppear(_ animated: Bool) {
212 | super.viewWillAppear(animated)
213 | struct Counter { static var count = 0 }
214 | Counter.count += 1
215 | print("LeftViewController.viewWillAppear(\(animated)), counter: \(Counter.count)")
216 | }
217 |
218 | override func viewDidAppear(_ animated: Bool) {
219 | super.viewDidAppear(animated)
220 | struct Counter { static var count = 0 }
221 | Counter.count += 1
222 | print("LeftViewController.viewDidAppear(\(animated)), counter: \(Counter.count)")
223 | }
224 |
225 | override func viewWillDisappear(_ animated: Bool) {
226 | super.viewWillDisappear(animated)
227 | struct Counter { static var count = 0 }
228 | Counter.count += 1
229 | print("LeftViewController.viewWillDisappear(\(animated)), counter: \(Counter.count)")
230 | }
231 |
232 | override func viewDidDisappear(_ animated: Bool) {
233 | super.viewDidDisappear(animated)
234 | struct Counter { static var count = 0 }
235 | Counter.count += 1
236 | print("LeftViewController.viewDidDisappear(\(animated)), counter: \(Counter.count)")
237 | }
238 |
239 | override func viewWillLayoutSubviews() {
240 | super.viewWillLayoutSubviews()
241 | struct Counter { static var count = 0 }
242 | Counter.count += 1
243 | print("LeftViewController.viewWillLayoutSubviews(), counter: \(Counter.count)")
244 | }
245 |
246 | }
247 |
--------------------------------------------------------------------------------
/Demo/_shared_files/SideMenuController/RightViewController/RightViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RightViewCell.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | private let cornerRadius: CGFloat = 16.0
10 | private let fillColorNormal: UIColor = .clear
11 | private let fillColorHighlighted = UIColor(white: 1.0, alpha: 0.3)
12 | private let textColorNormal: UIColor = .white
13 | private let textColorHighlighted: UIColor = .black
14 |
15 | class RightViewCell: UITableViewCell {
16 |
17 | var separatorView: UIView
18 |
19 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
20 | separatorView = UIView()
21 |
22 | super.init(style: style, reuseIdentifier: reuseIdentifier)
23 |
24 | selectionStyle = .none
25 | backgroundColor = .clear
26 |
27 | backgroundView = UIView()
28 | backgroundView!.backgroundColor = fillColorNormal
29 | backgroundView!.layer.masksToBounds = true
30 | backgroundView!.layer.cornerRadius = cornerRadius
31 |
32 | textLabel!.textAlignment = .center
33 | textLabel!.numberOfLines = 0
34 | textLabel!.lineBreakMode = .byWordWrapping
35 | textLabel!.textColor = textColorNormal
36 |
37 | separatorView.backgroundColor = UIColor.white.withAlphaComponent(0.3)
38 | addSubview(self.separatorView)
39 | }
40 |
41 | required init?(coder: NSCoder) {
42 | fatalError("init(coder:) has not been implemented")
43 | }
44 |
45 | override func layoutSubviews() {
46 | super.layoutSubviews()
47 |
48 | let backgroundOffsetX: CGFloat = 8.0
49 |
50 | backgroundView!.frame = {
51 | let offsetY: CGFloat = 8.0
52 | return CGRect(x: backgroundOffsetX,
53 | y: offsetY,
54 | width: bounds.width - backgroundOffsetX + cornerRadius,
55 | height: bounds.height - (offsetY * 2.0))
56 | }()
57 |
58 | textLabel!.frame = {
59 | let offsetX: CGFloat = 8.0
60 | return CGRect(x: backgroundOffsetX + offsetX,
61 | y: 0.0,
62 | width: bounds.width - backgroundOffsetX - (offsetX * 2.0),
63 | height: bounds.height)
64 | }()
65 |
66 | separatorView.frame = CGRect(x: 16.0,
67 | y: bounds.height - 1.0,
68 | width: bounds.width - 16.0,
69 | height: 1.0)
70 | }
71 |
72 | override func setHighlighted(_ highlighted: Bool, animated: Bool) {
73 | super.setHighlighted(highlighted, animated: animated)
74 | backgroundView!.backgroundColor = highlighted ? fillColorHighlighted : fillColorNormal
75 | textLabel!.textColor = highlighted ? textColorHighlighted : textColorNormal
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/Demo/_shared_files/SideMenuController/RightViewController/RightViewCellHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RightViewCellHeaderView.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | class RightViewCellHeaderView: UIView {
10 |
11 | init() {
12 | super.init(frame: .zero)
13 | backgroundColor = .clear
14 | }
15 |
16 | required init?(coder: NSCoder) {
17 | fatalError("init(coder:) has not been implemented")
18 | }
19 |
20 | override func draw(_ rect: CGRect) {
21 | super.draw(rect)
22 |
23 | guard let context = UIGraphicsGetCurrentContext() else { return }
24 |
25 | context.clear(rect)
26 | context.setFillColor(UIColor(white: 1.0, alpha: 0.3).cgColor)
27 |
28 | let offset: CGFloat = 16.0
29 | let height: CGFloat = 4.0
30 |
31 | context.fill(CGRect(x: offset,
32 | y: rect.height / 2.0 - height / 2.0,
33 | width: rect.width - offset,
34 | height: height))
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/Demo/_shared_files/SideMenuController/RightViewController/RightViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RightViewController.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | private let cellIdentifier = "cell"
10 | private let tableViewInset: CGFloat = 44.0 * 2.0
11 | private let cellHeight: CGFloat = 100.0
12 |
13 | class RightViewController: UITableViewController {
14 |
15 | private var type: DemoType?
16 |
17 | private let sections: [[SideViewCellItem]] = [
18 | [.close, .openLeft],
19 | [.changeRootVC],
20 | [.pushVC(title: "1"),
21 | .pushVC(title: "2"),
22 | .pushVC(title: "3"),
23 | .pushVC(title: "4"),
24 | .pushVC(title: "5"),
25 | .pushVC(title: "6"),
26 | .pushVC(title: "7"),
27 | .pushVC(title: "8"),
28 | .pushVC(title: "9"),
29 | .pushVC(title: "10")]
30 | ]
31 |
32 | init(type: DemoType) {
33 | self.type = type
34 | super.init(style: .grouped)
35 | }
36 |
37 | required init?(coder: NSCoder) {
38 | super.init(coder: coder)
39 | }
40 |
41 | // For Storyboard Demo
42 | func setup(type: DemoType) {
43 | self.type = type
44 | }
45 |
46 | // MARK: - Lifecycle -
47 |
48 | override func viewDidLoad() {
49 | super.viewDidLoad()
50 | struct Counter { static var count = 0 }
51 | Counter.count += 1
52 | print("RightViewController.viewDidLoad(), counter: \(Counter.count)")
53 |
54 | // For iOS < 11.0 use this property to avoid artefacts if necessary
55 | // automaticallyAdjustsScrollViewInsets = false
56 |
57 | tableView.register(RightViewCell.self, forCellReuseIdentifier: cellIdentifier)
58 | tableView.separatorStyle = .none
59 | tableView.contentInset = UIEdgeInsets(top: tableViewInset, left: 0.0, bottom: tableViewInset, right: 0.0)
60 | tableView.showsVerticalScrollIndicator = false
61 | tableView.backgroundColor = .clear
62 | tableView.contentInsetAdjustmentBehavior = .never
63 | tableView.contentOffset = CGPoint(x: 0.0, y: -tableViewInset)
64 | }
65 |
66 | // MARK: - Status Bar -
67 |
68 | override var prefersStatusBarHidden: Bool {
69 | switch type?.demoRow {
70 | case .statusBarHidden,
71 | .statusBarOnlyRoot:
72 | return true
73 | default:
74 | return false
75 | }
76 | }
77 |
78 | override var preferredStatusBarStyle: UIStatusBarStyle {
79 | guard let type = type else { return .default }
80 |
81 | switch type.demoRow {
82 | case .statusBarDifferentStyles:
83 | if isLightTheme() {
84 | return .lightContent
85 | } else {
86 | if #available(iOS 13.0, *) {
87 | return .darkContent
88 | } else {
89 | return .default
90 | }
91 | }
92 | case .statusBarNoBackground:
93 | if type.presentationStyle.shouldRootViewScale {
94 | return .lightContent
95 | }
96 | fallthrough
97 | default:
98 | return .default
99 | }
100 | }
101 |
102 | override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
103 | return .slide
104 | }
105 |
106 | // MARK: - UITableViewDataSource -
107 |
108 | override func numberOfSections(in tableView: UITableView) -> Int {
109 | return sections.count
110 | }
111 |
112 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
113 | return sections[section].count
114 | }
115 |
116 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
117 | let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! RightViewCell
118 | let item = sections[indexPath.section][indexPath.row]
119 |
120 | cell.textLabel!.text = item.description
121 | cell.textLabel!.font = UIFont.boldSystemFont(ofSize: {
122 | if case .pushVC = item {
123 | return 32.0
124 | }
125 | return 16.0
126 | }())
127 | cell.separatorView.isHidden = (indexPath.row == sections[indexPath.section].count - 1)
128 |
129 | return cell
130 | }
131 |
132 | // MARK: - UITableViewDelegate -
133 |
134 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
135 | guard let sideMenuController = self.sideMenuController else { return }
136 | let item = sections[indexPath.section][indexPath.row]
137 |
138 | func getNavigationController() -> UINavigationController {
139 | if type?.demoRow == .usageInsideNavigationController {
140 | return sideMenuController.parent as! RootNavigationController
141 | }
142 | else if type?.demoRow == .usageAsContainerForTabBarController {
143 | let tabBarController = sideMenuController.rootViewController as! RootTabBarController
144 | return tabBarController.selectedViewController as! RootNavigationController
145 | }
146 | else {
147 | return sideMenuController.rootViewController as! RootNavigationController
148 | }
149 | }
150 |
151 | switch item {
152 | case .close:
153 | sideMenuController.hideRightView(animated: true)
154 | case .openLeft:
155 | sideMenuController.showLeftView(animated: true)
156 | case .changeRootVC:
157 | let viewController = RootViewController(imageName: getBackgroundImageNameRandom())
158 | if type?.demoRow == .usageInsideNavigationController {
159 | sideMenuController.rootViewController = viewController
160 | UIView.transition(with: sideMenuController.rootViewWrapperView!,
161 | duration: sideMenuController.rightViewAnimationDuration,
162 | options: [.transitionCrossDissolve],
163 | animations: nil)
164 | }
165 | else {
166 | let navigationController = getNavigationController()
167 | navigationController.setViewControllers([viewController], animated: false)
168 | UIView.transition(with: navigationController.view,
169 | duration: sideMenuController.rightViewAnimationDuration,
170 | options: [.transitionCrossDissolve],
171 | animations: nil)
172 | }
173 | sideMenuController.hideRightView(animated: true)
174 | case .pushVC(let title):
175 | let viewController = RootViewControllerWithTextLabel(title: title,
176 | imageName: getBackgroundImageNameRandom())
177 | if type?.demoRow == .usageInsideNavigationController {
178 | sideMenuController.rootViewController = viewController
179 | UIView.transition(with: sideMenuController.rootViewWrapperView!,
180 | duration: sideMenuController.rightViewAnimationDuration,
181 | options: [.transitionCrossDissolve],
182 | animations: nil)
183 | }
184 | else {
185 | let navigationController = getNavigationController()
186 | navigationController.pushViewController(viewController, animated: true)
187 | }
188 | sideMenuController.hideRightView(animated: true)
189 | default:
190 | return
191 | }
192 | }
193 |
194 | override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
195 | let item = sections[indexPath.section][indexPath.row]
196 | if case .pushVC = item {
197 | return cellHeight
198 | }
199 | return cellHeight * 0.75
200 | }
201 |
202 | override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
203 | if section == 0 {
204 | return 0.0
205 | }
206 | return cellHeight * 0.75
207 | }
208 |
209 | override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
210 | return 0.0
211 | }
212 |
213 | override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
214 | if section == 0 {
215 | return nil
216 | }
217 | return RightViewCellHeaderView()
218 | }
219 |
220 | override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
221 | return nil
222 | }
223 |
224 | // MARK: - Logging -
225 |
226 | deinit {
227 | struct Counter { static var count = 0 }
228 | Counter.count += 1
229 | print("RightViewController.deinit(), counter: \(Counter.count)")
230 | }
231 |
232 | override func viewWillAppear(_ animated: Bool) {
233 | super.viewWillAppear(animated)
234 | struct Counter { static var count = 0 }
235 | Counter.count += 1
236 | print("RightViewController.viewWillAppear(\(animated)), counter: \(Counter.count)")
237 | }
238 |
239 | override func viewDidAppear(_ animated: Bool) {
240 | super.viewDidAppear(animated)
241 | struct Counter { static var count = 0 }
242 | Counter.count += 1
243 | print("RightViewController.viewDidAppear(\(animated)), counter: \(Counter.count)")
244 | }
245 |
246 | override func viewWillDisappear(_ animated: Bool) {
247 | super.viewWillDisappear(animated)
248 | struct Counter { static var count = 0 }
249 | Counter.count += 1
250 | print("RightViewController.viewWillDisappear(\(animated)), counter: \(Counter.count)")
251 | }
252 |
253 | override func viewDidDisappear(_ animated: Bool) {
254 | super.viewDidDisappear(animated)
255 | struct Counter { static var count = 0 }
256 | Counter.count += 1
257 | print("RightViewController.viewDidDisappear(\(animated)), counter: \(Counter.count)")
258 | }
259 |
260 | override func viewWillLayoutSubviews() {
261 | super.viewWillLayoutSubviews()
262 | struct Counter { static var count = 0 }
263 | Counter.count += 1
264 | print("RightViewController.viewWillLayoutSubviews(), counter: \(Counter.count)")
265 | }
266 |
267 | }
268 |
--------------------------------------------------------------------------------
/Demo/_shared_files/SideMenuController/RootViewController/RootNavigationController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootNavigationController.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | class RootNavigationController: UINavigationController {
10 |
11 | private var type: DemoType?
12 |
13 | init(type: DemoType) {
14 | super.init(nibName: nil, bundle: nil)
15 | setup(type: type)
16 | }
17 |
18 | required init?(coder aDecoder: NSCoder) {
19 | super.init(coder: aDecoder)
20 | }
21 |
22 | // For Storyboard Demo
23 | func setup(type: DemoType) {
24 | self.type = type
25 | }
26 |
27 | // MARK: - Lifecycle -
28 |
29 | override func viewDidLoad() {
30 | super.viewDidLoad()
31 | struct Counter { static var count = 0 }
32 | Counter.count += 1
33 | print("RootNavigationController.viewDidLoad(), counter: \(Counter.count)")
34 |
35 | setColors()
36 | }
37 |
38 | // MARK: - Rotation -
39 |
40 | override var shouldAutorotate : Bool {
41 | return true
42 | }
43 |
44 | // MARK: - Status Bar -
45 |
46 | override var prefersStatusBarHidden : Bool {
47 | switch type?.demoRow {
48 | case .statusBarHidden,
49 | .statusBarOnlySide:
50 | return true
51 | default:
52 | return false
53 | }
54 | }
55 |
56 | override var preferredStatusBarStyle : UIStatusBarStyle {
57 | return .default
58 | }
59 |
60 | override var preferredStatusBarUpdateAnimation : UIStatusBarAnimation {
61 | return .fade
62 | }
63 |
64 | // MARK: - Theme -
65 |
66 | override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
67 | super.traitCollectionDidChange(previousTraitCollection)
68 | setColors()
69 | }
70 |
71 | // MARK: - Other -
72 |
73 | func setColors() {
74 | navigationBar.barTintColor = UIColor(white: (isLightTheme() ? 1.0 : 0.0), alpha: 0.9)
75 | navigationBar.titleTextAttributes = [.foregroundColor: (isLightTheme() ? UIColor.black : UIColor.white)]
76 | }
77 |
78 | // MARK: - Logging -
79 |
80 | deinit {
81 | struct Counter { static var count = 0 }
82 | Counter.count += 1
83 | print("RootNavigationController.deinit(), counter: \(Counter.count)")
84 | }
85 |
86 | override func viewWillAppear(_ animated: Bool) {
87 | super.viewWillAppear(animated)
88 | struct Counter { static var count = 0 }
89 | Counter.count += 1
90 | print("RootNavigationController.viewWillAppear(\(animated)), counter: \(Counter.count)")
91 | }
92 |
93 | override func viewDidAppear(_ animated: Bool) {
94 | super.viewDidAppear(animated)
95 | struct Counter { static var count = 0 }
96 | Counter.count += 1
97 | print("RootNavigationController.viewDidAppear(\(animated)), counter: \(Counter.count)")
98 | }
99 |
100 | override func viewWillDisappear(_ animated: Bool) {
101 | super.viewWillDisappear(animated)
102 | struct Counter { static var count = 0 }
103 | Counter.count += 1
104 | print("RootNavigationController.viewWillDisappear(\(animated)), counter: \(Counter.count)")
105 | }
106 |
107 | override func viewDidDisappear(_ animated: Bool) {
108 | super.viewDidDisappear(animated)
109 | struct Counter { static var count = 0 }
110 | Counter.count += 1
111 | print("RootNavigationController.viewDidDisappear(\(animated)), counter: \(Counter.count)")
112 | }
113 |
114 | override func viewWillLayoutSubviews() {
115 | super.viewWillLayoutSubviews()
116 | struct Counter { static var count = 0 }
117 | Counter.count += 1
118 | print("RootNavigationController.viewWillLayoutSubviews(), counter: \(Counter.count)")
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/Demo/_shared_files/SideMenuController/RootViewController/RootTabBarController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootTabBarController.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | class RootTabBarController: UITabBarController {
10 |
11 | private var type: DemoType?
12 |
13 | init(type: DemoType) {
14 | super.init(nibName: nil, bundle: nil)
15 | setup(type: type)
16 | }
17 |
18 | required init?(coder aDecoder: NSCoder) {
19 | super.init(coder: aDecoder)
20 | }
21 |
22 | // For Storyboard Demo
23 | func setup(type: DemoType) {
24 | self.type = type
25 | }
26 |
27 | // MARK: - Lifecycle -
28 |
29 | override func viewDidLoad() {
30 | super.viewDidLoad()
31 | struct Counter { static var count = 0 }
32 | Counter.count += 1
33 | print("RootNavigationController.viewDidLoad(), counter: \(Counter.count)")
34 |
35 | // UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.font: UIFont.systemFont(ofSize: tabBar.bounds.height)], for: .normal)
36 | // UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.font: UIFont.systemFont(ofSize: tabBar.bounds.height)], for: .selected)
37 |
38 | setColors()
39 | }
40 |
41 | // override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
42 | // super.willTransition(to: newCollection, with: coordinator)
43 | //
44 | // UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.font: UIFont.systemFont(ofSize: tabBar.bounds.height)], for: .normal)
45 | // UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.font: UIFont.systemFont(ofSize: tabBar.bounds.height)], for: .selected)
46 | // }
47 | //
48 | // override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
49 | // super.viewWillTransition(to: size, with: coordinator)
50 | //
51 | // UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.font: UIFont.systemFont(ofSize: tabBar.bounds.height)], for: .normal)
52 | // UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.font: UIFont.systemFont(ofSize: tabBar.bounds.height)], for: .selected)
53 | // }
54 |
55 | // MARK: - Rotation -
56 |
57 | override var shouldAutorotate : Bool {
58 | return true
59 | }
60 |
61 | // MARK: - Status Bar -
62 |
63 | override var prefersStatusBarHidden : Bool {
64 | return false
65 | }
66 |
67 | override var preferredStatusBarStyle : UIStatusBarStyle {
68 | return .default
69 | }
70 |
71 | override var preferredStatusBarUpdateAnimation : UIStatusBarAnimation {
72 | return .fade
73 | }
74 |
75 | // MARK: - Theme -
76 |
77 | override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
78 | super.traitCollectionDidChange(previousTraitCollection)
79 | setColors()
80 | }
81 |
82 | // MARK: - Other -
83 |
84 | func setColors() {
85 | // navigationBar.barTintColor = UIColor(white: (isLightTheme() ? 1.0 : 0.0), alpha: 0.9)
86 | // navigationBar.titleTextAttributes = [.foregroundColor: (isLightTheme() ? UIColor.black : UIColor.white)]
87 | }
88 |
89 | // MARK: - Logging -
90 |
91 | deinit {
92 | struct Counter { static var count = 0 }
93 | Counter.count += 1
94 | print("RootNavigationController.deinit(), counter: \(Counter.count)")
95 | }
96 |
97 | override func viewWillAppear(_ animated: Bool) {
98 | super.viewWillAppear(animated)
99 | struct Counter { static var count = 0 }
100 | Counter.count += 1
101 | print("RootNavigationController.viewWillAppear(\(animated)), counter: \(Counter.count)")
102 | }
103 |
104 | override func viewDidAppear(_ animated: Bool) {
105 | super.viewDidAppear(animated)
106 | struct Counter { static var count = 0 }
107 | Counter.count += 1
108 | print("RootNavigationController.viewDidAppear(\(animated)), counter: \(Counter.count)")
109 | }
110 |
111 | override func viewWillDisappear(_ animated: Bool) {
112 | super.viewWillDisappear(animated)
113 | struct Counter { static var count = 0 }
114 | Counter.count += 1
115 | print("RootNavigationController.viewWillDisappear(\(animated)), counter: \(Counter.count)")
116 | }
117 |
118 | override func viewDidDisappear(_ animated: Bool) {
119 | super.viewDidDisappear(animated)
120 | struct Counter { static var count = 0 }
121 | Counter.count += 1
122 | print("RootNavigationController.viewDidDisappear(\(animated)), counter: \(Counter.count)")
123 | }
124 |
125 | override func viewWillLayoutSubviews() {
126 | super.viewWillLayoutSubviews()
127 | struct Counter { static var count = 0 }
128 | Counter.count += 1
129 | print("RootNavigationController.viewWillLayoutSubviews(), counter: \(Counter.count)")
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/Demo/_shared_files/SideMenuController/RootViewController/RootViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootViewController.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | private let titleDefault = "LGSideMenuController"
10 | private let showSideButtonsDefault = true
11 | private let buttonHeight: CGFloat = 44.0
12 |
13 | class RootViewController : UIViewController {
14 |
15 | var demoDescriptionLabel: UILabel? = nil
16 | var demoDescriptionBackgroundView: UIVisualEffectView? = nil
17 |
18 | var showSideButtons: Bool = showSideButtonsDefault
19 |
20 | var imageView = UIImageView()
21 |
22 | let button = UIButton()
23 | let buttonBackground = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
24 |
25 | init(title: String = titleDefault,
26 | description: String? = nil,
27 | showSideButtons: Bool = showSideButtonsDefault,
28 | imageName: String? = nil) {
29 | super.init(nibName: nil, bundle: nil)
30 |
31 | self.title = title
32 |
33 | if let description = description, !description.isEmpty {
34 | demoDescriptionLabel = UILabel()
35 | demoDescriptionLabel!.text = description
36 | demoDescriptionBackgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
37 | }
38 |
39 | self.showSideButtons = showSideButtons
40 |
41 | imageView.image = UIImage(named: imageName ?? getBackgroundImageNameDefault())
42 | }
43 |
44 | required init?(coder: NSCoder) {
45 | super.init(coder: coder)
46 | }
47 |
48 | // MARK: - Lifecycle -
49 |
50 | override func viewDidLoad() {
51 | super.viewDidLoad()
52 | struct Counter { static var count = 0 }
53 | Counter.count += 1
54 | print("RootViewController.viewDidLoad(), counter: \(Counter.count)")
55 |
56 | view.clipsToBounds = true
57 |
58 | if showSideButtons {
59 | navigationItem.leftBarButtonItem = UIBarButtonItem(image: menuIconImage, style: .plain, target: self, action: #selector(showLeftView(sender:)))
60 | navigationItem.rightBarButtonItem = UIBarButtonItem(image: menuIconImage, style: .plain, target: self, action: #selector(showRightView(sender:)))
61 | }
62 |
63 | imageView.contentMode = .scaleAspectFill
64 | view.addSubview(imageView)
65 |
66 | if let label = demoDescriptionLabel,
67 | let backgroundView = demoDescriptionBackgroundView {
68 | view.addSubview(backgroundView)
69 |
70 | label.font = UIFont.boldSystemFont(ofSize: 12.0)
71 | label.textAlignment = .left
72 | label.numberOfLines = 0
73 | label.lineBreakMode = .byWordWrapping
74 | label.backgroundColor = .clear
75 | backgroundView.contentView.addSubview(label)
76 | }
77 |
78 | view.addSubview(buttonBackground)
79 |
80 | button.setTitle("Show Choose Controller", for: .normal)
81 | button.setTitleColor(UIColor(red: 0.0, green: 0.5, blue: 1.0, alpha: 1.0), for: .highlighted)
82 | button.addTarget(self, action: #selector(showChooseController), for: .touchUpInside)
83 | button.backgroundColor = .clear
84 | buttonBackground.contentView.addSubview(button)
85 |
86 | setColors()
87 | }
88 |
89 | override func viewWillAppear(_ animated: Bool) {
90 | super.viewWillAppear(animated)
91 | struct Counter { static var count = 0 }
92 | Counter.count += 1
93 | print("RootViewController.viewWillAppear(\(animated)), counter: \(Counter.count)")
94 |
95 | if self.sideMenuController?.isLeftViewSwipeGestureEnabled ?? false {
96 | // If you want left view be available by swipe you need to disable navigation controller pop gesture
97 | navigationController!.interactivePopGestureRecognizer!.isEnabled = false
98 | }
99 | }
100 |
101 | override func viewWillLayoutSubviews() {
102 | super.viewWillLayoutSubviews()
103 | struct Counter { static var count = 0 }
104 | Counter.count += 1
105 | print("RootViewController.viewWillLayoutSubviews(), counter: \(Counter.count)")
106 |
107 | let safeAreaLayoutFrame = view.safeAreaLayoutGuide.layoutFrame
108 |
109 | imageView.frame = CGRect(x: 0.0, y: 0.0, width: view.frame.size.width, height: view.frame.size.height)
110 |
111 | if let label = demoDescriptionLabel,
112 | let backgroundView = demoDescriptionBackgroundView {
113 | let labelOffset: CGFloat = 8.0
114 |
115 | let labelSize = label.sizeThatFits(CGSize(width: view.bounds.width - (labelOffset * 2.0),
116 | height: CGFloat.greatestFiniteMagnitude))
117 |
118 | backgroundView.frame = CGRect(x: 0.0,
119 | y: 0.0,
120 | width: view.bounds.width,
121 | height: labelSize.height + (labelOffset * 2.0) + safeAreaLayoutFrame.minY)
122 |
123 | label.frame = {
124 | let labelOriginY = backgroundView.bounds.height - labelSize.height - labelOffset * 2.0
125 | return CGRect(x: labelOffset,
126 | y: labelOriginY,
127 | width: backgroundView.bounds.width - labelOffset * 2.0,
128 | height: backgroundView.bounds.height - labelOriginY)
129 | }()
130 | }
131 |
132 | buttonBackground.frame = {
133 | let bottomOffset = view.bounds.height - (safeAreaLayoutFrame.minY + safeAreaLayoutFrame.height)
134 | let height = buttonHeight + bottomOffset
135 | return CGRect(x: 0.0,
136 | y: view.frame.size.height - height,
137 | width: view.frame.size.width,
138 | height: height)
139 | }()
140 |
141 | button.frame = CGRect(x: 0.0, y: 0.0, width: buttonBackground.bounds.width, height: buttonHeight)
142 | }
143 |
144 | // MARK: - Theme -
145 |
146 | override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
147 | super.traitCollectionDidChange(previousTraitCollection)
148 | setColors()
149 | }
150 |
151 | // MARK: - Other -
152 |
153 | @objc
154 | func showLeftView(sender: AnyObject?) {
155 | sideMenuController?.toggleLeftView(animated: true)
156 | }
157 |
158 | @objc
159 | func showRightView(sender: AnyObject?) {
160 | sideMenuController?.toggleRightView(animated: true)
161 | }
162 |
163 | @objc
164 | func showChooseController() {
165 | let storyboard = UIStoryboard(name: "Choose", bundle: nil)
166 | let controller = storyboard.instantiateInitialViewController() as! ChooseNavigationController
167 | let window = UIApplication.shared.delegate!.window!!
168 |
169 | window.rootViewController = controller
170 |
171 | UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve, animations: nil, completion: nil)
172 | }
173 |
174 | func setColors() {
175 | button.setTitleColor(isLightTheme() ? .black : .white, for: .normal)
176 | }
177 |
178 | // MARK: - Logging -
179 |
180 | deinit {
181 | struct Counter { static var count = 0 }
182 | Counter.count += 1
183 | print("RootViewController.deinit(), counter: \(Counter.count)")
184 | }
185 |
186 | override func viewDidAppear(_ animated: Bool) {
187 | super.viewDidAppear(animated)
188 | struct Counter { static var count = 0 }
189 | Counter.count += 1
190 | print("RootViewController.viewDidAppear(\(animated)), counter: \(Counter.count)")
191 | }
192 |
193 | override func viewWillDisappear(_ animated: Bool) {
194 | super.viewWillDisappear(animated)
195 | struct Counter { static var count = 0 }
196 | Counter.count += 1
197 | print("RootViewController.viewWillDisappear(\(animated)), counter: \(Counter.count)")
198 | }
199 |
200 | override func viewDidDisappear(_ animated: Bool) {
201 | super.viewDidDisappear(animated)
202 | struct Counter { static var count = 0 }
203 | Counter.count += 1
204 | print("RootViewController.viewDidDisappear(\(animated)), counter: \(Counter.count)")
205 | }
206 |
207 | }
208 |
--------------------------------------------------------------------------------
/Demo/_shared_files/SideMenuController/RootViewController/RootViewControllerWithScrollView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootViewControllerWithScrollView.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | class RootViewControllerWithScrollView: RootViewController {
10 |
11 | private let scrollView = UIScrollView()
12 |
13 | // MARK: - Lifecycle -
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 |
18 | // For iOS < 11.0 use this property to avoid artefacts if necessary
19 | // automaticallyAdjustsScrollViewInsets = false
20 |
21 | scrollView.contentInsetAdjustmentBehavior = .never
22 | view.insertSubview(scrollView, at: 0)
23 |
24 | imageView.clipsToBounds = true
25 | imageView.removeFromSuperview()
26 | scrollView.addSubview(imageView)
27 | }
28 |
29 | override func viewDidAppear(_ animated: Bool) {
30 | super.viewDidAppear(animated)
31 | scrollView.contentOffset = CGPoint(x: (view.bounds.width * 5.0 / 2.0) - (view.bounds.width / 2.0),
32 | y: (view.bounds.height * 3.0 / 2.0) - (view.bounds.height / 2.0))
33 | }
34 |
35 | override func viewWillLayoutSubviews() {
36 | super.viewWillLayoutSubviews()
37 |
38 | scrollView.frame = view.bounds
39 | scrollView.contentInset = .zero
40 | scrollView.scrollIndicatorInsets = {
41 | let topInset = demoDescriptionBackgroundView != nil ?
42 | demoDescriptionBackgroundView!.frame.maxY - view.safeAreaLayoutGuide.layoutFrame.minY : 0.0
43 | return UIEdgeInsets(top: topInset, left: 0.0, bottom: buttonBackground.bounds.height, right: 0.0)
44 | }()
45 |
46 | imageView.frame = CGRect(origin: .zero,
47 | size: CGSize(width: scrollView.frame.width * 5.0,
48 | height: scrollView.frame.height * 3.0))
49 | scrollView.contentSize = imageView.frame.size
50 | }
51 |
52 | // MARK: - Other -
53 |
54 | override func setColors() {
55 | super.setColors()
56 | scrollView.backgroundColor = .gray
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/Demo/_shared_files/SideMenuController/RootViewController/RootViewControllerWithTableView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootViewControllerWithTableView.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | private let cellIdentifier = "cell"
10 |
11 | class RootViewControllerWithTableView: RootViewController, UITableViewDelegate, UITableViewDataSource {
12 |
13 | private let tableView = UITableView()
14 | private var rowNumbers = Array(1...100)
15 |
16 | private let tableViewBackgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
17 |
18 | // MARK: - Lifecycle -
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 |
23 | // For iOS < 11.0 use this property to avoid artefacts if necessary
24 | // automaticallyAdjustsScrollViewInsets = false
25 |
26 | view.insertSubview(tableViewBackgroundView, aboveSubview: imageView)
27 |
28 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
29 | tableView.delegate = self
30 | tableView.dataSource = self
31 | tableView.contentInsetAdjustmentBehavior = .never
32 | tableView.backgroundColor = .clear
33 | view.insertSubview(tableView, aboveSubview: tableViewBackgroundView)
34 | }
35 |
36 | override func viewDidAppear(_ animated: Bool) {
37 | super.viewDidAppear(animated)
38 | tableView.contentOffset = {
39 | let safeAreaLayoutFrame = view.safeAreaLayoutGuide.layoutFrame
40 | let topOffset = demoDescriptionBackgroundView?.frame.maxY ?? safeAreaLayoutFrame.minY
41 | return CGPoint(x: 0.0, y: -topOffset)
42 | }()
43 | }
44 |
45 | override func viewWillLayoutSubviews() {
46 | super.viewWillLayoutSubviews()
47 |
48 | tableViewBackgroundView.frame = view.bounds
49 |
50 | let safeAreaLayoutFrame = view.safeAreaLayoutGuide.layoutFrame
51 |
52 | tableView.frame = view.bounds
53 | tableView.contentInset = {
54 | let topInset = demoDescriptionBackgroundView?.frame.maxY ?? safeAreaLayoutFrame.minY
55 | return UIEdgeInsets(top: topInset, left: 0.0, bottom: buttonBackground.bounds.height, right: 0.0)
56 | }()
57 | tableView.scrollIndicatorInsets = {
58 | let topInset = demoDescriptionBackgroundView != nil ?
59 | demoDescriptionBackgroundView!.frame.maxY - safeAreaLayoutFrame.minY : 0.0
60 | return UIEdgeInsets(top: topInset, left: 0.0, bottom: buttonBackground.bounds.height, right: 0.0)
61 | }()
62 | }
63 |
64 | // MARK: - UITableViewDataSource -
65 |
66 | func numberOfSections(in tableView: UITableView) -> Int {
67 | return 1
68 | }
69 |
70 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
71 | return rowNumbers.count
72 | }
73 |
74 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
75 | let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
76 | let rowNumber = rowNumbers[indexPath.row]
77 |
78 | cell.textLabel?.text = "Row number \(rowNumber)"
79 | cell.textLabel?.font = UIFont.boldSystemFont(ofSize: 16.0)
80 | cell.textLabel?.textColor = isLightTheme() ? .black : .white
81 | cell.backgroundColor = .clear
82 |
83 | return cell
84 | }
85 |
86 | // MARK: - UITableViewDelegate -
87 |
88 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
89 | return 44.0
90 | }
91 |
92 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
93 | tableView.deselectRow(at: indexPath, animated: true)
94 | }
95 |
96 | func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
97 | if (editingStyle == .delete) {
98 | rowNumbers.remove(at: indexPath.row)
99 | tableView.deleteRows(at: [indexPath], with: .automatic)
100 | }
101 | }
102 |
103 | // MARK: - Other -
104 |
105 | override func setColors() {
106 | super.setColors()
107 | tableView.reloadData()
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/Demo/_shared_files/SideMenuController/RootViewController/RootViewControllerWithTextLabel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootViewControllerWithTextLabel.swift
3 | // LGSideMenuControllerDemo
4 | //
5 |
6 | import Foundation
7 | import UIKit
8 |
9 | class RootViewControllerWithTextLabel : RootViewController {
10 |
11 | private let textLabel = UILabel()
12 | private let textLabelBackground = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
13 |
14 | init(title: String, imageName: String? = nil) {
15 | super.init(title: title, showSideButtons: false, imageName: imageName)
16 | }
17 |
18 | required init?(coder: NSCoder) {
19 | fatalError("init(coder:) has not been implemented")
20 | }
21 |
22 | // MARK: - Lifecycle -
23 |
24 | override func viewDidLoad() {
25 | super.viewDidLoad()
26 | struct Counter { static var count = 0 }
27 | Counter.count += 1
28 | print("RootViewController.viewDidLoad(), counter: \(Counter.count)")
29 |
30 | textLabelBackground.layer.masksToBounds = true
31 | view.addSubview(textLabelBackground)
32 |
33 | textLabel.text = title
34 | textLabel.font = UIFont.boldSystemFont(ofSize: 64.0)
35 | textLabel.textAlignment = .center
36 | textLabel.numberOfLines = 1
37 | textLabel.lineBreakMode = .byWordWrapping
38 | textLabel.backgroundColor = .clear
39 | textLabel.adjustsFontSizeToFitWidth = true
40 | textLabel.baselineAdjustment = .alignCenters
41 | view.addSubview(textLabel)
42 | }
43 |
44 | override func viewWillLayoutSubviews() {
45 | super.viewWillLayoutSubviews()
46 | struct Counter { static var count = 0 }
47 | Counter.count += 1
48 | print("RootViewController.viewWillLayoutSubviews(), counter: \(Counter.count)")
49 |
50 | let textLabelOffset: CGFloat = 16.0
51 | textLabel.frame = {
52 | let safeAreaLayoutFrame = view.safeAreaLayoutGuide.layoutFrame
53 | let areaOffsetY = demoDescriptionBackgroundView?.frame.maxY ?? safeAreaLayoutFrame.minY
54 | let areaHeight = view.bounds.height - areaOffsetY - buttonBackground.bounds.height
55 |
56 | let maxWidth = view.bounds.width - (textLabelOffset * 4.0)
57 | var size = textLabel.sizeThatFits(CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude))
58 | size.width = max(size.width, size.height)
59 | size.width = min(size.width, maxWidth)
60 | return CGRect(x: view.bounds.width / 2.0 - size.width / 2.0,
61 | y: areaOffsetY + areaHeight / 2.0 - size.height / 2.0,
62 | width: size.width,
63 | height: size.height)
64 | }()
65 |
66 | if textLabel.bounds.width == textLabel.bounds.height {
67 | textLabelBackground.frame = textLabel.frame.insetBy(dx: -textLabelOffset, dy: -textLabelOffset)
68 | textLabelBackground.layer.cornerRadius = textLabelBackground.bounds.height / 2.0
69 | }
70 | else {
71 | textLabelBackground.frame = textLabel.frame.insetBy(dx: -textLabelOffset * 2.0, dy: -textLabelOffset)
72 | textLabelBackground.layer.cornerRadius = 32.0
73 | }
74 | }
75 |
76 | // MARK: - Other -
77 |
78 | override func setColors() {
79 | super.setColors()
80 | textLabel.textColor = isLightTheme() ? .black : .white
81 | }
82 |
83 | // MARK: - Logging -
84 |
85 | deinit {
86 | struct Counter { static var count = 0 }
87 | Counter.count += 1
88 | print("RootViewController.deinit(), counter: \(Counter.count)")
89 | }
90 |
91 | override func viewWillAppear(_ animated: Bool) {
92 | super.viewWillAppear(animated)
93 | struct Counter { static var count = 0 }
94 | Counter.count += 1
95 | print("RootViewController.viewWillAppear(\(animated)), counter: \(Counter.count)")
96 | }
97 |
98 | override func viewDidAppear(_ animated: Bool) {
99 | super.viewDidAppear(animated)
100 | struct Counter { static var count = 0 }
101 | Counter.count += 1
102 | print("RootViewController.viewDidAppear(\(animated)), counter: \(Counter.count)")
103 | }
104 |
105 | override func viewWillDisappear(_ animated: Bool) {
106 | super.viewWillDisappear(animated)
107 | struct Counter { static var count = 0 }
108 | Counter.count += 1
109 | print("RootViewController.viewWillDisappear(\(animated)), counter: \(Counter.count)")
110 | }
111 |
112 | override func viewDidDisappear(_ animated: Bool) {
113 | super.viewDidDisappear(animated)
114 | struct Counter { static var count = 0 }
115 | Counter.count += 1
116 | print("RootViewController.viewDidDisappear(\(animated)), counter: \(Counter.count)")
117 | }
118 |
119 | }
120 |
121 |
--------------------------------------------------------------------------------
/Framework/LGSideMenuControllerFramework.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Framework/LGSideMenuControllerFramework.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Framework/LGSideMenuControllerFramework.xcodeproj/xcshareddata/xcschemes/LGSideMenuControllerFramework.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
52 |
53 |
59 |
60 |
66 |
67 |
68 |
69 |
71 |
72 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/Framework/LGSideMenuControllerFramework/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Framework/LGSideMenuControllerFramework/LGSideMenuControllerFramework.h:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuControllerFramework.h
3 | // LGSideMenuControllerFramework
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | #import
31 |
32 | //! Project version number for LGSideMenuController framework.
33 | FOUNDATION_EXPORT double LGSideMenuControllerVersionNumber;
34 |
35 | //! Project version string for LGSideMenuController framework.
36 | FOUNDATION_EXPORT const unsigned char LGSideMenuControllerVersionString[];
37 |
--------------------------------------------------------------------------------
/Framework/LGSideMenuControllerFramework/module.modulemap:
--------------------------------------------------------------------------------
1 | framework module LGSideMenuController {
2 | umbrella header "LGSideMenuControllerFramework.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/LGSideMenuController.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = 'LGSideMenuController'
4 | s.version = '3.1.2'
5 | s.license = { type: 'MIT', file: 'LICENSE' }
6 | s.homepage = 'https://github.com/Friend-LGA/LGSideMenuController'
7 | s.author = { 'Grigorii Lutkov': 'friend.lga@gmail.com' }
8 | s.source = { git: 'https://github.com/Friend-LGA/LGSideMenuController.git', tag: s.version }
9 | s.summary = 'iOS view controller which manages left and right side views'
10 | s.platform = :ios, '9.0'
11 | s.swift_version = '5.0'
12 | s.source_files = 'LGSideMenuController/**/*.swift'
13 | s.framework = 'Foundation', 'CoreGraphics', 'QuartzCore', 'UIKit'
14 |
15 | end
16 |
--------------------------------------------------------------------------------
/LGSideMenuController/Extensions/LGSideMenuController+Callbacks.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuController+Callbacks.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | internal extension LGSideMenuController {
34 |
35 | // MARK: - Root View -
36 |
37 | func didTransformRootViewCallbacks(percentage: CGFloat) {
38 | NotificationCenter.default.post(name: Notification.didTransformRootView,
39 | object: self,
40 | userInfo: [Notification.Key.percentage: percentage])
41 |
42 | if let callback = self.didTransformRootView {
43 | callback(self, percentage)
44 | }
45 |
46 | if let delegate = self.delegate {
47 | delegate.didTransformRootView(sideMenuController: self, percentage: percentage)
48 | }
49 | }
50 |
51 | // MARK: - Left View -
52 |
53 | func willShowLeftViewCallbacks() {
54 | NotificationCenter.default.post(name: Notification.willShowLeftView, object: self)
55 |
56 | if let willShowLeftView = self.willShowLeftView {
57 | willShowLeftView(self)
58 | }
59 |
60 | if let delegate = self.delegate {
61 | delegate.willShowLeftView(sideMenuController: self)
62 | }
63 | }
64 |
65 | func didShowLeftViewCallbacks() {
66 | NotificationCenter.default.post(name: Notification.didShowLeftView, object: self)
67 |
68 | if let didShowLeftView = self.didShowLeftView {
69 | didShowLeftView(self)
70 | }
71 |
72 | if let delegate = self.delegate {
73 | delegate.didShowLeftView(sideMenuController: self)
74 | }
75 | }
76 |
77 | func willHideLeftViewCallbacks() {
78 | NotificationCenter.default.post(name: Notification.willHideLeftView, object: self)
79 |
80 | if let willHideLeftView = self.willHideLeftView {
81 | willHideLeftView(self)
82 | }
83 |
84 | if let delegate = self.delegate {
85 | delegate.willHideLeftView(sideMenuController: self)
86 | }
87 | }
88 |
89 | func didHideLeftViewCallbacks() {
90 | NotificationCenter.default.post(name: Notification.didHideLeftView, object: self)
91 |
92 | if let didHideLeftView = self.didHideLeftView {
93 | didHideLeftView(self)
94 | }
95 |
96 | if let delegate = self.delegate {
97 | delegate.didHideLeftView(sideMenuController: self)
98 | }
99 | }
100 |
101 | func showAnimationsForLeftViewCallbacks(duration: TimeInterval, timingFunction: CAMediaTimingFunction) {
102 | NotificationCenter.default.post(name: Notification.showAnimationsForLeftView,
103 | object: self,
104 | userInfo: [Notification.Key.duration: duration,
105 | Notification.Key.timigFunction: timingFunction])
106 |
107 | if let callback = self.showAnimationsForLeftView {
108 | callback(self, duration, timingFunction)
109 | }
110 |
111 | if let delegate = self.delegate {
112 | delegate.showAnimationsForLeftView(sideMenuController: self,
113 | duration: duration,
114 | timingFunction: timingFunction)
115 | }
116 | }
117 |
118 | func hideAnimationsForLeftViewCallbacks(duration: TimeInterval, timingFunction: CAMediaTimingFunction) {
119 | NotificationCenter.default.post(name: Notification.hideAnimationsForLeftView,
120 | object: self,
121 | userInfo: [Notification.Key.duration: duration,
122 | Notification.Key.timigFunction: timingFunction])
123 |
124 | if let callback = self.hideAnimationsForLeftView {
125 | callback(self, duration, timingFunction)
126 | }
127 |
128 | if let delegate = self.delegate {
129 | delegate.hideAnimationsForLeftView(sideMenuController: self,
130 | duration: duration,
131 | timingFunction: timingFunction)
132 | }
133 | }
134 |
135 | func didTransformLeftViewCallbacks(percentage: CGFloat) {
136 | NotificationCenter.default.post(name: Notification.didTransformLeftView,
137 | object: self,
138 | userInfo: [Notification.Key.percentage: percentage])
139 |
140 | if let callback = self.didTransformLeftView {
141 | callback(self, percentage)
142 | }
143 |
144 | if let delegate = self.delegate {
145 | delegate.didTransformLeftView(sideMenuController: self, percentage: percentage)
146 | }
147 | }
148 |
149 | // MARK: - Right View -
150 |
151 | func willShowRightViewCallbacks() {
152 | NotificationCenter.default.post(name: Notification.willShowRightView, object: self)
153 |
154 | if let willShowRightView = self.willShowRightView {
155 | willShowRightView(self)
156 | }
157 |
158 | if let delegate = self.delegate {
159 | delegate.willShowRightView(sideMenuController: self)
160 | }
161 | }
162 |
163 | func didShowRightViewCallbacks() {
164 | NotificationCenter.default.post(name: Notification.didShowRightView, object: self)
165 |
166 | if let didShowRightView = self.didShowRightView {
167 | didShowRightView(self)
168 | }
169 |
170 | if let delegate = self.delegate {
171 | delegate.didShowRightView(sideMenuController: self)
172 | }
173 | }
174 |
175 | func willHideRightViewCallbacks() {
176 | NotificationCenter.default.post(name: Notification.willHideRightView, object: self)
177 |
178 | if let willHideRightView = self.willHideRightView {
179 | willHideRightView(self)
180 | }
181 |
182 | if let delegate = self.delegate {
183 | delegate.willHideRightView(sideMenuController: self)
184 | }
185 | }
186 |
187 | func didHideRightViewCallbacks() {
188 | NotificationCenter.default.post(name: Notification.didHideRightView, object: self)
189 |
190 | if let didHideRightView = self.didHideRightView {
191 | didHideRightView(self)
192 | }
193 |
194 | if let delegate = self.delegate {
195 | delegate.didHideRightView(sideMenuController: self)
196 | }
197 | }
198 |
199 | func showAnimationsForRightViewCallbacks(duration: TimeInterval, timingFunction: CAMediaTimingFunction) {
200 | NotificationCenter.default.post(name: Notification.showAnimationsForRightView,
201 | object: self,
202 | userInfo: [Notification.Key.duration: duration,
203 | Notification.Key.timigFunction: timingFunction])
204 |
205 | if let callback = self.showAnimationsForRightView {
206 | callback(self, duration, timingFunction)
207 | }
208 |
209 | if let delegate = self.delegate {
210 | delegate.showAnimationsForRightView(sideMenuController: self,
211 | duration: duration,
212 | timingFunction: timingFunction)
213 | }
214 | }
215 |
216 | func hideAnimationsForRightViewCallbacks(duration: TimeInterval, timingFunction: CAMediaTimingFunction) {
217 | NotificationCenter.default.post(name: Notification.hideAnimationsForRightView,
218 | object: self,
219 | userInfo: [Notification.Key.duration: duration,
220 | Notification.Key.timigFunction: timingFunction])
221 |
222 | if let callback = self.hideAnimationsForRightView {
223 | callback(self, duration, timingFunction)
224 | }
225 |
226 | if let delegate = self.delegate {
227 | delegate.hideAnimationsForRightView(sideMenuController: self,
228 | duration: duration,
229 | timingFunction: timingFunction)
230 | }
231 | }
232 |
233 | func didTransformRightViewCallbacks(percentage: CGFloat) {
234 | NotificationCenter.default.post(name: Notification.didTransformRightView,
235 | object: self,
236 | userInfo: [Notification.Key.percentage: percentage])
237 |
238 | if let callback = self.didTransformRightView {
239 | callback(self, percentage)
240 | }
241 |
242 | if let delegate = self.delegate {
243 | delegate.didTransformRightView(sideMenuController: self, percentage: percentage)
244 | }
245 | }
246 |
247 | }
248 |
--------------------------------------------------------------------------------
/LGSideMenuController/Extensions/LGSideMenuController+Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuController+Helpers.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | internal extension LGSideMenuController {
34 |
35 | func disableRootViewLayouting() {
36 | guard self.isRootViewLayoutingEnabled == true,
37 | let wrapperView = self.rootViewWrapperView else { return }
38 |
39 | wrapperView.canLayoutSubviews = false
40 | self.isRootViewLayoutingEnabled = false
41 | }
42 |
43 | func enableRootViewLayouting() {
44 | guard self.isRootViewLayoutingEnabled == false,
45 | let wrapperView = self.rootViewWrapperView else { return }
46 |
47 | wrapperView.canLayoutSubviews = true
48 | self.isRootViewLayoutingEnabled = true
49 | }
50 |
51 | func disableRootViewControllerLayouting() {
52 | guard self.isRootViewControllerLayoutingEnabled == true,
53 | let viewController = self.rootViewController else { return }
54 |
55 | viewController.willMove(toParent: nil)
56 | viewController.removeFromParent()
57 | self.isRootViewControllerLayoutingEnabled = false
58 | }
59 |
60 | func enableRootViewControllerLayouting() {
61 | guard self.isRootViewControllerLayoutingEnabled == false,
62 | let viewController = self.rootViewController else { return }
63 |
64 | self.addChild(viewController)
65 | viewController.didMove(toParent: self)
66 | self.isRootViewControllerLayoutingEnabled = true
67 | }
68 |
69 | var leftViewWidthTotal: CGFloat {
70 | self.leftViewWidth + self.leftViewLayerBorderWidth
71 | }
72 |
73 | var rightViewWidthTotal: CGFloat {
74 | self.rightViewWidth + self.rightViewLayerBorderWidth
75 | }
76 |
77 | var rootViewOffsetTotalForLeftView: CGPoint {
78 | var result = self.rootViewOffsetWhenHiddenForLeftView
79 | if self.leftViewPresentationStyle.shouldRootViewMove {
80 | result.x += self.leftViewWidthTotal + self.rootViewLayerBorderWidthForLeftView
81 | }
82 | return result
83 | }
84 |
85 | var rootViewOffsetTotalForLeftViewWhenAlwaysVisible: CGFloat {
86 | return
87 | self.rootViewOffsetWhenHiddenForLeftView.x +
88 | self.leftViewWidthTotal +
89 | self.rootViewLayerBorderWidthForLeftView
90 | }
91 |
92 | var rootViewOffsetTotalForRightView: CGPoint {
93 | var result = CGPoint(x: -self.rootViewOffsetWhenHiddenForRightView.x,
94 | y: self.rootViewOffsetWhenHiddenForRightView.y)
95 | if self.rightViewPresentationStyle.shouldRootViewMove {
96 | result.x += self.rightViewWidthTotal + self.rootViewLayerBorderWidthForRightView
97 | }
98 | return result
99 | }
100 |
101 | var rootViewOffsetTotalForRightViewWhenAlwaysVisible: CGFloat {
102 | return
103 | -self.rootViewOffsetWhenHiddenForRightView.x +
104 | self.rightViewWidthTotal +
105 | self.rootViewLayerBorderWidthForRightView
106 | }
107 |
108 | // MARK: - Cancel Animations -
109 |
110 | func cancelRootViewAnimations() {
111 | guard let containerView = self.rootContainerView,
112 | let wrapperView = self.rootViewWrapperView,
113 | let coverView = self.rootViewCoverView else { return }
114 |
115 | CATransaction.begin()
116 | containerView.layer.removeAllAnimations()
117 | wrapperView.layer.removeAllAnimations()
118 | coverView.layer.removeAllAnimations()
119 | CATransaction.commit()
120 | }
121 |
122 | func cancelLeftViewAnimations() {
123 | guard let containerView = self.leftContainerView,
124 | let backgroundDecorationView = self.leftViewBackgroundDecorationView,
125 | let wrapperView = self.leftViewWrapperView,
126 | let coverView = self.leftViewCoverView else { return }
127 |
128 | CATransaction.begin()
129 | containerView.layer.removeAllAnimations()
130 | backgroundDecorationView.layer.removeAllAnimations()
131 | wrapperView.layer.removeAllAnimations()
132 | coverView.layer.removeAllAnimations()
133 | CATransaction.commit()
134 | }
135 |
136 | func cancelRightViewAnimations() {
137 | guard let containerView = self.rightContainerView,
138 | let backgroundDecorationView = self.rightViewBackgroundDecorationView,
139 | let wrapperView = self.rightViewWrapperView,
140 | let coverView = self.rightViewCoverView else { return }
141 |
142 | CATransaction.begin()
143 | containerView.layer.removeAllAnimations()
144 | backgroundDecorationView.layer.removeAllAnimations()
145 | wrapperView.layer.removeAllAnimations()
146 | coverView.layer.removeAllAnimations()
147 | CATransaction.commit()
148 | }
149 |
150 | // MARK: - Status Bar -
151 |
152 | var isViewLocatedUnderStatusBar: Bool {
153 | guard let keyWindow = LGSideMenuHelper.getKeyWindow() else { return false }
154 | let statusBarOrigin = keyWindow.convert(LGSideMenuHelper.getStatusBarFrame().origin, to: self.view)
155 | return statusBarOrigin == .zero
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/LGSideMenuController/Extensions/LGSideMenuController+Layouting.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuController+Layouting.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | extension LGSideMenuController {
34 |
35 | open override func viewWillLayoutSubviews() {
36 | super.viewWillLayoutSubviews()
37 | let size = self.view.bounds.size
38 | if self.isNeedsUpdateLayoutsAndStyles || self.savedSize != size {
39 | self.savedSize = size
40 | self.updateLayoutsAndStyles()
41 | }
42 | else if self.isNeedsUpdateRootViewLayoutsAndStyles {
43 | self.updateRootViewLayoutsAndStyles()
44 | }
45 | else if self.isNeedsUpdateLeftViewLayoutsAndStyles {
46 | self.updateLeftViewLayoutsAndStyles()
47 | }
48 | else if self.isNeedsUpdateRightViewLayoutsAndStyles {
49 | self.updateRightViewLayoutsAndStyles()
50 | }
51 | }
52 |
53 | /// Invalidates the current layout and triggers a layout update during the next update cycle.
54 | open func setNeedsUpdateLayoutsAndStyles() {
55 | self.isNeedsUpdateLayoutsAndStyles = true
56 | if self.isViewLoaded {
57 | self.view.setNeedsLayout()
58 | }
59 | }
60 |
61 | /// Invalidates the current layout and triggers a layout update during the next update cycle.
62 | open func setNeedsUpdateRootViewLayoutsAndStyles() {
63 | self.isNeedsUpdateRootViewLayoutsAndStyles = true
64 | if self.isViewLoaded {
65 | self.view.setNeedsLayout()
66 | }
67 | }
68 |
69 | /// Invalidates the current layout and triggers a layout update during the next update cycle.
70 | open func setNeedsUpdateLeftViewLayoutsAndStyles() {
71 | self.isNeedsUpdateLeftViewLayoutsAndStyles = true
72 | if self.isViewLoaded {
73 | self.view.setNeedsLayout()
74 | }
75 | }
76 |
77 | /// Invalidates the current layout and triggers a layout update during the next update cycle.
78 | open func setNeedsUpdateRightViewLayoutsAndStyles() {
79 | self.isNeedsUpdateRightViewLayoutsAndStyles = true
80 | if self.isViewLoaded {
81 | self.view.setNeedsLayout()
82 | }
83 | }
84 |
85 | /// Forces update layouts and styles for all views
86 | open func updateLayoutsAndStyles() {
87 | self.isNeedsUpdateLayoutsAndStyles = false
88 | self.isNeedsUpdateRootViewLayoutsAndStyles = false
89 | self.isNeedsUpdateLeftViewLayoutsAndStyles = false
90 | self.isNeedsUpdateRightViewLayoutsAndStyles = false
91 |
92 | self.validateViewsInit()
93 | self.validateViewsHierarchy()
94 | self.validateViewsFrames()
95 | self.validateViewsStyles()
96 | self.validateViewsTransforms()
97 | self.validateViewsVisibility()
98 | }
99 |
100 | /// Forces update layouts and styles for root views
101 | open func updateRootViewLayoutsAndStyles() {
102 | self.isNeedsUpdateRootViewLayoutsAndStyles = false
103 |
104 | self.validateRootViewsInit()
105 | self.validateViewsHierarchy() // Whole hierarchy should be validated
106 | self.validateRootViewsFrames()
107 | self.validateRootViewsStyles()
108 | self.validateRootViewsTransforms()
109 | self.validateRootViewsVisibility()
110 | }
111 |
112 | /// Forces update layouts and styles for left views
113 | open func updateLeftViewLayoutsAndStyles() {
114 | self.isNeedsUpdateLeftViewLayoutsAndStyles = false
115 |
116 | self.validateLeftViewsInit()
117 | self.validateViewsHierarchy() // Whole hierarchy should be validated
118 | self.validateLeftViewsFrames()
119 | self.validateLeftViewsStyles()
120 | self.validateLeftViewsTransforms()
121 | self.validateLeftViewsVisibility()
122 | }
123 |
124 | /// Forces update layouts and styles for right views
125 | open func updateRightViewLayoutsAndStyles() {
126 | self.isNeedsUpdateRightViewLayoutsAndStyles = false
127 |
128 | self.validateRightViewsInit()
129 | self.validateViewsHierarchy() // Whole hierarchy should be validated
130 | self.validateRightViewsFrames()
131 | self.validateRightViewsStyles()
132 | self.validateRightViewsTransforms()
133 | self.validateRightViewsVisibility()
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/LGSideMenuController/Extensions/LGSideMenuController+Rotating.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuController+Rotating.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | extension LGSideMenuController {
34 |
35 | open override var shouldAutorotate: Bool {
36 | if let rootViewController = self.rootViewController {
37 | return rootViewController.shouldAutorotate
38 | }
39 | return super.shouldAutorotate
40 | }
41 |
42 | open override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
43 | super.willTransition(to: newCollection, with: coordinator)
44 |
45 | if !self.isRootViewShowing {
46 | self.isRotationInvalidatedLayout = true
47 | }
48 | }
49 |
50 | open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
51 | super.viewWillTransition(to: size, with: coordinator)
52 |
53 | // We need to do this, because current orientation is already changed to a new one
54 | guard let previousOrientation = LGSideMenuHelper.getOppositeInterfaceOrientation() else { return }
55 |
56 | self.cancelRootViewAnimations()
57 | self.cancelLeftViewAnimations()
58 | self.cancelRightViewAnimations()
59 |
60 | if (self.leftView != nil && self.isLeftViewAlwaysVisible(orientation: previousOrientation)) ||
61 | (self.rightView != nil && self.isRightViewAlwaysVisible(orientation: previousOrientation)) {
62 | self.shouldUpdateVisibility = false
63 | }
64 |
65 | if self.state == .leftViewWillShow {
66 | if self.isLeftViewAlwaysVisible {
67 | self.showLeftViewDone()
68 | }
69 | }
70 | else if self.state == .leftViewWillHide {
71 | self.hideLeftViewDone(updateStatusBar: true)
72 | }
73 |
74 | if self.state == .rightViewWillShow {
75 | if self.isRightViewAlwaysVisible {
76 | self.showRightViewDone()
77 | }
78 | }
79 | else if self.state == .rightViewWillHide {
80 | self.hideRightViewDone(updateStatusBar: true)
81 | }
82 |
83 | coordinator.animate(alongsideTransition: { [weak self] (context: UIViewControllerTransitionCoordinatorContext) in
84 | guard let self = self else { return }
85 |
86 | if self.isLeftViewAlwaysVisible && !self.isLeftViewHidden {
87 | self.hideLeftViewPrepare()
88 | self.hideLeftViewActions(animated: true, duration: context.transitionDuration)
89 | }
90 |
91 | if self.isRightViewAlwaysVisible && !self.isRightViewHidden {
92 | self.hideRightViewPrepare()
93 | self.hideRightViewActions(animated: true, duration: context.transitionDuration)
94 | }
95 | }, completion: { [weak self] (context: UIViewControllerTransitionCoordinatorContext) in
96 | guard let self = self else { return }
97 |
98 | if !self.shouldUpdateVisibility {
99 | self.shouldUpdateVisibility = true
100 | self.validateLeftViewsVisibility()
101 | self.validateRightViewsVisibility()
102 | }
103 |
104 | self.validateViewsUserInteraction()
105 | })
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/LGSideMenuController/Extensions/LGSideMenuController+States.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuController+States.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | extension LGSideMenuController {
34 |
35 | /// Is root view fully opened
36 | open var isRootViewShowing: Bool {
37 | return self.state == .rootViewIsShowing
38 | }
39 |
40 | /// Is left view fully opened
41 | open var isLeftViewShowing: Bool {
42 | return self.state == .leftViewIsShowing
43 | }
44 |
45 | /// Is right view fully opened
46 | open var isRightViewShowing: Bool {
47 | return self.state == .rightViewIsShowing
48 | }
49 |
50 | /// Is either left or right view fully opened
51 | open var isSideViewShowing: Bool {
52 | return self.isLeftViewShowing || self.isRightViewShowing
53 | }
54 |
55 | /// Is left view showing or will show or will hide right now
56 | open var isLeftViewVisible: Bool {
57 | return self.state.isLeftViewVisible
58 | }
59 |
60 | /// Is right view showing or will show or will hide right now
61 | open var isRightViewVisible: Bool {
62 | return self.state.isRightViewVisible
63 | }
64 |
65 | /// Is either left or right view showing or will show or will hide right now
66 | open var isSideViewVisible: Bool {
67 | return self.isLeftViewVisible || self.isRightViewVisible
68 | }
69 |
70 | /// Is left view fully closed
71 | open var isRootViewHidden: Bool {
72 | return self.state.isRootViewHidden
73 | }
74 |
75 | /// Is left view fully closed
76 | open var isLeftViewHidden: Bool {
77 | return self.state.isLeftViewHidden
78 | }
79 |
80 | /// Is right view fully closed
81 | open var isRightViewHidden: Bool {
82 | return self.state.isRightViewHidden
83 | }
84 |
85 | /// Is either left or right view fully closed
86 | open var isSideViewHidden: Bool {
87 | return self.isLeftViewHidden || self.isRightViewHidden
88 | }
89 |
90 | /// Is left view currently will show or hide
91 | open var isLeftViewVisibilityChanging: Bool {
92 | return self.state == .leftViewWillShow || self.state == .leftViewWillHide
93 | }
94 |
95 | /// Is right view currently will show or hide
96 | open var isRightViewVisibilityChanging: Bool {
97 | return self.state == .rightViewWillShow || self.state == .rightViewWillHide
98 | }
99 |
100 | /// Is either left or right view currently will show or hide
101 | open var isSideViewVisibilityChanging: Bool {
102 | return self.isLeftViewVisibilityChanging || self.isRightViewVisibilityChanging
103 | }
104 |
105 | /// Is left view currently fully open or close
106 | open var isLeftViewVisibilityStable: Bool {
107 | return self.state != .leftViewWillShow && self.state != .leftViewWillHide
108 | }
109 |
110 | /// Is right view currently fully open or close
111 | open var isRightViewVisibilityStable: Bool {
112 | return self.state != .rightViewWillShow && self.state != .rightViewWillHide
113 | }
114 |
115 | /// Is either left or right view currently fully open or close
116 | open var isSideViewVisibilityStable: Bool {
117 | return self.isLeftViewVisibilityStable || self.isRightViewVisibilityStable
118 | }
119 |
120 | /// Is left view suppose to be "always visible" for current orientation
121 | open var isLeftViewAlwaysVisible: Bool {
122 | return self.leftViewAlwaysVisibleOptions.isVisible(sizeClass: self.traitCollection.horizontalSizeClass)
123 | }
124 |
125 | /// Is right view suppose to be "always visible" for current orientation
126 | open var isRightViewAlwaysVisible: Bool {
127 | return self.rightViewAlwaysVisibleOptions.isVisible(sizeClass: self.traitCollection.horizontalSizeClass)
128 | }
129 |
130 | /// Is left view suppose to be "always visible" for specified orientation
131 | open func isLeftViewAlwaysVisible(orientation: UIInterfaceOrientation) -> Bool {
132 | return self.leftViewAlwaysVisibleOptions.isVisible(orientation: orientation,
133 | sizeClass: self.traitCollection.horizontalSizeClass)
134 | }
135 |
136 | /// Is right view suppose to be "always visible" for specified orientation
137 | open func isRightViewAlwaysVisible(orientation: UIInterfaceOrientation) -> Bool {
138 | return self.rightViewAlwaysVisibleOptions.isVisible(orientation: orientation,
139 | sizeClass: self.traitCollection.horizontalSizeClass)
140 | }
141 |
142 | /// Is any of side views suppose to be "always visible" for current orientation
143 | open var isSideViewAlwaysVisible: Bool {
144 | return self.isLeftViewAlwaysVisible || self.isRightViewAlwaysVisible
145 | }
146 |
147 | /// Is left view showing or always showing or will show or will hide right now
148 | open var isLeftViewVisibleToUser: Bool {
149 | return self.isLeftViewVisible || self.isLeftViewAlwaysVisible
150 | }
151 |
152 | /// Is right view showing or always showing or will show or will hide right now
153 | open var isRightViewVisibleToUser: Bool {
154 | return self.isRightViewVisible || self.isRightViewAlwaysVisible
155 | }
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/LGSideMenuController/Extensions/LGSideMenuController+StatusBarHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuController+StatusBarHandler.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | extension LGSideMenuController {
34 |
35 | open override var prefersStatusBarHidden: Bool {
36 | if self.rootView != nil && (self.state == .rootViewIsShowing || self.state == .leftViewWillHide || self.state == .rightViewWillHide) {
37 | return self.isRootViewStatusBarHidden
38 | }
39 | else if self.leftView != nil && self.isLeftViewVisible && !self.isLeftViewAlwaysVisible {
40 | return self.isLeftViewStatusBarHidden
41 | }
42 | else if self.rightView != nil && self.isRightViewVisible && !self.isRightViewAlwaysVisible {
43 | return self.isRightViewStatusBarHidden
44 | }
45 |
46 | return super.prefersStatusBarHidden
47 | }
48 |
49 | open override var preferredStatusBarStyle: UIStatusBarStyle {
50 | if self.rootView != nil && (self.state == .rootViewIsShowing || self.state == .leftViewWillHide || self.state == .rightViewWillHide) {
51 | return self.rootViewStatusBarStyle
52 | }
53 | else if self.leftView != nil && self.isLeftViewVisible && !self.isLeftViewAlwaysVisible {
54 | return self.leftViewStatusBarStyle
55 | }
56 | else if self.rightView != nil && self.isRightViewVisible && !self.isRightViewAlwaysVisible {
57 | return self.rightViewStatusBarStyle
58 | }
59 |
60 | return super.preferredStatusBarStyle
61 | }
62 |
63 | open override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
64 | if self.rootView != nil && self.state == .rootViewIsShowing {
65 | return self.rootViewStatusBarUpdateAnimation
66 | }
67 | else if self.leftView != nil && self.isLeftViewVisible && !self.isLeftViewAlwaysVisible {
68 | return self.leftViewStatusBarUpdateAnimation
69 | }
70 | else if self.rightView != nil && self.isRightViewVisible && !self.isRightViewAlwaysVisible {
71 | return self.rightViewStatusBarUpdateAnimation
72 | }
73 |
74 | return super.preferredStatusBarUpdateAnimation
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/LGSideMenuController/Extensions/LGSideMenuController+ViewsRemoving.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuController+ViewsRemoving.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | internal extension LGSideMenuController {
34 |
35 | // Note:
36 | // We don't nullify any of rootView, rootViewController, leftView, leftViewController, rightView, rightViewController
37 | // because we don't manage their lifecycle from here, they should be assigned from the outside.
38 |
39 | func removeViewController(_ viewController: UIViewController?) {
40 | guard let viewController = viewController else { return }
41 | LGSideMenuHelper.setSideMenuController(nil, to: viewController)
42 | viewController.willMove(toParent: nil)
43 | viewController.removeFromParent()
44 | }
45 |
46 | // MARK: - Root View -
47 |
48 | func removeRootViews() {
49 | if let containerView = self.rootContainerView {
50 | containerView.removeFromSuperview()
51 | self.rootContainerView = nil
52 | }
53 |
54 | if let containerClipToBorderView = self.rootContainerClipToBorderView {
55 | containerClipToBorderView.removeFromSuperview()
56 | self.rootContainerClipToBorderView = nil
57 | }
58 |
59 | if let backgroundDecorationView = self.rootViewBackgroundDecorationView {
60 | backgroundDecorationView.removeFromSuperview()
61 | self.rootViewBackgroundDecorationView = nil
62 | }
63 |
64 | if let backgroundShadowView = self.rootViewBackgroundShadowView {
65 | backgroundShadowView.removeFromSuperview()
66 | self.rootViewBackgroundShadowView = nil
67 | }
68 |
69 | if let wrapperView = self.rootViewWrapperView {
70 | wrapperView.removeFromSuperview()
71 | self.rootViewWrapperView = nil
72 | }
73 |
74 | if let coverView = self.rootViewCoverView {
75 | coverView.removeFromSuperview()
76 | self.rootViewCoverView = nil
77 | }
78 | }
79 |
80 | // MARK: - Left View -
81 |
82 | func removeLeftViews() {
83 | if let containerView = self.leftContainerView {
84 | containerView.removeFromSuperview()
85 | self.leftContainerView = nil
86 | }
87 |
88 | if let containerClipToBorderView = self.leftContainerClipToBorderView {
89 | containerClipToBorderView.removeFromSuperview()
90 | self.leftContainerClipToBorderView = nil
91 | }
92 |
93 | if let backgroundDecorationView = self.leftViewBackgroundDecorationView {
94 | backgroundDecorationView.removeFromSuperview()
95 | self.leftViewBackgroundDecorationView = nil
96 | }
97 |
98 | if let backgroundShadowView = self.leftViewBackgroundShadowView {
99 | backgroundShadowView.removeFromSuperview()
100 | self.leftViewBackgroundShadowView = nil
101 | }
102 |
103 | if let backgroundImageView = self.leftViewBackgroundImageView {
104 | backgroundImageView.removeFromSuperview()
105 | self.leftViewBackgroundImageView = nil
106 | }
107 |
108 | if let backgroundView = self.leftViewBackgroundView {
109 | backgroundView.removeFromSuperview()
110 | // We don't nullify it because it is custom property
111 | }
112 |
113 | if let backgroundEffectView = self.leftViewBackgroundEffectView {
114 | backgroundEffectView.removeFromSuperview()
115 | self.leftViewBackgroundEffectView = nil
116 | }
117 |
118 | if let backgroundWrapperView = self.leftViewBackgroundWrapperView {
119 | backgroundWrapperView.removeFromSuperview()
120 | self.leftViewBackgroundWrapperView = nil
121 | }
122 |
123 | if let wrapperView = self.leftViewWrapperView {
124 | wrapperView.removeFromSuperview()
125 | self.leftViewWrapperView = nil
126 | }
127 |
128 | if let coverView = self.leftViewCoverView {
129 | coverView.removeFromSuperview()
130 | self.leftViewCoverView = nil
131 | }
132 |
133 | if let statusBarBackgroundView = self.leftViewStatusBarBackgroundView {
134 | statusBarBackgroundView.removeFromSuperview()
135 | self.leftViewStatusBarBackgroundView = nil
136 | }
137 |
138 | if let statusBarBackgroundEffectView = self.leftViewStatusBarBackgroundEffectView {
139 | statusBarBackgroundEffectView.removeFromSuperview()
140 | self.leftViewStatusBarBackgroundEffectView = nil
141 | }
142 | }
143 |
144 | // MARK: - Right View -
145 |
146 | func removeRightViews() {
147 | if let containerView = self.rightContainerView {
148 | containerView.removeFromSuperview()
149 | self.rightContainerView = nil
150 | }
151 |
152 | if let containerClipToBorderView = self.rightContainerClipToBorderView {
153 | containerClipToBorderView.removeFromSuperview()
154 | self.rightContainerClipToBorderView = nil
155 | }
156 |
157 | if let backgroundDecorationView = self.rightViewBackgroundDecorationView {
158 | backgroundDecorationView.removeFromSuperview()
159 | self.rightViewBackgroundDecorationView = nil
160 | }
161 |
162 | if let backgroundShadowView = self.rightViewBackgroundShadowView {
163 | backgroundShadowView.removeFromSuperview()
164 | self.rightViewBackgroundShadowView = nil
165 | }
166 |
167 | if let backgroundImageView = self.rightViewBackgroundImageView {
168 | backgroundImageView.removeFromSuperview()
169 | self.rightViewBackgroundImageView = nil
170 | }
171 |
172 | if let backgroundView = self.rightViewBackgroundView {
173 | backgroundView.removeFromSuperview()
174 | // We don't nullify it because it is custom property
175 | }
176 |
177 | if let backgroundEffectView = self.rightViewBackgroundEffectView {
178 | backgroundEffectView.removeFromSuperview()
179 | self.rightViewBackgroundEffectView = nil
180 | }
181 |
182 | if let backgroundWrapperView = self.rightViewBackgroundWrapperView {
183 | backgroundWrapperView.removeFromSuperview()
184 | self.rightViewBackgroundWrapperView = nil
185 | }
186 |
187 | if let wrapperView = self.rightViewWrapperView {
188 | wrapperView.removeFromSuperview()
189 | self.rightViewWrapperView = nil
190 | }
191 |
192 | if let coverView = self.rightViewCoverView {
193 | coverView.removeFromSuperview()
194 | self.rightViewCoverView = nil
195 | }
196 |
197 | if let statusBarBackgroundView = self.rightViewStatusBarBackgroundView {
198 | statusBarBackgroundView.removeFromSuperview()
199 | self.rightViewStatusBarBackgroundView = nil
200 | }
201 |
202 | if let statusBarBackgroundEffectView = self.rightViewStatusBarBackgroundEffectView {
203 | statusBarBackgroundEffectView.removeFromSuperview()
204 | self.rightViewStatusBarBackgroundEffectView = nil
205 | }
206 | }
207 |
208 | }
209 |
--------------------------------------------------------------------------------
/LGSideMenuController/Extensions/Validators/LGSideMenuController+ValidatingUserInteraction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuController+ValidatingUserInteraction.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | internal extension LGSideMenuController {
34 |
35 | func validateViewsUserInteraction() {
36 | self.validateRootViewsUserInteraction()
37 | self.validateLeftViewsUserInteraction()
38 | self.validateRightViewsUserInteraction()
39 | }
40 |
41 | func validateRootViewsUserInteraction() {
42 | guard let rootViewWrapperView = self.rootViewWrapperView else { return }
43 | rootViewWrapperView.isUserInteractionEnabled = self.isRootViewShowing
44 | }
45 |
46 | func validateLeftViewsUserInteraction() {
47 | guard let leftViewWrapperView = self.leftViewWrapperView else { return }
48 | leftViewWrapperView.isUserInteractionEnabled =
49 | (self.isLeftViewShowing || self.isLeftViewAlwaysVisible) && self.isRightViewHidden
50 | }
51 |
52 | func validateRightViewsUserInteraction() {
53 | guard let rightViewWrapperView = self.rightViewWrapperView else { return }
54 | rightViewWrapperView.isUserInteractionEnabled =
55 | (self.isRightViewShowing || self.isRightViewAlwaysVisible) && self.isLeftViewHidden
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/LGSideMenuController/Extensions/Validators/LGSideMenuController+ValidatingViewsHierarchy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuController+ValidatingViewsHierarchy.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | internal extension LGSideMenuController {
34 |
35 | func validateViewsHierarchy() {
36 | self.validateRootViewsHierarchy()
37 | self.validateLeftViewsHierarchy()
38 | self.validateRightViewsHierarchy()
39 | }
40 |
41 | func validateRootViewsHierarchy() {
42 | guard let rootView = self.rootView,
43 | let containerView = self.rootContainerView,
44 | let containerClipToBorderView = self.rootContainerClipToBorderView,
45 | let backgroundDecorationView = self.rootViewBackgroundDecorationView,
46 | let backgroundShadowView = self.rootViewBackgroundShadowView,
47 | let wrapperView = self.rootViewWrapperView,
48 | let coverView = self.rootViewCoverView else { return }
49 |
50 | if isRootViewControllerLayoutingEnabled,
51 | let rootViewController = self.rootViewController,
52 | rootViewController.parent != self {
53 | self.addChild(rootViewController)
54 | rootViewController.didMove(toParent: self)
55 | }
56 |
57 | self.view.insertSubview(containerView, at: 0)
58 |
59 | containerView.insertSubview(backgroundDecorationView, at: 0)
60 | containerView.insertSubview(containerClipToBorderView, at: 1)
61 |
62 | backgroundDecorationView.insertSubview(backgroundShadowView, at: 0)
63 |
64 | containerClipToBorderView.insertSubview(wrapperView, at: 0)
65 | containerClipToBorderView.insertSubview(coverView, at: 1)
66 |
67 | wrapperView.insertSubview(rootView, at: 0)
68 | }
69 |
70 | func validateLeftViewsHierarchy() {
71 | guard let rootContainerView = self.rootContainerView,
72 | let leftView = self.leftView,
73 | let containerView = self.leftContainerView,
74 | let containerClipToBorderView = self.leftContainerClipToBorderView,
75 | let backgroundDecorationView = self.leftViewBackgroundDecorationView,
76 | let backgroundShadowView = self.leftViewBackgroundShadowView,
77 | let backgroundWrapperView = self.leftViewBackgroundWrapperView,
78 | let backgroundEffectView = self.leftViewBackgroundEffectView,
79 | let wrapperView = self.leftViewWrapperView,
80 | let coverView = self.leftViewCoverView,
81 | let statusBarBackgroundView = self.leftViewStatusBarBackgroundView,
82 | let statusBarBackgroundEffectView = self.leftViewStatusBarBackgroundEffectView else { return }
83 |
84 | if let leftViewController = self.leftViewController, leftViewController.parent != self {
85 | self.addChild(leftViewController)
86 | leftViewController.didMove(toParent: self)
87 | }
88 |
89 | if self.leftViewPresentationStyle.isAbove {
90 | self.view.insertSubview(containerView, aboveSubview: rootContainerView)
91 | }
92 | else {
93 | self.view.insertSubview(containerView, belowSubview: rootContainerView)
94 | }
95 |
96 | containerView.insertSubview(backgroundDecorationView, at: 0)
97 | containerView.insertSubview(containerClipToBorderView, at: 1)
98 |
99 | backgroundDecorationView.insertSubview(backgroundShadowView, at: 0)
100 | backgroundDecorationView.insertSubview(backgroundEffectView, at: 1)
101 | backgroundDecorationView.insertSubview(backgroundWrapperView, at: 2)
102 |
103 | containerClipToBorderView.insertSubview(wrapperView, at: 0)
104 | containerClipToBorderView.insertSubview(coverView, at: 1)
105 | containerClipToBorderView.insertSubview(statusBarBackgroundView, at: 2)
106 |
107 | if let backgroundImageView = self.leftViewBackgroundImageView {
108 | backgroundWrapperView.insertSubview(backgroundImageView, at: 0)
109 | }
110 | else if let backgroundView = self.leftViewBackgroundView {
111 | backgroundWrapperView.insertSubview(backgroundView, at: 0)
112 | }
113 |
114 | wrapperView.insertSubview(leftView, at: 0)
115 |
116 | statusBarBackgroundView.insertSubview(statusBarBackgroundEffectView, at: 0)
117 | }
118 |
119 | func validateRightViewsHierarchy() {
120 | guard let rootContainerView = self.rootContainerView,
121 | let rightView = self.rightView,
122 | let containerView = self.rightContainerView,
123 | let containerClipToBorderView = self.rightContainerClipToBorderView,
124 | let backgroundDecorationView = self.rightViewBackgroundDecorationView,
125 | let backgroundShadowView = self.rightViewBackgroundShadowView,
126 | let backgroundWrapperView = self.rightViewBackgroundWrapperView,
127 | let backgroundEffectView = self.rightViewBackgroundEffectView,
128 | let wrapperView = self.rightViewWrapperView,
129 | let coverView = self.rightViewCoverView,
130 | let statusBarBackgroundView = self.rightViewStatusBarBackgroundView,
131 | let statusBarBackgroundEffectView = self.rightViewStatusBarBackgroundEffectView else { return }
132 |
133 | if let rightViewController = self.rightViewController, rightViewController.parent != self {
134 | self.addChild(rightViewController)
135 | rightViewController.didMove(toParent: self)
136 | }
137 |
138 | if self.rightViewPresentationStyle.isAbove {
139 | self.view.insertSubview(containerView, aboveSubview: rootContainerView)
140 | }
141 | else {
142 | self.view.insertSubview(containerView, belowSubview: rootContainerView)
143 | }
144 |
145 | containerView.insertSubview(backgroundDecorationView, at: 0)
146 | containerView.insertSubview(containerClipToBorderView, at: 1)
147 |
148 | backgroundDecorationView.insertSubview(backgroundShadowView, at: 0)
149 | backgroundDecorationView.insertSubview(backgroundEffectView, at: 1)
150 | backgroundDecorationView.insertSubview(backgroundWrapperView, at: 2)
151 |
152 | containerClipToBorderView.insertSubview(wrapperView, at: 0)
153 | containerClipToBorderView.insertSubview(coverView, at: 1)
154 | containerClipToBorderView.insertSubview(statusBarBackgroundView, at: 2)
155 |
156 | if let backgroundImageView = self.rightViewBackgroundImageView {
157 | backgroundWrapperView.insertSubview(backgroundImageView, at: 0)
158 | }
159 | else if let backgroundView = self.rightViewBackgroundView {
160 | backgroundWrapperView.insertSubview(backgroundView, at: 0)
161 | }
162 |
163 | wrapperView.insertSubview(rightView, at: 0)
164 |
165 | statusBarBackgroundView.insertSubview(statusBarBackgroundEffectView, at: 0)
166 | }
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/LGSideMenuController/Extensions/Validators/LGSideMenuController+ValidatingViewsInit.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuController+ValidatingViewsInit.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | internal extension LGSideMenuController {
34 |
35 | func validateViewsInit() {
36 | self.validateRootViewsInit()
37 | self.validateLeftViewsInit()
38 | self.validateRightViewsInit()
39 | }
40 |
41 | func validateRootViewsInit() {
42 | guard self.rootView != nil else { return }
43 |
44 | if self.rootContainerView == nil {
45 | self.rootContainerView = UIView()
46 | defaultRootViewSetup(self.rootContainerView, true)
47 | }
48 |
49 | if self.rootContainerClipToBorderView == nil {
50 | self.rootContainerClipToBorderView = UIView()
51 | self.rootContainerClipToBorderView?.clipsToBounds = true
52 | defaultRootViewSetup(self.rootContainerClipToBorderView, true)
53 | }
54 |
55 | if self.rootViewBackgroundDecorationView == nil {
56 | self.rootViewBackgroundDecorationView = LGSideMenuBackgroundDecorationView()
57 | defaultRootViewSetup(self.rootViewBackgroundDecorationView)
58 | }
59 |
60 | if self.rootViewBackgroundShadowView == nil {
61 | self.rootViewBackgroundShadowView = LGSideMenuBackgroundShadowView()
62 | defaultRootViewSetup(self.rootViewBackgroundShadowView)
63 | }
64 |
65 | if self.rootViewWrapperView == nil {
66 | self.rootViewWrapperView = LGSideMenuWrapperView()
67 | self.rootViewWrapperView?.clipsToBounds = true
68 | self.rootViewWrapperView?.canLayoutSubviews = self.isRootViewLayoutingEnabled
69 | defaultRootViewSetup(self.rootViewWrapperView, true)
70 | }
71 |
72 | if self.rootViewCoverView == nil {
73 | self.rootViewCoverView = UIVisualEffectView()
74 | defaultRootViewSetup(self.rootViewCoverView)
75 | }
76 | }
77 |
78 | func validateLeftViewsInit() {
79 | guard self.leftView != nil else { return }
80 |
81 | if self.leftContainerView == nil {
82 | self.leftContainerView = UIView()
83 | self.leftContainerView?.clipsToBounds = true
84 | defaultLeftViewSetup(self.leftContainerView, true)
85 | }
86 |
87 | if self.leftContainerClipToBorderView == nil {
88 | self.leftContainerClipToBorderView = UIView()
89 | self.leftContainerClipToBorderView?.clipsToBounds = true
90 | defaultLeftViewSetup(self.leftContainerClipToBorderView, true)
91 | }
92 |
93 | if self.leftViewBackgroundDecorationView == nil {
94 | self.leftViewBackgroundDecorationView = LGSideMenuBackgroundDecorationView()
95 | defaultLeftViewSetup(self.leftViewBackgroundDecorationView)
96 | }
97 |
98 | if self.leftViewBackgroundShadowView == nil {
99 | self.leftViewBackgroundShadowView = LGSideMenuBackgroundShadowView()
100 | defaultLeftViewSetup(self.leftViewBackgroundShadowView)
101 | }
102 |
103 | if self.leftViewBackgroundEffectView == nil {
104 | self.leftViewBackgroundEffectView = UIVisualEffectView()
105 | defaultLeftViewSetup(self.leftViewBackgroundEffectView)
106 | }
107 |
108 | if self.leftViewBackgroundWrapperView == nil {
109 | self.leftViewBackgroundWrapperView = UIView()
110 | self.leftViewBackgroundWrapperView?.clipsToBounds = true
111 | defaultLeftViewSetup(self.leftViewBackgroundWrapperView)
112 | }
113 |
114 | if self.leftViewBackgroundImage != nil && self.leftViewBackgroundImageView == nil {
115 | self.leftViewBackgroundImageView = UIImageView()
116 | self.leftViewBackgroundImageView?.contentMode = .scaleAspectFill
117 | defaultLeftViewSetup(self.leftViewBackgroundImageView)
118 | }
119 |
120 | if self.leftViewWrapperView == nil {
121 | self.leftViewWrapperView = LGSideMenuWrapperView()
122 | self.leftViewWrapperView?.clipsToBounds = true
123 | defaultLeftViewSetup(self.leftViewWrapperView, true)
124 | }
125 |
126 | if self.leftViewCoverView == nil {
127 | self.leftViewCoverView = UIVisualEffectView()
128 | defaultLeftViewSetup(self.leftViewCoverView)
129 | }
130 |
131 | if self.leftViewStatusBarBackgroundView == nil {
132 | self.leftViewStatusBarBackgroundView = LGSideMenuStatusBarBackgroundView()
133 | defaultLeftViewSetup(self.leftViewStatusBarBackgroundView)
134 | }
135 |
136 | if self.leftViewStatusBarBackgroundEffectView == nil {
137 | self.leftViewStatusBarBackgroundEffectView = UIVisualEffectView()
138 | defaultLeftViewSetup(self.leftViewStatusBarBackgroundEffectView)
139 | }
140 | }
141 |
142 | func validateRightViewsInit() {
143 | guard self.rightView != nil else { return }
144 |
145 | if self.rightContainerView == nil {
146 | self.rightContainerView = UIView()
147 | self.rightContainerView?.clipsToBounds = true
148 | defaultRightViewSetup(self.rightContainerView, true)
149 | }
150 |
151 | if self.rightContainerClipToBorderView == nil {
152 | self.rightContainerClipToBorderView = UIView()
153 | self.rightContainerClipToBorderView?.clipsToBounds = true
154 | defaultRightViewSetup(self.rightContainerClipToBorderView, true)
155 | }
156 |
157 | if self.rightViewBackgroundDecorationView == nil {
158 | self.rightViewBackgroundDecorationView = LGSideMenuBackgroundDecorationView()
159 | defaultRightViewSetup(self.rightViewBackgroundDecorationView)
160 | }
161 |
162 | if self.rightViewBackgroundShadowView == nil {
163 | self.rightViewBackgroundShadowView = LGSideMenuBackgroundShadowView()
164 | defaultRightViewSetup(self.rightViewBackgroundShadowView)
165 | }
166 |
167 | if self.rightViewBackgroundEffectView == nil {
168 | self.rightViewBackgroundEffectView = UIVisualEffectView()
169 | defaultRightViewSetup(self.rightViewBackgroundEffectView)
170 | }
171 |
172 | if self.rightViewBackgroundWrapperView == nil {
173 | self.rightViewBackgroundWrapperView = UIView()
174 | self.rightViewBackgroundWrapperView?.clipsToBounds = true
175 | defaultRightViewSetup(self.rightViewBackgroundWrapperView)
176 | }
177 |
178 | if self.rightViewBackgroundImage != nil && self.rightViewBackgroundImageView == nil {
179 | self.rightViewBackgroundImageView = UIImageView()
180 | self.rightViewBackgroundImageView?.contentMode = .scaleAspectFill
181 | defaultRightViewSetup(self.rightViewBackgroundImageView)
182 | }
183 |
184 | if self.rightViewWrapperView == nil {
185 | self.rightViewWrapperView = LGSideMenuWrapperView()
186 | self.rightViewWrapperView?.clipsToBounds = true
187 | defaultRightViewSetup(self.rightViewWrapperView, true)
188 | }
189 |
190 | if self.rightViewCoverView == nil {
191 | self.rightViewCoverView = UIVisualEffectView()
192 | defaultRightViewSetup(self.rightViewCoverView)
193 | }
194 |
195 | if self.rightViewStatusBarBackgroundView == nil {
196 | self.rightViewStatusBarBackgroundView = LGSideMenuStatusBarBackgroundView()
197 | defaultRightViewSetup(self.rightViewStatusBarBackgroundView)
198 | }
199 |
200 | if self.rightViewStatusBarBackgroundEffectView == nil {
201 | self.rightViewStatusBarBackgroundEffectView = UIVisualEffectView()
202 | defaultRightViewSetup(self.rightViewStatusBarBackgroundEffectView)
203 | }
204 | }
205 |
206 | private func defaultRootViewSetup(_ view: UIView?, _ isUserInteractionEnabled: Bool = false) {
207 | view?.backgroundColor = .clear
208 | view?.isUserInteractionEnabled = isUserInteractionEnabled
209 | }
210 |
211 | private func defaultLeftViewSetup(_ view: UIView?, _ isUserInteractionEnabled: Bool = false) {
212 | view?.backgroundColor = .clear
213 | view?.isUserInteractionEnabled = isUserInteractionEnabled
214 | view?.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5)
215 | }
216 |
217 | private func defaultRightViewSetup(_ view: UIView?, _ isUserInteractionEnabled: Bool = false) {
218 | view?.backgroundColor = .clear
219 | view?.isUserInteractionEnabled = isUserInteractionEnabled
220 | view?.layer.anchorPoint = CGPoint(x: 1.0, y: 0.5)
221 | }
222 |
223 | }
224 |
--------------------------------------------------------------------------------
/LGSideMenuController/Extensions/Validators/LGSideMenuController+ValidatingViewsStyles.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuController+ValidatingViewsStyles.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | internal extension LGSideMenuController {
34 |
35 | func validateViewsStyles() {
36 | self.validateRootViewsStyles()
37 | self.validateLeftViewsStyles()
38 | self.validateRightViewsStyles()
39 | }
40 |
41 | func validateRootViewsStyles() {
42 | guard let backgroundDecorationView = self.rootViewBackgroundDecorationView,
43 | let backgroundShadowView = self.rootViewBackgroundShadowView,
44 | let coverView = self.rootViewCoverView else { return }
45 |
46 | backgroundDecorationView.fillColor = self.rootViewBackgroundColor
47 |
48 | if self.leftView != nil && self.isLeftViewVisibleToUser {
49 | backgroundDecorationView.strokeColor = self.rootViewLayerBorderColorForLeftView
50 | backgroundDecorationView.strokeWidth = self.rootViewLayerBorderWidthForLeftView
51 |
52 | backgroundShadowView.shadowColor = self.rootViewLayerShadowColorForLeftView
53 | backgroundShadowView.shadowBlur = self.rootViewLayerShadowRadiusForLeftView
54 | }
55 | else if self.rightView != nil && self.isRightViewVisibleToUser {
56 | backgroundDecorationView.strokeColor = self.rootViewLayerBorderColorForRightView
57 | backgroundDecorationView.strokeWidth = self.rootViewLayerBorderWidthForRightView
58 |
59 | backgroundShadowView.shadowColor = self.rootViewLayerShadowColorForRightView
60 | backgroundShadowView.shadowBlur = self.rootViewLayerShadowRadiusForRightView
61 | }
62 | else {
63 | backgroundDecorationView.strokeColor = .clear
64 | backgroundDecorationView.strokeWidth = 0.0
65 |
66 | backgroundShadowView.shadowColor = .clear
67 | backgroundShadowView.shadowBlur = 0.0
68 | }
69 |
70 | if self.leftView != nil && self.isLeftViewVisible && !self.isLeftViewAlwaysVisible {
71 | coverView.backgroundColor = self.rootViewCoverColorForLeftView
72 | coverView.effect = self.rootViewCoverBlurEffectForLeftView
73 | }
74 | else if self.rightView != nil && self.isRightViewVisible && !self.isRightViewAlwaysVisible {
75 | coverView.backgroundColor = self.rootViewCoverColorForRightView
76 | coverView.effect = self.rootViewCoverBlurEffectForRightView
77 | }
78 | else {
79 | coverView.backgroundColor = nil
80 | coverView.effect = nil
81 | }
82 | }
83 |
84 | func validateLeftViewsStyles() {
85 | guard let backgroundDecorationView = self.leftViewBackgroundDecorationView,
86 | let backgroundShadowView = self.leftViewBackgroundShadowView,
87 | let backgroundEffectView = self.leftViewBackgroundEffectView,
88 | let coverView = self.leftViewCoverView,
89 | let statusBarBackgroundView = self.leftViewStatusBarBackgroundView,
90 | let statusBarBackgroundEffectView = self.leftViewStatusBarBackgroundEffectView else { return }
91 |
92 | backgroundDecorationView.fillColor = self.leftViewBackgroundColor
93 | backgroundDecorationView.strokeColor = self.leftViewLayerBorderColor
94 | backgroundDecorationView.strokeWidth = self.leftViewLayerBorderWidth
95 | backgroundDecorationView.alpha = self.leftViewBackgroundAlpha
96 |
97 | backgroundShadowView.shadowColor = self.leftViewLayerShadowColor
98 | backgroundShadowView.shadowBlur = self.leftViewLayerShadowRadius
99 |
100 | backgroundEffectView.effect = self.leftViewBackgroundBlurEffect
101 |
102 | if let backgroundImageView = self.leftViewBackgroundImageView {
103 | backgroundImageView.image = self.leftViewBackgroundImage
104 | }
105 |
106 | if self.isLeftViewAlwaysVisible {
107 | coverView.backgroundColor = self.leftViewCoverColorWhenAlwaysVisible
108 | coverView.effect = self.leftViewCoverBlurEffectWhenAlwaysVisible
109 | }
110 | else {
111 | coverView.backgroundColor = self.leftViewCoverColor
112 | coverView.effect = self.leftViewCoverBlurEffect
113 | }
114 |
115 | statusBarBackgroundView.fillColor = self.leftViewStatusBarBackgroundColor
116 | statusBarBackgroundView.shadowColor = self.leftViewStatusBarBackgroundShadowColor
117 | statusBarBackgroundView.shadowBlur = self.leftViewStatusBarBackgroundShadowRadius
118 |
119 | statusBarBackgroundEffectView.effect = self.leftViewStatusBarBackgroundBlurEffect
120 | }
121 |
122 | func validateRightViewsStyles() {
123 | guard let backgroundDecorationView = self.rightViewBackgroundDecorationView,
124 | let backgroundShadowView = self.rightViewBackgroundShadowView,
125 | let backgroundEffectView = self.rightViewBackgroundEffectView,
126 | let coverView = self.rightViewCoverView,
127 | let statusBarBackgroundView = self.rightViewStatusBarBackgroundView,
128 | let statusBarBackgroundEffectView = self.rightViewStatusBarBackgroundEffectView else { return }
129 |
130 | backgroundDecorationView.fillColor = self.rightViewBackgroundColor
131 | backgroundDecorationView.strokeColor = self.rightViewLayerBorderColor
132 | backgroundDecorationView.strokeWidth = self.rightViewLayerBorderWidth
133 | backgroundDecorationView.alpha = self.rightViewBackgroundAlpha
134 |
135 | backgroundShadowView.shadowColor = self.rightViewLayerShadowColor
136 | backgroundShadowView.shadowBlur = self.rightViewLayerShadowRadius
137 |
138 | backgroundEffectView.effect = self.rightViewBackgroundBlurEffect
139 |
140 | if let backgroundImageView = self.rightViewBackgroundImageView {
141 | backgroundImageView.image = self.rightViewBackgroundImage
142 | }
143 |
144 | if self.isRightViewAlwaysVisible {
145 | coverView.backgroundColor = self.rightViewCoverColorWhenAlwaysVisible
146 | coverView.effect = self.rightViewCoverBlurEffectWhenAlwaysVisible
147 | }
148 | else {
149 | coverView.backgroundColor = self.rightViewCoverColor
150 | coverView.effect = self.rightViewCoverBlurEffect
151 | }
152 |
153 | statusBarBackgroundView.fillColor = self.rightViewStatusBarBackgroundColor
154 | statusBarBackgroundView.shadowColor = self.rightViewStatusBarBackgroundShadowColor
155 | statusBarBackgroundView.shadowBlur = self.rightViewStatusBarBackgroundShadowRadius
156 |
157 | statusBarBackgroundEffectView.effect = self.rightViewStatusBarBackgroundBlurEffect
158 | }
159 |
160 | }
161 |
--------------------------------------------------------------------------------
/LGSideMenuController/Extensions/Validators/LGSideMenuController+ValidatingViewsVisibility.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuController+ValidatingViewsVisibility.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | internal extension LGSideMenuController {
34 |
35 | func validateViewsVisibility() {
36 | self.validateRootViewsVisibility()
37 | self.validateLeftViewsVisibility()
38 | self.validateRightViewsVisibility()
39 | }
40 |
41 | func validateRootViewsVisibility() {
42 | guard self.shouldUpdateVisibility == true,
43 | let backgroundDecorationView = self.rootViewBackgroundDecorationView,
44 | let coverView = self.rootViewCoverView else { return }
45 |
46 | backgroundDecorationView.isHidden = self.isRootViewShowing && !self.isLeftViewAlwaysVisible && !self.isRightViewAlwaysVisible
47 | coverView.isHidden = self.isRootViewShowing
48 | }
49 |
50 | func validateLeftViewsVisibility() {
51 | guard self.shouldUpdateVisibility == true,
52 | let containerView = self.leftContainerView,
53 | let coverView = self.leftViewCoverView,
54 | let statusBarBackgroundView = self.leftViewStatusBarBackgroundView else { return }
55 |
56 | containerView.isHidden = !self.isLeftViewVisibleToUser
57 | coverView.isHidden = self.isLeftViewShowing || (self.isLeftViewAlwaysVisible && !self.isRightViewVisible)
58 |
59 | statusBarBackgroundView.isHidden =
60 | self.isLeftViewStatusBarBackgroundHidden ||
61 | self.isLeftViewStatusBarHidden ||
62 | self.isNavigationBarVisible() ||
63 | !self.isViewLocatedUnderStatusBar
64 | }
65 |
66 | func validateRightViewsVisibility() {
67 | guard self.shouldUpdateVisibility == true,
68 | let containerView = self.rightContainerView,
69 | let coverView = self.rightViewCoverView,
70 | let statusBarBackgroundView = self.rightViewStatusBarBackgroundView else { return }
71 |
72 | containerView.isHidden = !self.isRightViewVisibleToUser
73 | coverView.isHidden = self.isRightViewShowing || (self.isRightViewAlwaysVisible && !self.isLeftViewVisible)
74 |
75 | statusBarBackgroundView.isHidden =
76 | self.isRightViewStatusBarBackgroundHidden ||
77 | self.isRightViewStatusBarHidden ||
78 | self.isNavigationBarVisible() ||
79 | !self.isViewLocatedUnderStatusBar
80 | }
81 |
82 | private func isNavigationBarVisible() -> Bool {
83 | guard let navigationController = navigationController else { return false }
84 | return !navigationController.isNavigationBarHidden
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/LGSideMenuController/LGSideMenuBackgroundDecorationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuBackgroundDecorationView.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import CoreGraphics
32 | import UIKit
33 |
34 | public final class LGSideMenuBackgroundDecorationView: UIView {
35 |
36 | public internal(set) var strokeColor: UIColor = .clear {
37 | didSet {
38 | setNeedsDisplay()
39 | }
40 | }
41 |
42 | public internal(set) var strokeWidth: CGFloat = .zero {
43 | didSet {
44 | setNeedsDisplay()
45 | }
46 | }
47 |
48 | public internal(set) var fillColor: UIColor = .clear {
49 | didSet {
50 | setNeedsDisplay()
51 | }
52 | }
53 |
54 | public init() {
55 | super.init(frame: .zero)
56 | backgroundColor = .clear
57 | }
58 |
59 | required public init?(coder: NSCoder) {
60 | fatalError("init(coder:) has not been implemented")
61 | }
62 |
63 | public override func draw(_ rect: CGRect) {
64 | guard let context = UIGraphicsGetCurrentContext() else { return }
65 |
66 | context.clear(rect)
67 |
68 | let path = UIBezierPath(rect: rect)
69 | path.close()
70 |
71 | // To have inner stroke we need to fill rect and erase smaller rect from inside of it
72 | if self.strokeColor != .clear && self.strokeWidth > 0 {
73 | context.beginPath()
74 | context.addPath(path.cgPath)
75 | context.setFillColor(strokeColor.cgColor)
76 | context.fillPath()
77 |
78 | let strokePath = getStrokedPath(rect: rect)
79 | strokePath.close()
80 |
81 | context.beginPath()
82 | context.addPath(strokePath.cgPath)
83 | context.setFillColor(UIColor.clear.cgColor)
84 | context.setBlendMode(.clear)
85 | context.fillPath()
86 | context.setBlendMode(.normal)
87 | }
88 |
89 | // Fill smaller rect
90 | if self.fillColor != .clear {
91 | let strokePath = getStrokedPath(rect: rect)
92 | strokePath.close()
93 |
94 | context.beginPath()
95 | context.addPath(strokePath.cgPath)
96 | context.setFillColor(fillColor.cgColor)
97 | context.fillPath()
98 | }
99 | }
100 |
101 | private func getStrokedPath(rect: CGRect) -> UIBezierPath {
102 | return UIBezierPath(rect: rect.insetBy(dx: self.strokeWidth, dy: self.strokeWidth))
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/LGSideMenuController/LGSideMenuBackgroundShadowView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuBackgroundShadowView.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import CoreGraphics
32 | import UIKit
33 |
34 | public final class LGSideMenuBackgroundShadowView: UIView {
35 |
36 | public internal(set) var shadowColor: UIColor = .clear {
37 | didSet {
38 | setNeedsDisplay()
39 | }
40 | }
41 |
42 | public internal(set) var shadowBlur: CGFloat = .zero {
43 | didSet {
44 | setNeedsDisplay()
45 | }
46 | }
47 |
48 | public init() {
49 | super.init(frame: .zero)
50 | backgroundColor = .clear
51 | }
52 |
53 | required public init?(coder: NSCoder) {
54 | fatalError("init(coder:) has not been implemented")
55 | }
56 |
57 | public override func draw(_ rect: CGRect) {
58 | guard let context = UIGraphicsGetCurrentContext() else { return }
59 |
60 | let drawRect = rect.insetBy(dx: self.shadowBlur, dy: self.shadowBlur)
61 |
62 | let path = UIBezierPath(rect: drawRect)
63 | path.close()
64 |
65 | context.clear(rect)
66 | context.beginPath()
67 | context.addPath(path.cgPath)
68 |
69 | // Fill it black to draw proper shadow, then erase black internals and keep only shadow
70 | if shadowColor != .clear && self.shadowBlur > 0 {
71 | context.setShadow(offset: .zero, blur: self.shadowBlur, color: shadowColor.cgColor)
72 | context.setFillColor(UIColor.black.cgColor)
73 | context.fillPath()
74 | context.setShadow(offset: .zero, blur: .zero, color: nil)
75 |
76 | context.beginPath()
77 | context.addPath(path.cgPath)
78 | context.setFillColor(UIColor.clear.cgColor)
79 | context.setBlendMode(.clear)
80 | context.fillPath()
81 | context.setBlendMode(.normal)
82 | }
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/LGSideMenuController/LGSideMenuDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuDelegate.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import QuartzCore
32 | import UIKit
33 |
34 | /// Delegate protocol to observe behaviour of LGSideMenuController
35 | public protocol LGSideMenuDelegate {
36 |
37 | func willShowLeftView(sideMenuController: LGSideMenuController)
38 | func didShowLeftView(sideMenuController: LGSideMenuController)
39 |
40 | func willHideLeftView(sideMenuController: LGSideMenuController)
41 | func didHideLeftView(sideMenuController: LGSideMenuController)
42 |
43 | func willShowRightView(sideMenuController: LGSideMenuController)
44 | func didShowRightView(sideMenuController: LGSideMenuController)
45 |
46 | func willHideRightView(sideMenuController: LGSideMenuController)
47 | func didHideRightView(sideMenuController: LGSideMenuController)
48 |
49 | /// This method is executed inside animation block for showing left view.
50 | /// Use it to add some custom animations.
51 | func showAnimationsForLeftView(sideMenuController: LGSideMenuController,
52 | duration: TimeInterval,
53 | timingFunction: CAMediaTimingFunction)
54 |
55 | /// This method is executed inside animation block for hiding left view.
56 | /// Use it to add some custom animations.
57 | func hideAnimationsForLeftView(sideMenuController: LGSideMenuController,
58 | duration: TimeInterval,
59 | timingFunction: CAMediaTimingFunction)
60 |
61 | /// This method is executed inside animation block for showing right view.
62 | /// Use it to add some custom animations.
63 | func showAnimationsForRightView(sideMenuController: LGSideMenuController,
64 | duration: TimeInterval,
65 | timingFunction: CAMediaTimingFunction)
66 |
67 | /// This method is executed inside animation block for hiding right view.
68 | /// Use it to add some custom animations.
69 | func hideAnimationsForRightView(sideMenuController: LGSideMenuController,
70 | duration: TimeInterval,
71 | timingFunction: CAMediaTimingFunction)
72 |
73 | /// This method is executed on every transformation of root view during showing/hiding of side views
74 | /// You can retrieve percentage between `0.0` and `1.0` from userInfo dictionary, where
75 | /// - `0.0` - view is fully shown
76 | /// - `1.0` - view is fully hidden
77 | func didTransformRootView(sideMenuController: LGSideMenuController, percentage: CGFloat)
78 |
79 | /// This method is executed on every transformation of left view during showing/hiding
80 | /// You can retrieve percentage between `0.0` and `1.0` from userInfo dictionary, where
81 | /// - `0.0` - view is fully hidden
82 | /// - `1.0` - view is fully shown
83 | func didTransformLeftView(sideMenuController: LGSideMenuController, percentage: CGFloat)
84 |
85 | /// This method is executed on every transformation of right view during showing/hiding
86 | /// You can retrieve percentage between `0.0` and `1.0` from userInfo dictionary, where
87 | /// - `0.0` - view is fully hidden
88 | /// - `1.0` - view is fully shown
89 | func didTransformRightView(sideMenuController: LGSideMenuController, percentage: CGFloat)
90 | }
91 |
92 | // As swift doesn't support optional methods,
93 | // we use this extension with default empty implementations for delegate methods
94 | public extension LGSideMenuDelegate {
95 |
96 | func willShowLeftView(sideMenuController: LGSideMenuController) {}
97 | func didShowLeftView(sideMenuController: LGSideMenuController) {}
98 |
99 | func willHideLeftView(sideMenuController: LGSideMenuController) {}
100 | func didHideLeftView(sideMenuController: LGSideMenuController) {}
101 |
102 | func willShowRightView(sideMenuController: LGSideMenuController) {}
103 | func didShowRightView(sideMenuController: LGSideMenuController) {}
104 |
105 | func willHideRightView(sideMenuController: LGSideMenuController) {}
106 | func didHideRightView(sideMenuController: LGSideMenuController) {}
107 |
108 | func showAnimationsForLeftView(sideMenuController: LGSideMenuController,
109 | duration: TimeInterval,
110 | timingFunction: CAMediaTimingFunction) {}
111 |
112 | func hideAnimationsForLeftView(sideMenuController: LGSideMenuController,
113 | duration: TimeInterval,
114 | timingFunction: CAMediaTimingFunction) {}
115 |
116 | func showAnimationsForRightView(sideMenuController: LGSideMenuController,
117 | duration: TimeInterval,
118 | timingFunction: CAMediaTimingFunction) {}
119 |
120 | func hideAnimationsForRightView(sideMenuController: LGSideMenuController,
121 | duration: TimeInterval,
122 | timingFunction: CAMediaTimingFunction) {}
123 |
124 | func rootViewIsTransforming(sideMenuController: LGSideMenuController, percentage: CGFloat) {}
125 | func leftViewIsTransforming(sideMenuController: LGSideMenuController, percentage: CGFloat) {}
126 | func rightViewIsTransforming(sideMenuController: LGSideMenuController, percentage: CGFloat) {}
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/LGSideMenuController/LGSideMenuHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuHelper.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import ObjectiveC
32 | import QuartzCore
33 | import UIKit
34 |
35 | internal struct LGSideMenuHelper {
36 | private struct Keys {
37 | static var sideMenuController = "sideMenuController"
38 | }
39 |
40 | static func animate(duration: TimeInterval, timingFunction: CAMediaTimingFunction, animations: @escaping () -> Void, completion: @escaping () -> Void) {
41 | UIView.beginAnimations(nil, context: nil)
42 | UIView.setAnimationDuration(duration)
43 | CATransaction.begin()
44 | CATransaction.setCompletionBlock(completion)
45 | CATransaction.setAnimationTimingFunction(timingFunction)
46 | animations()
47 | CATransaction.commit()
48 | UIView.commitAnimations()
49 | }
50 |
51 | static func statusBarAppearanceUpdate(viewController: UIViewController, duration: TimeInterval, animations: (() -> Void)?) {
52 | if viewController.preferredStatusBarUpdateAnimation == .none || duration == .zero {
53 | if let animations = animations {
54 | animations()
55 | }
56 | viewController.setNeedsStatusBarAppearanceUpdate()
57 | }
58 | else {
59 | UIView.animate(withDuration: duration, animations: {
60 | if let animations = animations {
61 | animations()
62 | }
63 | viewController.setNeedsStatusBarAppearanceUpdate()
64 | })
65 | }
66 | }
67 |
68 | static func isPhone() -> Bool {
69 | return UIDevice.current.userInterfaceIdiom == .phone
70 | }
71 |
72 | static func isPad() -> Bool {
73 | return UIDevice.current.userInterfaceIdiom == .pad
74 | }
75 |
76 | static func getKeyWindow() -> UIWindow? {
77 | if #available(iOS 13.0, *) {
78 | return UIApplication.shared.windows.first(where: { $0.isKeyWindow })
79 | } else {
80 | return UIApplication.shared.keyWindow
81 | }
82 | }
83 |
84 | static func getStatusBarFrame() -> CGRect {
85 | if #available(iOS 13.0, *) {
86 | return getKeyWindow()?.windowScene?.statusBarManager?.statusBarFrame ?? .zero
87 | } else {
88 | return UIApplication.shared.statusBarFrame
89 | }
90 | }
91 |
92 | static func getInterfaceOrientation() -> UIInterfaceOrientation? {
93 | if #available(iOS 13.0, *) {
94 | return getKeyWindow()?.windowScene?.interfaceOrientation
95 | } else {
96 | return UIApplication.shared.statusBarOrientation
97 | }
98 | }
99 |
100 | static func getOppositeInterfaceOrientation() -> UIInterfaceOrientation? {
101 | guard let orientation = getInterfaceOrientation() else { return nil }
102 |
103 | if (orientation.isLandscape) {
104 | return .portrait
105 | } else {
106 | return .landscapeLeft
107 | }
108 | }
109 |
110 | static func isPortrait() -> Bool {
111 | return getInterfaceOrientation()?.isPortrait ?? true
112 | }
113 |
114 | static func isLandscape() -> Bool {
115 | return getInterfaceOrientation()?.isLandscape ?? false
116 | }
117 |
118 | static func setSideMenuController(_ sideMenuController: LGSideMenuController?, to viewController: UIViewController) {
119 | objc_setAssociatedObject(viewController, &Keys.sideMenuController, sideMenuController, .OBJC_ASSOCIATION_ASSIGN)
120 | }
121 |
122 | static func getSideMenuController(from viewController: UIViewController) -> LGSideMenuController? {
123 | return objc_getAssociatedObject(viewController, &Keys.sideMenuController) as? LGSideMenuController
124 | }
125 |
126 | static func canPerformSegue(_ viewController: UIViewController, withIdentifier identifier: String) -> Bool {
127 | guard let identifiers = viewController.value(forKey: "storyboardSegueTemplates") as? [NSObject] else { return false }
128 | return identifiers.contains { (object: NSObject) -> Bool in
129 | if let id = object.value(forKey: "_identifier") as? String {
130 | return id == identifier
131 | } else {
132 | return false
133 | }
134 | }
135 | }
136 |
137 | }
138 |
--------------------------------------------------------------------------------
/LGSideMenuController/LGSideMenuSegue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuSegue.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | public final class LGSideMenuSegue: UIStoryboardSegue {
34 |
35 | public struct Identifier {
36 | public static let root = "root"
37 | public static let left = "left"
38 | public static let right = "right"
39 | }
40 |
41 | public override func perform() {
42 | guard let sideMenuController = self.source as? LGSideMenuController else {
43 | assert(false, "LGSideMenuSegue must have source as LGSideMenuController")
44 | return
45 | }
46 |
47 | switch identifier {
48 | case Identifier.root:
49 | sideMenuController.rootViewController = destination
50 | case Identifier.left:
51 | sideMenuController.leftViewController = destination
52 | case Identifier.right:
53 | sideMenuController.rightViewController = destination
54 | default:
55 | assert(false, "LGSideMenuSegue must have identifier either \"root\", \"left\" or \"right\"")
56 | }
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/LGSideMenuController/LGSideMenuStatusBarBackgroundView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuStatusBarBackgroundView.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import CoreGraphics
32 | import UIKit
33 |
34 | public final class LGSideMenuStatusBarBackgroundView: UIView {
35 |
36 | public internal(set) var fillColor: UIColor = .clear {
37 | didSet {
38 | setNeedsDisplay()
39 | }
40 | }
41 |
42 | public internal(set) var shadowColor: UIColor = .clear {
43 | didSet {
44 | setNeedsDisplay()
45 | }
46 | }
47 |
48 | public internal(set) var shadowBlur: CGFloat = .zero {
49 | didSet {
50 | setNeedsDisplay()
51 | }
52 | }
53 |
54 | public init() {
55 | super.init(frame: CGRect.zero)
56 | self.backgroundColor = .clear
57 | }
58 |
59 | required public init?(coder: NSCoder) {
60 | fatalError("init(coder:) has not been implemented")
61 | }
62 |
63 | public override func draw(_ rect: CGRect) {
64 | guard let context = UIGraphicsGetCurrentContext() else { return }
65 |
66 | // dx: 0.0, because shadow is much lighter at the corner
67 | let drawRect = rect.insetBy(dx: 0.0, dy: self.shadowBlur)
68 |
69 | let path = UIBezierPath(rect: drawRect)
70 | path.close()
71 |
72 | context.clear(rect)
73 | context.beginPath()
74 | context.addPath(path.cgPath)
75 |
76 | // Fill it black to draw proper shadow, then erase black internals and keep only shadow
77 | if shadowColor != .clear && self.shadowBlur > 0 {
78 | context.setShadow(offset: .zero, blur: self.shadowBlur, color: shadowColor.cgColor)
79 | context.setFillColor(UIColor.black.cgColor)
80 | context.fillPath()
81 | context.setShadow(offset: .zero, blur: .zero, color: nil)
82 |
83 | context.beginPath()
84 | context.addPath(path.cgPath)
85 | context.setFillColor(UIColor.clear.cgColor)
86 | context.setBlendMode(.clear)
87 | context.fillPath()
88 | context.setBlendMode(.normal)
89 | }
90 |
91 | // Fill it with proper color
92 | if self.fillColor != .clear {
93 | context.beginPath()
94 | context.addPath(path.cgPath)
95 | context.setFillColor(fillColor.cgColor)
96 | context.fillPath()
97 | }
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/LGSideMenuController/LGSideMenuWrapperView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGSideMenuWrapperView.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | public final class LGSideMenuWrapperView: UIView {
34 |
35 | public var canLayoutSubviews = true
36 |
37 | public override func setNeedsLayout() {
38 | guard canLayoutSubviews else { return }
39 | super.setNeedsLayout()
40 | }
41 |
42 | public override func layoutIfNeeded() {
43 | guard canLayoutSubviews else { return }
44 | super.layoutIfNeeded()
45 | }
46 |
47 | public override func layoutSubviews() {
48 | guard canLayoutSubviews else { return }
49 | super.layoutSubviews()
50 | }
51 |
52 | public override func layoutSublayers(of layer: CALayer) {
53 | guard canLayoutSubviews else { return }
54 | super.layoutSublayers(of: layer)
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/LGSideMenuController/UIViewController+LGSideMenuController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+LGSideMenuController.swift
3 | // LGSideMenuController
4 | //
5 | //
6 | // The MIT License (MIT)
7 | //
8 | // Copyright © 2015 Grigorii Lutkov
9 | // (https://github.com/Friend-LGA/LGSideMenuController)
10 | //
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | //
29 |
30 | import Foundation
31 | import UIKit
32 |
33 | extension UIViewController {
34 |
35 | /// If the view controller or one of its ancestors is a child of a LGSideMenuController, this property contains the owning LGSideMenuController.
36 | /// This property is nil if the view controller is not embedded inside a LGSideMenuController.
37 | weak open var sideMenuController: LGSideMenuController? {
38 | if let controller = self as? LGSideMenuController {
39 | return controller
40 | }
41 | if let controller = LGSideMenuHelper.getSideMenuController(from: self) {
42 | return controller
43 | }
44 | if let controller = self.parent?.sideMenuController {
45 | return controller
46 | }
47 | return nil
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Grigorii Lutkov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | //
3 | //
4 | // The MIT License (MIT)
5 | //
6 | // Copyright © 2015 Grigorii Lutkov
7 | // (https://github.com/Friend-LGA/LGSideMenuController)
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in all
17 | // copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | // SOFTWARE.
26 | //
27 |
28 | import PackageDescription
29 |
30 | let package = Package(
31 | name: "LGSideMenuController",
32 | platforms: [
33 | .iOS(.v9)
34 | ], products: [
35 | .library(name: "LGSideMenuController",
36 | targets: ["LGSideMenuController"])
37 | ],
38 | targets: [
39 | .target(name: "LGSideMenuController",
40 | path: "LGSideMenuController")
41 | ]
42 | )
43 |
--------------------------------------------------------------------------------