├── .gitignore ├── .swift-version ├── .travis.yml ├── CHANGELOG.md ├── Example ├── FlowingMenu │ ├── FlowingMenu.h │ └── Info.plist ├── FlowingMenuExample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ ├── FlowingMenu.xcscheme │ │ ├── FlowingMenuTests.xcscheme │ │ └── FlowingMenuUITests.xcscheme ├── FlowingMenuExample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── avatars │ │ │ ├── 1.imageset │ │ │ │ ├── 1@2x.jpg │ │ │ │ └── Contents.json │ │ │ ├── 15.imageset │ │ │ │ ├── 15@2x.jpg │ │ │ │ └── Contents.json │ │ │ ├── 27.imageset │ │ │ │ ├── 27@2x.jpg │ │ │ │ └── Contents.json │ │ │ ├── 37.imageset │ │ │ │ ├── 37@2x.jpg │ │ │ │ └── Contents.json │ │ │ ├── 4.imageset │ │ │ │ ├── 4@2x.jpg │ │ │ │ └── Contents.json │ │ │ ├── 47.imageset │ │ │ │ ├── 47@2x.jpg │ │ │ │ └── Contents.json │ │ │ ├── 5.imageset │ │ │ │ ├── 5@2x.jpg │ │ │ │ └── Contents.json │ │ │ ├── 52.imageset │ │ │ │ ├── 52@2x.jpg │ │ │ │ └── Contents.json │ │ │ ├── 63.imageset │ │ │ │ ├── 63@2x.jpg │ │ │ │ └── Contents.json │ │ │ ├── 79.imageset │ │ │ │ ├── 79@2x.jpg │ │ │ │ └── Contents.json │ │ │ ├── 89.imageset │ │ │ │ ├── 89@2x.jpg │ │ │ │ └── Contents.json │ │ │ ├── 92.imageset │ │ │ │ ├── 92@2x.jpg │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ └── pictos │ │ │ ├── Contents.json │ │ │ ├── picto-back.imageset │ │ │ ├── Contents.json │ │ │ └── picto-back@2x.png │ │ │ ├── picto-chat.imageset │ │ │ ├── Contents.json │ │ │ └── picto-chat@2x.png │ │ │ ├── picto-hamburger.imageset │ │ │ ├── Contents.json │ │ │ └── picto-hamburger@2x.png │ │ │ └── picto-threedot.imageset │ │ │ ├── Contents.json │ │ │ └── picto-threedot@2x.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── MenuViewController.swift │ ├── ViewController.swift │ ├── cells │ │ ├── UserChatCellView.swift │ │ └── UserContactCellView.swift │ ├── model │ │ └── User.swift │ └── utils │ │ └── UIColor.swift ├── FlowingMenuTests │ ├── FlowingMenuAnimatedTransitioningTests.swift │ ├── FlowingMenuDelegateTests.swift │ ├── FlowingMenuTransitionManagerTests.swift │ ├── FlowingMenuTransitioningDelegateTests.swift │ ├── FlowingMenuUIGestureRecognizerTests.swift │ ├── Info.plist │ └── XCTTestCaseTemplate.swift └── FlowingMenuUITests │ ├── FlowingMenuUITests.swift │ └── Info.plist ├── FlowingMenu.podspec ├── LICENSE ├── Package.swift ├── README.md └── Sources ├── FlowingMenuDelegate.swift ├── FlowingMenuTransitionManager+Delegate.swift ├── FlowingMenuTransitionManager+UIGestureRecognizer.swift ├── FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift ├── FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift ├── FlowingMenuTransitionManager.swift ├── FlowingMenuTransitionStatus.swift └── UIView.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 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 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | Carthage/* 20 | *.swiftpm -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode10 3 | script: 4 | - xcodebuild -version 5 | - xcodebuild -project Example/FlowingMenuExample.xcodeproj -scheme FlowingMenuTests -sdk iphonesimulator -destination "OS=9.3,name=iPad 2" -configuration Release ONLY_ACTIVE_ARCH=YES -enableCodeCoverage YES test 6 | after_success: 7 | - bash <(curl -s https://codecov.io/bash) - cF iOS 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | 3 | ## [Version 3.1.0](https://github.com/yannickl/FlowingMenu/releases/tag/3.1.0) 4 | Released on 2018-12-09. 5 | 6 | Update to Swift 4.2 7 | 8 | ## [Version 3.0.1](https://github.com/yannickl/FlowingMenu/releases/tag/3.0.1) 9 | Released on 2018-08-08. 10 | 11 | Minor update to Swift 4.1 12 | 13 | ## [Version 3.0.0](https://github.com/yannickl/FlowingMenu/releases/tag/3.0.0) 14 | Released on 2017-10-11. 15 | 16 | **Swift 4 supports** 17 | 18 | #[Version 2.0.1](https://github.com/yannickl/FlowingMenu/releases/tag/2.0.1) 19 | Released on 2016-10-10. 20 | 21 | - Fixing the gesture priority [#9](https://github.com/yannickl/FlowingMenu/issues/9). 22 | 23 | ## [Version 2.0.0](https://github.com/yannickl/FlowingMenu/releases/tag/2.0.0) 24 | Released on 2016-09-14. 25 | 26 | **Swift 3 supports** 27 | 28 | ## [Version 1.1.0](https://github.com/yannickl/FlowingMenu/releases/tag/1.1.0) 29 | Released on 2016-09-14. 30 | 31 | **Swift 2.3 supports** 32 | 33 | ## [Version 1.0.0](https://github.com/yannickl/FlowingMenu/releases/tag/1.0.0) 34 | Released on 2016-03-01. 35 | 36 | - [ADD] Swift Package Manager support 37 | 38 | ## [Version 0.3.0](https://github.com/yannickl/FlowingMenu/releases/tag/0.3.0) 39 | Released on 2016-01-24. 40 | 41 | - [ADD] Tap to dismiss [#3](https://github.com/yannickl/FlowingMenu/issues/3) 42 | - [FIX] Bug when Slide left to close [#5](https://github.com/yannickl/FlowingMenu/issues/5) 43 | 44 | ## [Version 0.2.0](https://github.com/yannickl/FlowingMenu/releases/tag/0.2.0) 45 | Released on 2015-12-06. 46 | 47 | - [REFACTORING] Adding the `FlowingMenuTransitionStatus` class to allow animation testing 48 | - [RENAMING] Renaming `flowingMenuTransitionManagerNeedsPresentMenu:` to `flowingMenuNeedsPresentMenu:` 49 | - [RENAMING] Renaming `flowingMenuTransitionManagerNeedsDismissMenu:` to `flowingMenuNeedsDismissMenu:` 50 | - [RENAMING] Renaming `colorOfElasticShapeInFlowingMenuTransitionManager:` to `colorOfElasticShapeInFlowingMenu:` 51 | - [RENAMING] Renaming `flowingMenuTransitionManager:widthOfMenuView:` to `flowingMenu:widthOfMenuView:` 52 | 53 | ## [Version 0.1.0](https://github.com/yannickl/FlowingMenu/releases/tag/0.1.0) 54 | Released on 2015-12-04. 55 | 56 | - `flowingMenuTransitionManager:widthOfMenuView:` delegate method to get the width menu 57 | - `colorOfElasticShapeInFlowingMenuTransitionManager:` delegate method to get the color of the elastic shape 58 | - `flowingMenuTransitionManagerNeedsPresentMenu:` and `flowingMenuTransitionManagerNeedsDismissMenu:` delegate methods to respond to interactive event 59 | - `setInteractivePresentationView:` and `setInteractiveDismissView:` methods define views attached with gestures 60 | - Cocoapods support 61 | - Carthage support 62 | -------------------------------------------------------------------------------- /Example/FlowingMenu/FlowingMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // FlowingMenu.h 3 | // FlowingMenu 4 | // 5 | // Created by Yannick LORIOT on 04/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for FlowingMenu. 12 | FOUNDATION_EXPORT double FlowingMenuVersionNumber; 13 | 14 | //! Project version string for FlowingMenu. 15 | FOUNDATION_EXPORT const unsigned char FlowingMenuVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Example/FlowingMenu/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 | 3.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | CEACBD2C1C8635BC00193E0E /* FlowingMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD241C8635BC00193E0E /* FlowingMenuDelegate.swift */; }; 11 | CEACBD2D1C8635BC00193E0E /* FlowingMenuTransitionManager+Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD251C8635BC00193E0E /* FlowingMenuTransitionManager+Delegate.swift */; }; 12 | CEACBD2E1C8635BC00193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD261C8635BC00193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift */; }; 13 | CEACBD2F1C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD271C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift */; }; 14 | CEACBD301C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD281C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift */; }; 15 | CEACBD311C8635BC00193E0E /* FlowingMenuTransitionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD291C8635BC00193E0E /* FlowingMenuTransitionManager.swift */; }; 16 | CEACBD321C8635BC00193E0E /* FlowingMenuTransitionStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD2A1C8635BC00193E0E /* FlowingMenuTransitionStatus.swift */; }; 17 | CEACBD331C8635BC00193E0E /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD2B1C8635BC00193E0E /* UIView.swift */; }; 18 | CEACBD341C8635C500193E0E /* FlowingMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD241C8635BC00193E0E /* FlowingMenuDelegate.swift */; }; 19 | CEACBD351C8635C500193E0E /* FlowingMenuTransitionManager+Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD251C8635BC00193E0E /* FlowingMenuTransitionManager+Delegate.swift */; }; 20 | CEACBD361C8635C500193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD261C8635BC00193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift */; }; 21 | CEACBD371C8635C500193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD271C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift */; }; 22 | CEACBD381C8635C500193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD281C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift */; }; 23 | CEACBD391C8635C500193E0E /* FlowingMenuTransitionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD291C8635BC00193E0E /* FlowingMenuTransitionManager.swift */; }; 24 | CEACBD3A1C8635C500193E0E /* FlowingMenuTransitionStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD2A1C8635BC00193E0E /* FlowingMenuTransitionStatus.swift */; }; 25 | CEACBD3B1C8635C500193E0E /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD2B1C8635BC00193E0E /* UIView.swift */; }; 26 | CEACBD3C1C8635C600193E0E /* FlowingMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD241C8635BC00193E0E /* FlowingMenuDelegate.swift */; }; 27 | CEACBD3D1C8635C600193E0E /* FlowingMenuTransitionManager+Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD251C8635BC00193E0E /* FlowingMenuTransitionManager+Delegate.swift */; }; 28 | CEACBD3E1C8635C600193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD261C8635BC00193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift */; }; 29 | CEACBD3F1C8635C600193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD271C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift */; }; 30 | CEACBD401C8635C600193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD281C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift */; }; 31 | CEACBD411C8635C600193E0E /* FlowingMenuTransitionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD291C8635BC00193E0E /* FlowingMenuTransitionManager.swift */; }; 32 | CEACBD421C8635C600193E0E /* FlowingMenuTransitionStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD2A1C8635BC00193E0E /* FlowingMenuTransitionStatus.swift */; }; 33 | CEACBD431C8635C600193E0E /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD2B1C8635BC00193E0E /* UIView.swift */; }; 34 | CEACBD441C8635C700193E0E /* FlowingMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD241C8635BC00193E0E /* FlowingMenuDelegate.swift */; }; 35 | CEACBD451C8635C700193E0E /* FlowingMenuTransitionManager+Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD251C8635BC00193E0E /* FlowingMenuTransitionManager+Delegate.swift */; }; 36 | CEACBD461C8635C700193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD261C8635BC00193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift */; }; 37 | CEACBD471C8635C700193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD271C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift */; }; 38 | CEACBD481C8635C700193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD281C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift */; }; 39 | CEACBD491C8635C700193E0E /* FlowingMenuTransitionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD291C8635BC00193E0E /* FlowingMenuTransitionManager.swift */; }; 40 | CEACBD4A1C8635C700193E0E /* FlowingMenuTransitionStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD2A1C8635BC00193E0E /* FlowingMenuTransitionStatus.swift */; }; 41 | CEACBD4B1C8635C700193E0E /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEACBD2B1C8635BC00193E0E /* UIView.swift */; }; 42 | CECF53C61C0F86DD00DF6B92 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF53C51C0F86DD00DF6B92 /* AppDelegate.swift */; }; 43 | CECF53C81C0F86DD00DF6B92 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF53C71C0F86DD00DF6B92 /* ViewController.swift */; }; 44 | CECF53CB1C0F86DD00DF6B92 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CECF53C91C0F86DD00DF6B92 /* Main.storyboard */; }; 45 | CECF53CD1C0F86DD00DF6B92 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CECF53CC1C0F86DD00DF6B92 /* Assets.xcassets */; }; 46 | CECF53D01C0F86DD00DF6B92 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CECF53CE1C0F86DD00DF6B92 /* LaunchScreen.storyboard */; }; 47 | CECF53E01C0F92D400DF6B92 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF53DF1C0F92D400DF6B92 /* User.swift */; }; 48 | CECF53E51C10403D00DF6B92 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF53E41C10403D00DF6B92 /* UIColor.swift */; }; 49 | CECF53EA1C10469700DF6B92 /* UserContactCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF53E91C10469700DF6B92 /* UserContactCellView.swift */; }; 50 | CECF53F01C10595E00DF6B92 /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF53EF1C10595E00DF6B92 /* MenuViewController.swift */; }; 51 | CECF53F21C10886B00DF6B92 /* UserChatCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF53F11C10886B00DF6B92 /* UserChatCellView.swift */; }; 52 | CECF54061C11A6C600DF6B92 /* FlowingMenuUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF54051C11A6C600DF6B92 /* FlowingMenuUITests.swift */; }; 53 | CECF54141C11AB2D00DF6B92 /* FlowingMenuUIGestureRecognizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF54131C11AB2D00DF6B92 /* FlowingMenuUIGestureRecognizerTests.swift */; }; 54 | CECF541C1C11AB8F00DF6B92 /* XCTTestCaseTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF541B1C11AB8F00DF6B92 /* XCTTestCaseTemplate.swift */; }; 55 | CECF542C1C11B39400DF6B92 /* FlowingMenuTransitionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF542B1C11B39400DF6B92 /* FlowingMenuTransitionManagerTests.swift */; }; 56 | CECF54351C11B5C400DF6B92 /* FlowingMenu.h in Headers */ = {isa = PBXBuildFile; fileRef = CECF54341C11B5C400DF6B92 /* FlowingMenu.h */; settings = {ATTRIBUTES = (Public, ); }; }; 57 | CECF54391C11B5C400DF6B92 /* FlowingMenu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CECF54321C11B5C400DF6B92 /* FlowingMenu.framework */; }; 58 | CECF543A1C11B5C400DF6B92 /* FlowingMenu.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CECF54321C11B5C400DF6B92 /* FlowingMenu.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 59 | CECF54471C11C43900DF6B92 /* FlowingMenuTransitioningDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF54461C11C43900DF6B92 /* FlowingMenuTransitioningDelegateTests.swift */; }; 60 | CECF54491C11C68700DF6B92 /* FlowingMenuAnimatedTransitioningTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF54481C11C68700DF6B92 /* FlowingMenuAnimatedTransitioningTests.swift */; }; 61 | CECF544B1C11C83600DF6B92 /* FlowingMenuDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECF544A1C11C83600DF6B92 /* FlowingMenuDelegateTests.swift */; }; 62 | /* End PBXBuildFile section */ 63 | 64 | /* Begin PBXContainerItemProxy section */ 65 | CECF54081C11A6C600DF6B92 /* PBXContainerItemProxy */ = { 66 | isa = PBXContainerItemProxy; 67 | containerPortal = CECF53BA1C0F86DD00DF6B92 /* Project object */; 68 | proxyType = 1; 69 | remoteGlobalIDString = CECF53C11C0F86DD00DF6B92; 70 | remoteInfo = FlowingMenuExample; 71 | }; 72 | CECF54161C11AB2D00DF6B92 /* PBXContainerItemProxy */ = { 73 | isa = PBXContainerItemProxy; 74 | containerPortal = CECF53BA1C0F86DD00DF6B92 /* Project object */; 75 | proxyType = 1; 76 | remoteGlobalIDString = CECF53C11C0F86DD00DF6B92; 77 | remoteInfo = FlowingMenuExample; 78 | }; 79 | CECF54371C11B5C400DF6B92 /* PBXContainerItemProxy */ = { 80 | isa = PBXContainerItemProxy; 81 | containerPortal = CECF53BA1C0F86DD00DF6B92 /* Project object */; 82 | proxyType = 1; 83 | remoteGlobalIDString = CECF54311C11B5C400DF6B92; 84 | remoteInfo = FlowingMenu; 85 | }; 86 | /* End PBXContainerItemProxy section */ 87 | 88 | /* Begin PBXCopyFilesBuildPhase section */ 89 | CECF543E1C11B5C400DF6B92 /* Embed Frameworks */ = { 90 | isa = PBXCopyFilesBuildPhase; 91 | buildActionMask = 2147483647; 92 | dstPath = ""; 93 | dstSubfolderSpec = 10; 94 | files = ( 95 | CECF543A1C11B5C400DF6B92 /* FlowingMenu.framework in Embed Frameworks */, 96 | ); 97 | name = "Embed Frameworks"; 98 | runOnlyForDeploymentPostprocessing = 0; 99 | }; 100 | /* End PBXCopyFilesBuildPhase section */ 101 | 102 | /* Begin PBXFileReference section */ 103 | CEACBD241C8635BC00193E0E /* FlowingMenuDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowingMenuDelegate.swift; sourceTree = ""; }; 104 | CEACBD251C8635BC00193E0E /* FlowingMenuTransitionManager+Delegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FlowingMenuTransitionManager+Delegate.swift"; sourceTree = ""; }; 105 | CEACBD261C8635BC00193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FlowingMenuTransitionManager+UIGestureRecognizer.swift"; sourceTree = ""; }; 106 | CEACBD271C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift"; sourceTree = ""; }; 107 | CEACBD281C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift"; sourceTree = ""; }; 108 | CEACBD291C8635BC00193E0E /* FlowingMenuTransitionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowingMenuTransitionManager.swift; sourceTree = ""; }; 109 | CEACBD2A1C8635BC00193E0E /* FlowingMenuTransitionStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowingMenuTransitionStatus.swift; sourceTree = ""; }; 110 | CEACBD2B1C8635BC00193E0E /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; 111 | CECF53C21C0F86DD00DF6B92 /* FlowingMenuExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FlowingMenuExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 112 | CECF53C51C0F86DD00DF6B92 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 113 | CECF53C71C0F86DD00DF6B92 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 114 | CECF53CA1C0F86DD00DF6B92 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 115 | CECF53CC1C0F86DD00DF6B92 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 116 | CECF53CF1C0F86DD00DF6B92 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 117 | CECF53D11C0F86DD00DF6B92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 118 | CECF53DF1C0F92D400DF6B92 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 119 | CECF53E41C10403D00DF6B92 /* UIColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; 120 | CECF53E91C10469700DF6B92 /* UserContactCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserContactCellView.swift; sourceTree = ""; }; 121 | CECF53EF1C10595E00DF6B92 /* MenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = ""; }; 122 | CECF53F11C10886B00DF6B92 /* UserChatCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserChatCellView.swift; sourceTree = ""; }; 123 | CECF54031C11A6C600DF6B92 /* FlowingMenuUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FlowingMenuUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 124 | CECF54051C11A6C600DF6B92 /* FlowingMenuUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowingMenuUITests.swift; sourceTree = ""; }; 125 | CECF54071C11A6C600DF6B92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 126 | CECF54111C11AB2D00DF6B92 /* FlowingMenuTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FlowingMenuTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 127 | CECF54131C11AB2D00DF6B92 /* FlowingMenuUIGestureRecognizerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowingMenuUIGestureRecognizerTests.swift; sourceTree = ""; }; 128 | CECF54151C11AB2D00DF6B92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 129 | CECF541B1C11AB8F00DF6B92 /* XCTTestCaseTemplate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCTTestCaseTemplate.swift; sourceTree = ""; }; 130 | CECF542B1C11B39400DF6B92 /* FlowingMenuTransitionManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowingMenuTransitionManagerTests.swift; sourceTree = ""; }; 131 | CECF54321C11B5C400DF6B92 /* FlowingMenu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FlowingMenu.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 132 | CECF54341C11B5C400DF6B92 /* FlowingMenu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FlowingMenu.h; sourceTree = ""; }; 133 | CECF54361C11B5C400DF6B92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 134 | CECF54461C11C43900DF6B92 /* FlowingMenuTransitioningDelegateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowingMenuTransitioningDelegateTests.swift; sourceTree = ""; }; 135 | CECF54481C11C68700DF6B92 /* FlowingMenuAnimatedTransitioningTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowingMenuAnimatedTransitioningTests.swift; sourceTree = ""; }; 136 | CECF544A1C11C83600DF6B92 /* FlowingMenuDelegateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowingMenuDelegateTests.swift; sourceTree = ""; }; 137 | /* End PBXFileReference section */ 138 | 139 | /* Begin PBXFrameworksBuildPhase section */ 140 | CECF53BF1C0F86DD00DF6B92 /* Frameworks */ = { 141 | isa = PBXFrameworksBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | CECF54391C11B5C400DF6B92 /* FlowingMenu.framework in Frameworks */, 145 | ); 146 | runOnlyForDeploymentPostprocessing = 0; 147 | }; 148 | CECF54001C11A6C600DF6B92 /* Frameworks */ = { 149 | isa = PBXFrameworksBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | ); 153 | runOnlyForDeploymentPostprocessing = 0; 154 | }; 155 | CECF540E1C11AB2D00DF6B92 /* Frameworks */ = { 156 | isa = PBXFrameworksBuildPhase; 157 | buildActionMask = 2147483647; 158 | files = ( 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | CECF542E1C11B5C400DF6B92 /* Frameworks */ = { 163 | isa = PBXFrameworksBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXFrameworksBuildPhase section */ 170 | 171 | /* Begin PBXGroup section */ 172 | CEACBD231C8635BC00193E0E /* Sources */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | CEACBD241C8635BC00193E0E /* FlowingMenuDelegate.swift */, 176 | CEACBD251C8635BC00193E0E /* FlowingMenuTransitionManager+Delegate.swift */, 177 | CEACBD261C8635BC00193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift */, 178 | CEACBD271C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift */, 179 | CEACBD281C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift */, 180 | CEACBD291C8635BC00193E0E /* FlowingMenuTransitionManager.swift */, 181 | CEACBD2A1C8635BC00193E0E /* FlowingMenuTransitionStatus.swift */, 182 | CEACBD2B1C8635BC00193E0E /* UIView.swift */, 183 | ); 184 | name = Sources; 185 | path = ../Sources; 186 | sourceTree = ""; 187 | }; 188 | CECF53B91C0F86DD00DF6B92 = { 189 | isa = PBXGroup; 190 | children = ( 191 | CEACBD231C8635BC00193E0E /* Sources */, 192 | CECF53C41C0F86DD00DF6B92 /* FlowingMenuExample */, 193 | CECF54121C11AB2D00DF6B92 /* FlowingMenuTests */, 194 | CECF54041C11A6C600DF6B92 /* FlowingMenuUITests */, 195 | CECF54331C11B5C400DF6B92 /* FlowingMenu */, 196 | CECF53C31C0F86DD00DF6B92 /* Products */, 197 | ); 198 | sourceTree = ""; 199 | }; 200 | CECF53C31C0F86DD00DF6B92 /* Products */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | CECF53C21C0F86DD00DF6B92 /* FlowingMenuExample.app */, 204 | CECF54031C11A6C600DF6B92 /* FlowingMenuUITests.xctest */, 205 | CECF54111C11AB2D00DF6B92 /* FlowingMenuTests.xctest */, 206 | CECF54321C11B5C400DF6B92 /* FlowingMenu.framework */, 207 | ); 208 | name = Products; 209 | sourceTree = ""; 210 | }; 211 | CECF53C41C0F86DD00DF6B92 /* FlowingMenuExample */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | CECF53E81C10469700DF6B92 /* cells */, 215 | CECF53DE1C0F92D400DF6B92 /* model */, 216 | CECF53E31C10403D00DF6B92 /* utils */, 217 | CECF53C51C0F86DD00DF6B92 /* AppDelegate.swift */, 218 | CECF53C71C0F86DD00DF6B92 /* ViewController.swift */, 219 | CECF53EF1C10595E00DF6B92 /* MenuViewController.swift */, 220 | CECF53C91C0F86DD00DF6B92 /* Main.storyboard */, 221 | CECF53CC1C0F86DD00DF6B92 /* Assets.xcassets */, 222 | CECF53CE1C0F86DD00DF6B92 /* LaunchScreen.storyboard */, 223 | CECF53D11C0F86DD00DF6B92 /* Info.plist */, 224 | ); 225 | path = FlowingMenuExample; 226 | sourceTree = ""; 227 | }; 228 | CECF53DE1C0F92D400DF6B92 /* model */ = { 229 | isa = PBXGroup; 230 | children = ( 231 | CECF53DF1C0F92D400DF6B92 /* User.swift */, 232 | ); 233 | path = model; 234 | sourceTree = ""; 235 | }; 236 | CECF53E31C10403D00DF6B92 /* utils */ = { 237 | isa = PBXGroup; 238 | children = ( 239 | CECF53E41C10403D00DF6B92 /* UIColor.swift */, 240 | ); 241 | path = utils; 242 | sourceTree = ""; 243 | }; 244 | CECF53E81C10469700DF6B92 /* cells */ = { 245 | isa = PBXGroup; 246 | children = ( 247 | CECF53E91C10469700DF6B92 /* UserContactCellView.swift */, 248 | CECF53F11C10886B00DF6B92 /* UserChatCellView.swift */, 249 | ); 250 | path = cells; 251 | sourceTree = ""; 252 | }; 253 | CECF54041C11A6C600DF6B92 /* FlowingMenuUITests */ = { 254 | isa = PBXGroup; 255 | children = ( 256 | CECF54051C11A6C600DF6B92 /* FlowingMenuUITests.swift */, 257 | CECF54071C11A6C600DF6B92 /* Info.plist */, 258 | ); 259 | path = FlowingMenuUITests; 260 | sourceTree = ""; 261 | }; 262 | CECF54121C11AB2D00DF6B92 /* FlowingMenuTests */ = { 263 | isa = PBXGroup; 264 | children = ( 265 | CECF541B1C11AB8F00DF6B92 /* XCTTestCaseTemplate.swift */, 266 | CECF542B1C11B39400DF6B92 /* FlowingMenuTransitionManagerTests.swift */, 267 | CECF54131C11AB2D00DF6B92 /* FlowingMenuUIGestureRecognizerTests.swift */, 268 | CECF54461C11C43900DF6B92 /* FlowingMenuTransitioningDelegateTests.swift */, 269 | CECF54481C11C68700DF6B92 /* FlowingMenuAnimatedTransitioningTests.swift */, 270 | CECF544A1C11C83600DF6B92 /* FlowingMenuDelegateTests.swift */, 271 | CECF54151C11AB2D00DF6B92 /* Info.plist */, 272 | ); 273 | path = FlowingMenuTests; 274 | sourceTree = ""; 275 | }; 276 | CECF54331C11B5C400DF6B92 /* FlowingMenu */ = { 277 | isa = PBXGroup; 278 | children = ( 279 | CECF54341C11B5C400DF6B92 /* FlowingMenu.h */, 280 | CECF54361C11B5C400DF6B92 /* Info.plist */, 281 | ); 282 | path = FlowingMenu; 283 | sourceTree = ""; 284 | }; 285 | /* End PBXGroup section */ 286 | 287 | /* Begin PBXHeadersBuildPhase section */ 288 | CECF542F1C11B5C400DF6B92 /* Headers */ = { 289 | isa = PBXHeadersBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | CECF54351C11B5C400DF6B92 /* FlowingMenu.h in Headers */, 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | /* End PBXHeadersBuildPhase section */ 297 | 298 | /* Begin PBXNativeTarget section */ 299 | CECF53C11C0F86DD00DF6B92 /* FlowingMenuExample */ = { 300 | isa = PBXNativeTarget; 301 | buildConfigurationList = CECF53D41C0F86DD00DF6B92 /* Build configuration list for PBXNativeTarget "FlowingMenuExample" */; 302 | buildPhases = ( 303 | CECF53BE1C0F86DD00DF6B92 /* Sources */, 304 | CECF53BF1C0F86DD00DF6B92 /* Frameworks */, 305 | CECF53C01C0F86DD00DF6B92 /* Resources */, 306 | CECF543E1C11B5C400DF6B92 /* Embed Frameworks */, 307 | ); 308 | buildRules = ( 309 | ); 310 | dependencies = ( 311 | CECF54381C11B5C400DF6B92 /* PBXTargetDependency */, 312 | ); 313 | name = FlowingMenuExample; 314 | productName = FlowingMenuExample; 315 | productReference = CECF53C21C0F86DD00DF6B92 /* FlowingMenuExample.app */; 316 | productType = "com.apple.product-type.application"; 317 | }; 318 | CECF54021C11A6C600DF6B92 /* FlowingMenuUITests */ = { 319 | isa = PBXNativeTarget; 320 | buildConfigurationList = CECF540A1C11A6C600DF6B92 /* Build configuration list for PBXNativeTarget "FlowingMenuUITests" */; 321 | buildPhases = ( 322 | CECF53FF1C11A6C600DF6B92 /* Sources */, 323 | CECF54001C11A6C600DF6B92 /* Frameworks */, 324 | CECF54011C11A6C600DF6B92 /* Resources */, 325 | ); 326 | buildRules = ( 327 | ); 328 | dependencies = ( 329 | CECF54091C11A6C600DF6B92 /* PBXTargetDependency */, 330 | ); 331 | name = FlowingMenuUITests; 332 | productName = FlowingMenuUITests; 333 | productReference = CECF54031C11A6C600DF6B92 /* FlowingMenuUITests.xctest */; 334 | productType = "com.apple.product-type.bundle.ui-testing"; 335 | }; 336 | CECF54101C11AB2D00DF6B92 /* FlowingMenuTests */ = { 337 | isa = PBXNativeTarget; 338 | buildConfigurationList = CECF54181C11AB2D00DF6B92 /* Build configuration list for PBXNativeTarget "FlowingMenuTests" */; 339 | buildPhases = ( 340 | CECF540D1C11AB2D00DF6B92 /* Sources */, 341 | CECF540E1C11AB2D00DF6B92 /* Frameworks */, 342 | CECF540F1C11AB2D00DF6B92 /* Resources */, 343 | ); 344 | buildRules = ( 345 | ); 346 | dependencies = ( 347 | CECF54171C11AB2D00DF6B92 /* PBXTargetDependency */, 348 | ); 349 | name = FlowingMenuTests; 350 | productName = FlowingMenuTests; 351 | productReference = CECF54111C11AB2D00DF6B92 /* FlowingMenuTests.xctest */; 352 | productType = "com.apple.product-type.bundle.unit-test"; 353 | }; 354 | CECF54311C11B5C400DF6B92 /* FlowingMenu */ = { 355 | isa = PBXNativeTarget; 356 | buildConfigurationList = CECF543B1C11B5C400DF6B92 /* Build configuration list for PBXNativeTarget "FlowingMenu" */; 357 | buildPhases = ( 358 | CECF542D1C11B5C400DF6B92 /* Sources */, 359 | CECF542E1C11B5C400DF6B92 /* Frameworks */, 360 | CECF542F1C11B5C400DF6B92 /* Headers */, 361 | CECF54301C11B5C400DF6B92 /* Resources */, 362 | ); 363 | buildRules = ( 364 | ); 365 | dependencies = ( 366 | ); 367 | name = FlowingMenu; 368 | productName = FlowingMenu; 369 | productReference = CECF54321C11B5C400DF6B92 /* FlowingMenu.framework */; 370 | productType = "com.apple.product-type.framework"; 371 | }; 372 | /* End PBXNativeTarget section */ 373 | 374 | /* Begin PBXProject section */ 375 | CECF53BA1C0F86DD00DF6B92 /* Project object */ = { 376 | isa = PBXProject; 377 | attributes = { 378 | LastSwiftUpdateCheck = 0710; 379 | LastUpgradeCheck = 0940; 380 | ORGANIZATIONNAME = "Yannick LORIOT"; 381 | TargetAttributes = { 382 | CECF53C11C0F86DD00DF6B92 = { 383 | CreatedOnToolsVersion = 7.1.1; 384 | LastSwiftMigration = 0900; 385 | }; 386 | CECF54021C11A6C600DF6B92 = { 387 | CreatedOnToolsVersion = 7.1.1; 388 | TestTargetID = CECF53C11C0F86DD00DF6B92; 389 | }; 390 | CECF54101C11AB2D00DF6B92 = { 391 | CreatedOnToolsVersion = 7.1.1; 392 | TestTargetID = CECF53C11C0F86DD00DF6B92; 393 | }; 394 | CECF54311C11B5C400DF6B92 = { 395 | CreatedOnToolsVersion = 7.1.1; 396 | LastSwiftMigration = 0900; 397 | }; 398 | }; 399 | }; 400 | buildConfigurationList = CECF53BD1C0F86DD00DF6B92 /* Build configuration list for PBXProject "FlowingMenuExample" */; 401 | compatibilityVersion = "Xcode 3.2"; 402 | developmentRegion = English; 403 | hasScannedForEncodings = 0; 404 | knownRegions = ( 405 | en, 406 | Base, 407 | ); 408 | mainGroup = CECF53B91C0F86DD00DF6B92; 409 | productRefGroup = CECF53C31C0F86DD00DF6B92 /* Products */; 410 | projectDirPath = ""; 411 | projectRoot = ""; 412 | targets = ( 413 | CECF53C11C0F86DD00DF6B92 /* FlowingMenuExample */, 414 | CECF54311C11B5C400DF6B92 /* FlowingMenu */, 415 | CECF54101C11AB2D00DF6B92 /* FlowingMenuTests */, 416 | CECF54021C11A6C600DF6B92 /* FlowingMenuUITests */, 417 | ); 418 | }; 419 | /* End PBXProject section */ 420 | 421 | /* Begin PBXResourcesBuildPhase section */ 422 | CECF53C01C0F86DD00DF6B92 /* Resources */ = { 423 | isa = PBXResourcesBuildPhase; 424 | buildActionMask = 2147483647; 425 | files = ( 426 | CECF53D01C0F86DD00DF6B92 /* LaunchScreen.storyboard in Resources */, 427 | CECF53CD1C0F86DD00DF6B92 /* Assets.xcassets in Resources */, 428 | CECF53CB1C0F86DD00DF6B92 /* Main.storyboard in Resources */, 429 | ); 430 | runOnlyForDeploymentPostprocessing = 0; 431 | }; 432 | CECF54011C11A6C600DF6B92 /* Resources */ = { 433 | isa = PBXResourcesBuildPhase; 434 | buildActionMask = 2147483647; 435 | files = ( 436 | ); 437 | runOnlyForDeploymentPostprocessing = 0; 438 | }; 439 | CECF540F1C11AB2D00DF6B92 /* Resources */ = { 440 | isa = PBXResourcesBuildPhase; 441 | buildActionMask = 2147483647; 442 | files = ( 443 | ); 444 | runOnlyForDeploymentPostprocessing = 0; 445 | }; 446 | CECF54301C11B5C400DF6B92 /* Resources */ = { 447 | isa = PBXResourcesBuildPhase; 448 | buildActionMask = 2147483647; 449 | files = ( 450 | ); 451 | runOnlyForDeploymentPostprocessing = 0; 452 | }; 453 | /* End PBXResourcesBuildPhase section */ 454 | 455 | /* Begin PBXSourcesBuildPhase section */ 456 | CECF53BE1C0F86DD00DF6B92 /* Sources */ = { 457 | isa = PBXSourcesBuildPhase; 458 | buildActionMask = 2147483647; 459 | files = ( 460 | CECF53F21C10886B00DF6B92 /* UserChatCellView.swift in Sources */, 461 | CECF53E01C0F92D400DF6B92 /* User.swift in Sources */, 462 | CEACBD321C8635BC00193E0E /* FlowingMenuTransitionStatus.swift in Sources */, 463 | CEACBD2F1C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift in Sources */, 464 | CEACBD2C1C8635BC00193E0E /* FlowingMenuDelegate.swift in Sources */, 465 | CEACBD301C8635BC00193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift in Sources */, 466 | CECF53C81C0F86DD00DF6B92 /* ViewController.swift in Sources */, 467 | CECF53EA1C10469700DF6B92 /* UserContactCellView.swift in Sources */, 468 | CECF53F01C10595E00DF6B92 /* MenuViewController.swift in Sources */, 469 | CEACBD2D1C8635BC00193E0E /* FlowingMenuTransitionManager+Delegate.swift in Sources */, 470 | CEACBD331C8635BC00193E0E /* UIView.swift in Sources */, 471 | CECF53C61C0F86DD00DF6B92 /* AppDelegate.swift in Sources */, 472 | CEACBD2E1C8635BC00193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift in Sources */, 473 | CEACBD311C8635BC00193E0E /* FlowingMenuTransitionManager.swift in Sources */, 474 | CECF53E51C10403D00DF6B92 /* UIColor.swift in Sources */, 475 | ); 476 | runOnlyForDeploymentPostprocessing = 0; 477 | }; 478 | CECF53FF1C11A6C600DF6B92 /* Sources */ = { 479 | isa = PBXSourcesBuildPhase; 480 | buildActionMask = 2147483647; 481 | files = ( 482 | CEACBD471C8635C700193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift in Sources */, 483 | CEACBD451C8635C700193E0E /* FlowingMenuTransitionManager+Delegate.swift in Sources */, 484 | CEACBD4A1C8635C700193E0E /* FlowingMenuTransitionStatus.swift in Sources */, 485 | CEACBD441C8635C700193E0E /* FlowingMenuDelegate.swift in Sources */, 486 | CEACBD461C8635C700193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift in Sources */, 487 | CEACBD491C8635C700193E0E /* FlowingMenuTransitionManager.swift in Sources */, 488 | CEACBD481C8635C700193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift in Sources */, 489 | CEACBD4B1C8635C700193E0E /* UIView.swift in Sources */, 490 | CECF54061C11A6C600DF6B92 /* FlowingMenuUITests.swift in Sources */, 491 | ); 492 | runOnlyForDeploymentPostprocessing = 0; 493 | }; 494 | CECF540D1C11AB2D00DF6B92 /* Sources */ = { 495 | isa = PBXSourcesBuildPhase; 496 | buildActionMask = 2147483647; 497 | files = ( 498 | CEACBD3F1C8635C600193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift in Sources */, 499 | CEACBD421C8635C600193E0E /* FlowingMenuTransitionStatus.swift in Sources */, 500 | CECF54471C11C43900DF6B92 /* FlowingMenuTransitioningDelegateTests.swift in Sources */, 501 | CEACBD3E1C8635C600193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift in Sources */, 502 | CEACBD3C1C8635C600193E0E /* FlowingMenuDelegate.swift in Sources */, 503 | CECF54141C11AB2D00DF6B92 /* FlowingMenuUIGestureRecognizerTests.swift in Sources */, 504 | CECF544B1C11C83600DF6B92 /* FlowingMenuDelegateTests.swift in Sources */, 505 | CEACBD411C8635C600193E0E /* FlowingMenuTransitionManager.swift in Sources */, 506 | CECF542C1C11B39400DF6B92 /* FlowingMenuTransitionManagerTests.swift in Sources */, 507 | CEACBD3D1C8635C600193E0E /* FlowingMenuTransitionManager+Delegate.swift in Sources */, 508 | CEACBD431C8635C600193E0E /* UIView.swift in Sources */, 509 | CEACBD401C8635C600193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift in Sources */, 510 | CECF541C1C11AB8F00DF6B92 /* XCTTestCaseTemplate.swift in Sources */, 511 | CECF54491C11C68700DF6B92 /* FlowingMenuAnimatedTransitioningTests.swift in Sources */, 512 | ); 513 | runOnlyForDeploymentPostprocessing = 0; 514 | }; 515 | CECF542D1C11B5C400DF6B92 /* Sources */ = { 516 | isa = PBXSourcesBuildPhase; 517 | buildActionMask = 2147483647; 518 | files = ( 519 | CEACBD3B1C8635C500193E0E /* UIView.swift in Sources */, 520 | CEACBD391C8635C500193E0E /* FlowingMenuTransitionManager.swift in Sources */, 521 | CEACBD371C8635C500193E0E /* FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift in Sources */, 522 | CEACBD3A1C8635C500193E0E /* FlowingMenuTransitionStatus.swift in Sources */, 523 | CEACBD341C8635C500193E0E /* FlowingMenuDelegate.swift in Sources */, 524 | CEACBD381C8635C500193E0E /* FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift in Sources */, 525 | CEACBD361C8635C500193E0E /* FlowingMenuTransitionManager+UIGestureRecognizer.swift in Sources */, 526 | CEACBD351C8635C500193E0E /* FlowingMenuTransitionManager+Delegate.swift in Sources */, 527 | ); 528 | runOnlyForDeploymentPostprocessing = 0; 529 | }; 530 | /* End PBXSourcesBuildPhase section */ 531 | 532 | /* Begin PBXTargetDependency section */ 533 | CECF54091C11A6C600DF6B92 /* PBXTargetDependency */ = { 534 | isa = PBXTargetDependency; 535 | target = CECF53C11C0F86DD00DF6B92 /* FlowingMenuExample */; 536 | targetProxy = CECF54081C11A6C600DF6B92 /* PBXContainerItemProxy */; 537 | }; 538 | CECF54171C11AB2D00DF6B92 /* PBXTargetDependency */ = { 539 | isa = PBXTargetDependency; 540 | target = CECF53C11C0F86DD00DF6B92 /* FlowingMenuExample */; 541 | targetProxy = CECF54161C11AB2D00DF6B92 /* PBXContainerItemProxy */; 542 | }; 543 | CECF54381C11B5C400DF6B92 /* PBXTargetDependency */ = { 544 | isa = PBXTargetDependency; 545 | target = CECF54311C11B5C400DF6B92 /* FlowingMenu */; 546 | targetProxy = CECF54371C11B5C400DF6B92 /* PBXContainerItemProxy */; 547 | }; 548 | /* End PBXTargetDependency section */ 549 | 550 | /* Begin PBXVariantGroup section */ 551 | CECF53C91C0F86DD00DF6B92 /* Main.storyboard */ = { 552 | isa = PBXVariantGroup; 553 | children = ( 554 | CECF53CA1C0F86DD00DF6B92 /* Base */, 555 | ); 556 | name = Main.storyboard; 557 | sourceTree = ""; 558 | }; 559 | CECF53CE1C0F86DD00DF6B92 /* LaunchScreen.storyboard */ = { 560 | isa = PBXVariantGroup; 561 | children = ( 562 | CECF53CF1C0F86DD00DF6B92 /* Base */, 563 | ); 564 | name = LaunchScreen.storyboard; 565 | sourceTree = ""; 566 | }; 567 | /* End PBXVariantGroup section */ 568 | 569 | /* Begin XCBuildConfiguration section */ 570 | CECF53D21C0F86DD00DF6B92 /* Debug */ = { 571 | isa = XCBuildConfiguration; 572 | buildSettings = { 573 | ALWAYS_SEARCH_USER_PATHS = NO; 574 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 575 | CLANG_CXX_LIBRARY = "libc++"; 576 | CLANG_ENABLE_MODULES = YES; 577 | CLANG_ENABLE_OBJC_ARC = YES; 578 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 579 | CLANG_WARN_BOOL_CONVERSION = YES; 580 | CLANG_WARN_COMMA = YES; 581 | CLANG_WARN_CONSTANT_CONVERSION = YES; 582 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 583 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 584 | CLANG_WARN_EMPTY_BODY = YES; 585 | CLANG_WARN_ENUM_CONVERSION = YES; 586 | CLANG_WARN_INFINITE_RECURSION = YES; 587 | CLANG_WARN_INT_CONVERSION = YES; 588 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 589 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 590 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 591 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 592 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 593 | CLANG_WARN_STRICT_PROTOTYPES = YES; 594 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 595 | CLANG_WARN_UNREACHABLE_CODE = YES; 596 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 597 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 598 | COPY_PHASE_STRIP = NO; 599 | DEBUG_INFORMATION_FORMAT = dwarf; 600 | ENABLE_STRICT_OBJC_MSGSEND = YES; 601 | ENABLE_TESTABILITY = YES; 602 | GCC_C_LANGUAGE_STANDARD = gnu99; 603 | GCC_DYNAMIC_NO_PIC = NO; 604 | GCC_NO_COMMON_BLOCKS = YES; 605 | GCC_OPTIMIZATION_LEVEL = 0; 606 | GCC_PREPROCESSOR_DEFINITIONS = ( 607 | "DEBUG=1", 608 | "$(inherited)", 609 | ); 610 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 611 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 612 | GCC_WARN_UNDECLARED_SELECTOR = YES; 613 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 614 | GCC_WARN_UNUSED_FUNCTION = YES; 615 | GCC_WARN_UNUSED_VARIABLE = YES; 616 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 617 | MTL_ENABLE_DEBUG_INFO = YES; 618 | ONLY_ACTIVE_ARCH = YES; 619 | SDKROOT = iphoneos; 620 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 621 | SWIFT_VERSION = 4.2; 622 | TARGETED_DEVICE_FAMILY = "1,2"; 623 | }; 624 | name = Debug; 625 | }; 626 | CECF53D31C0F86DD00DF6B92 /* Release */ = { 627 | isa = XCBuildConfiguration; 628 | buildSettings = { 629 | ALWAYS_SEARCH_USER_PATHS = NO; 630 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 631 | CLANG_CXX_LIBRARY = "libc++"; 632 | CLANG_ENABLE_MODULES = YES; 633 | CLANG_ENABLE_OBJC_ARC = YES; 634 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 635 | CLANG_WARN_BOOL_CONVERSION = YES; 636 | CLANG_WARN_COMMA = YES; 637 | CLANG_WARN_CONSTANT_CONVERSION = YES; 638 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 639 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 640 | CLANG_WARN_EMPTY_BODY = YES; 641 | CLANG_WARN_ENUM_CONVERSION = YES; 642 | CLANG_WARN_INFINITE_RECURSION = YES; 643 | CLANG_WARN_INT_CONVERSION = YES; 644 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 645 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 646 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 647 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 648 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 649 | CLANG_WARN_STRICT_PROTOTYPES = YES; 650 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 651 | CLANG_WARN_UNREACHABLE_CODE = YES; 652 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 653 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 654 | COPY_PHASE_STRIP = NO; 655 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 656 | ENABLE_NS_ASSERTIONS = NO; 657 | ENABLE_STRICT_OBJC_MSGSEND = YES; 658 | GCC_C_LANGUAGE_STANDARD = gnu99; 659 | GCC_NO_COMMON_BLOCKS = YES; 660 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 661 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 662 | GCC_WARN_UNDECLARED_SELECTOR = YES; 663 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 664 | GCC_WARN_UNUSED_FUNCTION = YES; 665 | GCC_WARN_UNUSED_VARIABLE = YES; 666 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 667 | MTL_ENABLE_DEBUG_INFO = NO; 668 | SDKROOT = iphoneos; 669 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 670 | SWIFT_VERSION = 4.2; 671 | TARGETED_DEVICE_FAMILY = "1,2"; 672 | VALIDATE_PRODUCT = YES; 673 | }; 674 | name = Release; 675 | }; 676 | CECF53D51C0F86DD00DF6B92 /* Debug */ = { 677 | isa = XCBuildConfiguration; 678 | buildSettings = { 679 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 680 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 681 | DEVELOPMENT_TEAM = ""; 682 | INFOPLIST_FILE = FlowingMenuExample/Info.plist; 683 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 684 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.FlowingMenuExample; 685 | PRODUCT_NAME = "$(TARGET_NAME)"; 686 | }; 687 | name = Debug; 688 | }; 689 | CECF53D61C0F86DD00DF6B92 /* Release */ = { 690 | isa = XCBuildConfiguration; 691 | buildSettings = { 692 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 693 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 694 | DEVELOPMENT_TEAM = ""; 695 | INFOPLIST_FILE = FlowingMenuExample/Info.plist; 696 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 697 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.FlowingMenuExample; 698 | PRODUCT_NAME = "$(TARGET_NAME)"; 699 | }; 700 | name = Release; 701 | }; 702 | CECF540B1C11A6C600DF6B92 /* Debug */ = { 703 | isa = XCBuildConfiguration; 704 | buildSettings = { 705 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 706 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; 707 | INFOPLIST_FILE = FlowingMenuUITests/Info.plist; 708 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 709 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 710 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.FlowingMenuUITests; 711 | PRODUCT_NAME = "$(TARGET_NAME)"; 712 | TEST_TARGET_NAME = FlowingMenuExample; 713 | USES_XCTRUNNER = YES; 714 | }; 715 | name = Debug; 716 | }; 717 | CECF540C1C11A6C600DF6B92 /* Release */ = { 718 | isa = XCBuildConfiguration; 719 | buildSettings = { 720 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 721 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; 722 | INFOPLIST_FILE = FlowingMenuUITests/Info.plist; 723 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 724 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 725 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.FlowingMenuUITests; 726 | PRODUCT_NAME = "$(TARGET_NAME)"; 727 | TEST_TARGET_NAME = FlowingMenuExample; 728 | USES_XCTRUNNER = YES; 729 | }; 730 | name = Release; 731 | }; 732 | CECF54191C11AB2D00DF6B92 /* Debug */ = { 733 | isa = XCBuildConfiguration; 734 | buildSettings = { 735 | BUNDLE_LOADER = "$(TEST_HOST)"; 736 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 737 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; 738 | INFOPLIST_FILE = FlowingMenuTests/Info.plist; 739 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 740 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.FlowingMenuTests; 741 | PRODUCT_NAME = "$(TARGET_NAME)"; 742 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 743 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FlowingMenuExample.app/FlowingMenuExample"; 744 | }; 745 | name = Debug; 746 | }; 747 | CECF541A1C11AB2D00DF6B92 /* Release */ = { 748 | isa = XCBuildConfiguration; 749 | buildSettings = { 750 | BUNDLE_LOADER = "$(TEST_HOST)"; 751 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 752 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; 753 | INFOPLIST_FILE = FlowingMenuTests/Info.plist; 754 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 755 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.FlowingMenuTests; 756 | PRODUCT_NAME = "$(TARGET_NAME)"; 757 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 758 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FlowingMenuExample.app/FlowingMenuExample"; 759 | }; 760 | name = Release; 761 | }; 762 | CECF543C1C11B5C400DF6B92 /* Debug */ = { 763 | isa = XCBuildConfiguration; 764 | buildSettings = { 765 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 766 | CURRENT_PROJECT_VERSION = 1; 767 | DEFINES_MODULE = YES; 768 | DYLIB_COMPATIBILITY_VERSION = 1; 769 | DYLIB_CURRENT_VERSION = 1; 770 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 771 | INFOPLIST_FILE = FlowingMenu/Info.plist; 772 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 773 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 774 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 775 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.FlowingMenu; 776 | PRODUCT_NAME = "$(TARGET_NAME)"; 777 | SKIP_INSTALL = YES; 778 | VERSIONING_SYSTEM = "apple-generic"; 779 | VERSION_INFO_PREFIX = ""; 780 | }; 781 | name = Debug; 782 | }; 783 | CECF543D1C11B5C400DF6B92 /* Release */ = { 784 | isa = XCBuildConfiguration; 785 | buildSettings = { 786 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 787 | CURRENT_PROJECT_VERSION = 1; 788 | DEFINES_MODULE = YES; 789 | DYLIB_COMPATIBILITY_VERSION = 1; 790 | DYLIB_CURRENT_VERSION = 1; 791 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 792 | INFOPLIST_FILE = FlowingMenu/Info.plist; 793 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 794 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 795 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 796 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.FlowingMenu; 797 | PRODUCT_NAME = "$(TARGET_NAME)"; 798 | SKIP_INSTALL = YES; 799 | VERSIONING_SYSTEM = "apple-generic"; 800 | VERSION_INFO_PREFIX = ""; 801 | }; 802 | name = Release; 803 | }; 804 | /* End XCBuildConfiguration section */ 805 | 806 | /* Begin XCConfigurationList section */ 807 | CECF53BD1C0F86DD00DF6B92 /* Build configuration list for PBXProject "FlowingMenuExample" */ = { 808 | isa = XCConfigurationList; 809 | buildConfigurations = ( 810 | CECF53D21C0F86DD00DF6B92 /* Debug */, 811 | CECF53D31C0F86DD00DF6B92 /* Release */, 812 | ); 813 | defaultConfigurationIsVisible = 0; 814 | defaultConfigurationName = Release; 815 | }; 816 | CECF53D41C0F86DD00DF6B92 /* Build configuration list for PBXNativeTarget "FlowingMenuExample" */ = { 817 | isa = XCConfigurationList; 818 | buildConfigurations = ( 819 | CECF53D51C0F86DD00DF6B92 /* Debug */, 820 | CECF53D61C0F86DD00DF6B92 /* Release */, 821 | ); 822 | defaultConfigurationIsVisible = 0; 823 | defaultConfigurationName = Release; 824 | }; 825 | CECF540A1C11A6C600DF6B92 /* Build configuration list for PBXNativeTarget "FlowingMenuUITests" */ = { 826 | isa = XCConfigurationList; 827 | buildConfigurations = ( 828 | CECF540B1C11A6C600DF6B92 /* Debug */, 829 | CECF540C1C11A6C600DF6B92 /* Release */, 830 | ); 831 | defaultConfigurationIsVisible = 0; 832 | defaultConfigurationName = Release; 833 | }; 834 | CECF54181C11AB2D00DF6B92 /* Build configuration list for PBXNativeTarget "FlowingMenuTests" */ = { 835 | isa = XCConfigurationList; 836 | buildConfigurations = ( 837 | CECF54191C11AB2D00DF6B92 /* Debug */, 838 | CECF541A1C11AB2D00DF6B92 /* Release */, 839 | ); 840 | defaultConfigurationIsVisible = 0; 841 | defaultConfigurationName = Release; 842 | }; 843 | CECF543B1C11B5C400DF6B92 /* Build configuration list for PBXNativeTarget "FlowingMenu" */ = { 844 | isa = XCConfigurationList; 845 | buildConfigurations = ( 846 | CECF543C1C11B5C400DF6B92 /* Debug */, 847 | CECF543D1C11B5C400DF6B92 /* Release */, 848 | ); 849 | defaultConfigurationIsVisible = 0; 850 | defaultConfigurationName = Release; 851 | }; 852 | /* End XCConfigurationList section */ 853 | }; 854 | rootObject = CECF53BA1C0F86DD00DF6B92 /* Project object */; 855 | } 856 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample.xcodeproj/xcshareddata/xcschemes/FlowingMenu.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample.xcodeproj/xcshareddata/xcschemes/FlowingMenuTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 16 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 40 | 41 | 42 | 43 | 49 | 50 | 52 | 53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample.xcodeproj/xcshareddata/xcschemes/FlowingMenuUITests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 16 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 40 | 41 | 42 | 43 | 49 | 50 | 52 | 53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // FlowingMenuExample 4 | // 5 | // Created by Yannick LORIOT on 02/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/1.imageset/1@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/avatars/1.imageset/1@2x.jpg -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "1@2x.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/15.imageset/15@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/avatars/15.imageset/15@2x.jpg -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/15.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "15@2x.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/27.imageset/27@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/avatars/27.imageset/27@2x.jpg -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/27.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "27@2x.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/37.imageset/37@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/avatars/37.imageset/37@2x.jpg -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/37.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "37@2x.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/4.imageset/4@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/avatars/4.imageset/4@2x.jpg -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "4@2x.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/47.imageset/47@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/avatars/47.imageset/47@2x.jpg -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/47.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "47@2x.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/5.imageset/5@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/avatars/5.imageset/5@2x.jpg -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "5@2x.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/52.imageset/52@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/avatars/52.imageset/52@2x.jpg -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/52.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "52@2x.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/63.imageset/63@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/avatars/63.imageset/63@2x.jpg -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/63.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "63@2x.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/79.imageset/79@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/avatars/79.imageset/79@2x.jpg -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/79.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "79@2x.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/89.imageset/89@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/avatars/89.imageset/89@2x.jpg -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/89.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "89@2x.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/92.imageset/92@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/avatars/92.imageset/92@2x.jpg -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/92.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "92@2x.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/avatars/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/pictos/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/pictos/picto-back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "picto-back@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "template" 23 | } 24 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/pictos/picto-back.imageset/picto-back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/pictos/picto-back.imageset/picto-back@2x.png -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/pictos/picto-chat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "picto-chat@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "template" 23 | } 24 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/pictos/picto-chat.imageset/picto-chat@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/pictos/picto-chat.imageset/picto-chat@2x.png -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/pictos/picto-hamburger.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "picto-hamburger@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/pictos/picto-hamburger.imageset/picto-hamburger@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/pictos/picto-hamburger.imageset/picto-hamburger@2x.png -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/pictos/picto-threedot.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "picto-threedot@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Assets.xcassets/pictos/picto-threedot.imageset/picto-threedot@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yannickl/FlowingMenu/2930764e4a49d64355010b8de664cf2404b07792/Example/FlowingMenuExample/Assets.xcassets/pictos/picto-threedot.imageset/picto-threedot@2x.png -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Base.lproj/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 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample/Base.lproj/Main.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 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 59 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample/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 | APPL 17 | CFBundleShortVersionString 18 | 3.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIRequiresFullScreen 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | UIViewControllerBasedStatusBarAppearance 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample/MenuViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuViewController.swift 3 | // FlowingMenuExample 4 | // 5 | // Created by Yannick LORIOT on 03/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class MenuViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 12 | @IBOutlet weak var topBar: UINavigationBar! 13 | @IBOutlet weak var userTableView: UITableView! 14 | @IBOutlet weak var backButtonItem: UIBarButtonItem! 15 | 16 | let CellName = "UserChatCell" 17 | 18 | let users = User.dummyUsers() 19 | let mainColor = UIColor(hex: 0xC4ABAA) 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | topBar.tintColor = .black 25 | topBar.barTintColor = mainColor 26 | topBar.titleTextAttributes = [ 27 | NSAttributedString.Key.font: UIFont(name: "HelveticaNeue-Light", size: 22)!, 28 | NSAttributedString.Key.foregroundColor: UIColor.black 29 | ] 30 | userTableView.backgroundColor = mainColor 31 | view.backgroundColor = mainColor 32 | } 33 | 34 | // MARK: - Managing the Status Bar 35 | 36 | override var preferredStatusBarStyle: UIStatusBarStyle { 37 | return .lightContent 38 | } 39 | 40 | // MARK: - UITableView DataSource Methods 41 | 42 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 43 | return users.count 44 | } 45 | 46 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 47 | let cell = tableView.dequeueReusableCell(withIdentifier: CellName) as! UserChatCellView 48 | 49 | let user = users[(indexPath as NSIndexPath).row] 50 | 51 | cell.displayNameLabel.text = user.displayName() 52 | cell.avatarImageView.image = user.avatarImage() 53 | cell.statusView.isHidden = !user.newMessage 54 | 55 | cell.contentView.backgroundColor = mainColor 56 | 57 | return cell 58 | } 59 | 60 | // MARK: - UITableView Delegate Methods 61 | 62 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 63 | print("Select row at \(indexPath)") 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // FlowingMenuExample 4 | // 5 | // Created by Yannick LORIOT on 02/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController, UITableViewDataSource, FlowingMenuDelegate { 12 | @IBOutlet weak var topBar: UINavigationBar! 13 | @IBOutlet weak var userTableView: UITableView! 14 | @IBOutlet var flowingMenuTransitionManager: FlowingMenuTransitionManager! 15 | 16 | let PresentSegueName = "PresentMenuSegue" 17 | let DismissSegueName = "DismissMenuSegue" 18 | let CellName = "UserContactCell" 19 | 20 | let users = User.dummyUsers() 21 | let mainColor = UIColor(hex: 0x804C5F) 22 | let derivatedColor = UIColor(hex: 0x794759) 23 | 24 | var menu: UIViewController? 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | 29 | flowingMenuTransitionManager.setInteractivePresentationView(view) 30 | flowingMenuTransitionManager.delegate = self 31 | 32 | topBar.tintColor = .white 33 | topBar.barTintColor = mainColor 34 | topBar.titleTextAttributes = [ 35 | NSAttributedString.Key.font: UIFont(name: "HelveticaNeue-Light", size: 22)!, 36 | NSAttributedString.Key.foregroundColor: UIColor.white 37 | ] 38 | userTableView.backgroundColor = mainColor 39 | view.backgroundColor = mainColor 40 | } 41 | 42 | // MARK: - Interacting with Storyboards and Segues 43 | 44 | 45 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 46 | if segue.identifier == PresentSegueName { 47 | let vc = segue.destination 48 | vc.transitioningDelegate = flowingMenuTransitionManager 49 | 50 | flowingMenuTransitionManager.setInteractiveDismissView(vc.view) 51 | 52 | menu = vc 53 | } 54 | } 55 | 56 | @IBAction func unwindToMainViewController(_ sender: UIStoryboardSegue) { 57 | } 58 | 59 | // MARK: - Managing the Status Bar 60 | 61 | override var preferredStatusBarStyle: UIStatusBarStyle { 62 | return .lightContent 63 | } 64 | 65 | // MARK: - FlowingMenu Delegate Methods 66 | 67 | func colorOfElasticShapeInFlowingMenu(_ flowingMenu: FlowingMenuTransitionManager) -> UIColor? { 68 | return mainColor 69 | } 70 | 71 | func flowingMenuNeedsPresentMenu(_ flowingMenu: FlowingMenuTransitionManager) { 72 | performSegue(withIdentifier: PresentSegueName, sender: self) 73 | } 74 | 75 | func flowingMenuNeedsDismissMenu(_ flowingMenu: FlowingMenuTransitionManager) { 76 | menu?.performSegue(withIdentifier: DismissSegueName, sender: self) 77 | } 78 | 79 | // MARK: - UITableView DataSource Methods 80 | 81 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 82 | return users.count 83 | } 84 | 85 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 86 | let cell = tableView.dequeueReusableCell(withIdentifier: CellName) as! UserContactCellView 87 | 88 | let user = users[(indexPath as NSIndexPath).row] 89 | 90 | cell.displayNameLabel.text = user.displayName() 91 | cell.avatarImageView.image = user.avatarImage() 92 | 93 | cell.contentView.backgroundColor = (indexPath as NSIndexPath).row % 2 == 0 ? derivatedColor : mainColor 94 | 95 | return cell 96 | } 97 | } 98 | 99 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample/cells/UserChatCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserChatCellView.swift 3 | // FlowingMenuExample 4 | // 5 | // Created by Yannick LORIOT on 03/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class UserChatCellView: UITableViewCell { 12 | @IBOutlet weak var avatarImageView: UIImageView! { 13 | didSet { 14 | avatarImageView.layer.masksToBounds = true 15 | } 16 | } 17 | @IBOutlet weak var displayNameLabel: UILabel! 18 | @IBOutlet weak var statusView: UIView! { 19 | didSet { 20 | statusView.layer.masksToBounds = true 21 | } 22 | } 23 | 24 | override func layoutSubviews() { 25 | super.layoutSubviews() 26 | 27 | // iOS 10 bug: rdar://27644391 28 | contentView.layoutSubviews() 29 | 30 | avatarImageView.layer.cornerRadius = avatarImageView.bounds.width / 2 31 | statusView.layer.cornerRadius = statusView.bounds.width / 2 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample/cells/UserContactCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserContactCellView.swift 3 | // FlowingMenuExample 4 | // 5 | // Created by Yannick LORIOT on 03/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class UserContactCellView: UITableViewCell { 12 | @IBOutlet weak var avatarImageView: UIImageView! { 13 | didSet { 14 | avatarImageView.layer.masksToBounds = true 15 | } 16 | } 17 | @IBOutlet weak var displayNameLabel: UILabel! 18 | @IBOutlet weak var roleLabel: UILabel! 19 | 20 | override func layoutSubviews() { 21 | super.layoutSubviews() 22 | 23 | // iOS 10 bug: rdar://27644391 24 | contentView.layoutSubviews() 25 | 26 | avatarImageView.layer.cornerRadius = avatarImageView.bounds.width / 2 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Example/FlowingMenuExample/model/User.swift: -------------------------------------------------------------------------------- 1 | // 2 | // User.swift 3 | // FlowingMenuExample 4 | // 5 | // Created by Yannick LORIOT on 02/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | struct User { 13 | let id: Int 14 | let firstname: String 15 | let lastname: String 16 | let newMessage: Bool 17 | 18 | func displayName() -> String { 19 | return "\(firstname) \(lastname)" 20 | } 21 | 22 | func avatarImage() -> UIImage? { 23 | return UIImage(named: "\(id)") 24 | } 25 | } 26 | 27 | extension User { 28 | // Data taken from https://randomuser.me/ 29 | static func dummyUsers() -> [User] { 30 | return [ 31 | User(id: 79, firstname: "Noelia", lastname: "Marin", newMessage: true), 32 | User(id: 1, firstname: "Enrique", lastname: "Santos", newMessage: true), 33 | User(id: 63, firstname: "Roberto", lastname: "Crespo", newMessage: true), 34 | User(id: 52, firstname: "Veronica", lastname: "Cortes", newMessage: true), 35 | User(id: 47, firstname: "Nerea", lastname: "Alonso", newMessage: false), 36 | User(id: 27, firstname: "Silvia", lastname: "Herrero", newMessage: false), 37 | User(id: 4, firstname: "Susana", lastname: "Aguilar", newMessage: false), 38 | User(id: 89, firstname: "Alejandro", lastname: "Moya", newMessage: false), 39 | User(id: 5, firstname: "Inmaculada", lastname: "Cortes", newMessage: false), 40 | User(id: 15, firstname: "Teresa", lastname: "Saez", newMessage: false), 41 | User(id: 37, firstname: "Lorenzo", lastname: "Vicente", newMessage: false), 42 | User(id: 92, firstname: "Joel", lastname: "Mattila", newMessage: false) 43 | ] 44 | } 45 | } -------------------------------------------------------------------------------- /Example/FlowingMenuExample/utils/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // FlowingMenuExample 4 | // 5 | // Created by Yannick LORIOT on 03/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | /** 13 | Creates a color from an hex int. 14 | 15 | - parameter hexString: A hexa-decimal color number representation. 16 | */ 17 | convenience init(hex: UInt32) { 18 | let mask = 0x000000FF 19 | 20 | let r = Int(hex >> 16) & mask 21 | let g = Int(hex >> 8) & mask 22 | let b = Int(hex) & mask 23 | 24 | let red = CGFloat(r) / 255 25 | let green = CGFloat(g) / 255 26 | let blue = CGFloat(b) / 255 27 | 28 | self.init(red:red, green:green, blue:blue, alpha:1) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Example/FlowingMenuTests/FlowingMenuAnimatedTransitioningTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlowingMenuAnimatedTransitioningTests.swift 3 | // FlowingMenuExample 4 | // 5 | // Created by Yannick LORIOT on 04/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class FlowingMenuAnimatedTransitioningTests: XCTTestCaseTemplate { 12 | var transitionManager = FlowingMenuTransitionManager() 13 | 14 | override func setUp() { 15 | super.setUp() 16 | 17 | transitionManager = FlowingMenuTransitionManager() 18 | } 19 | 20 | func testTransitionDuration() { 21 | XCTAssertFalse(transitionManager.interactive) 22 | 23 | var duration = transitionManager.transitionDuration(using: nil) 24 | 25 | XCTAssertEqual(duration, 0.2) 26 | 27 | transitionManager.interactive = true 28 | 29 | duration = transitionManager.transitionDuration(using: nil) 30 | 31 | XCTAssertEqual(duration, 0.6) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Example/FlowingMenuTests/FlowingMenuDelegateTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlowingMenuDelegateTests.swift 3 | // FlowingMenuExample 4 | // 5 | // Created by Yannick LORIOT on 04/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class FlowingMenuDelegateTests: XCTTestCaseTemplate { 12 | var transitionManager = FlowingMenuTransitionManager() 13 | 14 | override func setUp() { 15 | super.setUp() 16 | 17 | transitionManager = FlowingMenuTransitionManager() 18 | } 19 | 20 | // MARK: - Default 21 | 22 | func testFlowingMenuWidthOfMenuView_Default() { 23 | let view = UIView(frame: CGRect(x: 0, y: 0, width: 36, height: 36)) 24 | let width = transitionManager.flowingMenu(transitionManager, widthOfMenuView: view) 25 | 26 | XCTAssertEqual(width, 24) 27 | } 28 | 29 | func testColorOfElasticShapeInFlowingMenu_Default() { 30 | let defaultColor = transitionManager.colorOfElasticShapeInFlowingMenu(transitionManager) 31 | 32 | XCTAssertNil(defaultColor) 33 | } 34 | 35 | func testFlowingMenuNeedsPresentMenu_Default() { 36 | transitionManager.flowingMenuNeedsPresentMenu(transitionManager) 37 | } 38 | 39 | func testFlowingMenuNeedsDismissMenu_Default() { 40 | transitionManager.flowingMenuNeedsDismissMenu(transitionManager) 41 | } 42 | 43 | // MARK: - Custom 44 | 45 | func testFlowingMenuWidthOfMenuView_Custom() { 46 | let expec = expectation(description: "Get Width") 47 | let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 400)) 48 | 49 | class ConcreteDelegate: FlowingMenuDelegate { 50 | let expectation: XCTestExpectation 51 | 52 | init(expectation: XCTestExpectation) { 53 | self.expectation = expectation 54 | } 55 | 56 | func flowingMenu(_ flowingMenu: FlowingMenuTransitionManager, widthOfMenuView menuView: UIView) -> CGFloat { 57 | expectation.fulfill() 58 | 59 | return menuView.bounds.width 60 | } 61 | } 62 | 63 | let delegate = ConcreteDelegate(expectation: expec) 64 | let width = delegate.flowingMenu(transitionManager, widthOfMenuView: view) 65 | 66 | XCTAssertEqual(view.bounds.width, width) 67 | 68 | waitForExpectations(timeout: 0.1, handler: nil) 69 | } 70 | 71 | func testColorOfElasticShapeInFlowingMenu_Custom() { 72 | let expec = expectation(description: "Get Color") 73 | 74 | class ConcreteDelegate: FlowingMenuDelegate { 75 | let expectation: XCTestExpectation 76 | 77 | init(expectation: XCTestExpectation) { 78 | self.expectation = expectation 79 | } 80 | 81 | func colorOfElasticShapeInFlowingMenu(_ flowingMenu: FlowingMenuTransitionManager) -> UIColor? { 82 | expectation.fulfill() 83 | 84 | return UIColor.yellow 85 | } 86 | } 87 | 88 | let delegate = ConcreteDelegate(expectation: expec) 89 | let color = delegate.colorOfElasticShapeInFlowingMenu(transitionManager) 90 | 91 | XCTAssertEqual(color, UIColor.yellow) 92 | 93 | waitForExpectations(timeout: 0.1, handler: nil) 94 | } 95 | 96 | func testFlowingMenuNeedsPresentMenu_Custom() { 97 | let expec = expectation(description: "Present Menu") 98 | 99 | class ConcreteDelegate: FlowingMenuDelegate { 100 | let expectation: XCTestExpectation 101 | 102 | init(expectation: XCTestExpectation) { 103 | self.expectation = expectation 104 | } 105 | 106 | func flowingMenuNeedsPresentMenu(_ flowingMenu: FlowingMenuTransitionManager) { 107 | expectation.fulfill() 108 | } 109 | } 110 | 111 | let delegate = ConcreteDelegate(expectation: expec) 112 | delegate.flowingMenuNeedsPresentMenu(transitionManager) 113 | 114 | waitForExpectations(timeout: 0.1, handler: nil) 115 | } 116 | 117 | func testFlowingMenuNeedsDismissMenu_Custom() { 118 | let expec = expectation(description: "Dismiss Menu") 119 | 120 | class ConcreteDelegate: FlowingMenuDelegate { 121 | let expectation: XCTestExpectation 122 | 123 | init(expectation: XCTestExpectation) { 124 | self.expectation = expectation 125 | } 126 | 127 | func flowingMenuNeedsDismissMenu(_ flowingMenu: FlowingMenuTransitionManager) { 128 | expectation.fulfill() 129 | } 130 | } 131 | 132 | let delegate = ConcreteDelegate(expectation: expec) 133 | delegate.flowingMenuNeedsDismissMenu(transitionManager) 134 | 135 | waitForExpectations(timeout: 0.1, handler: nil) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Example/FlowingMenuTests/FlowingMenuTransitionManagerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlowingMenuTransitionManagerTests.swift 3 | // FlowingMenuExample 4 | // 5 | // Created by Yannick LORIOT on 04/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class FlowingMenuTransitionManagerTests: XCTTestCaseTemplate { 12 | var transitionManager = FlowingMenuTransitionManager() 13 | 14 | override func setUp() { 15 | super.setUp() 16 | 17 | transitionManager = FlowingMenuTransitionManager() 18 | } 19 | 20 | func testAnimatingFlag() { 21 | XCTAssertTrue(transitionManager.displayLink.isPaused) 22 | 23 | transitionManager.animating = true 24 | 25 | XCTAssertFalse(transitionManager.displayLink.isPaused) 26 | 27 | transitionManager.animating = false 28 | 29 | XCTAssertTrue(transitionManager.displayLink.isPaused) 30 | } 31 | 32 | func testPresentMenu() { 33 | let expec = expectation(description: "Present menu animation") 34 | 35 | DispatchQueue.main.async { [weak self] in 36 | let menuView = UIView() 37 | let otherView = UIView() 38 | let containerView = UIView() 39 | let status = FlowingMenuTransitionStatus(cancelled: false) 40 | let duration = 0.2 41 | 42 | self?.transitionManager.presentMenu(menuView, otherView: otherView, containerView: containerView, status: status, duration: duration) { 43 | expec.fulfill() 44 | } 45 | } 46 | 47 | waitForExpectations(timeout: 1, handler: nil) 48 | } 49 | 50 | func testPresentMenu_Interactive() { 51 | let expec = expectation(description: "Present menu animation") 52 | 53 | DispatchQueue.main.async { [weak self] in 54 | let menuView = UIView() 55 | let otherView = UIView() 56 | let containerView = UIView() 57 | let status = FlowingMenuTransitionStatus(cancelled: false) 58 | let duration = 0.2 59 | 60 | self?.transitionManager.interactive = true 61 | self?.transitionManager.presentMenu(menuView, otherView: otherView, containerView: containerView, status: status, duration: duration) { 62 | expec.fulfill() 63 | } 64 | } 65 | 66 | waitForExpectations(timeout: 1, handler: nil) 67 | } 68 | 69 | func testDismissMenu() { 70 | let expec = expectation(description: "Dismiss menu animation") 71 | 72 | DispatchQueue.main.async { [weak self] in 73 | let menuView = UIView() 74 | let otherView = UIView() 75 | let containerView = UIView() 76 | let status = FlowingMenuTransitionStatus(cancelled: false) 77 | let duration = 0.2 78 | 79 | self?.transitionManager.dismissMenu(menuView, otherView: otherView, containerView: containerView, status: status, duration: duration) { 80 | expec.fulfill() 81 | } 82 | } 83 | 84 | waitForExpectations(timeout: 1, handler: nil) 85 | } 86 | 87 | func testDismissMenu_Interactive() { 88 | let expec = expectation(description: "Dismiss menu animation") 89 | 90 | DispatchQueue.main.async { [weak self] in 91 | let menuView = UIView() 92 | let otherView = UIView() 93 | let containerView = UIView() 94 | let status = FlowingMenuTransitionStatus(cancelled: false) 95 | let duration = 0.2 96 | 97 | self?.transitionManager.interactive = true 98 | self?.transitionManager.dismissMenu(menuView, otherView: otherView, containerView: containerView, status: status, duration: duration) { 99 | expec.fulfill() 100 | } 101 | } 102 | 103 | waitForExpectations(timeout: 1, handler: nil) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Example/FlowingMenuTests/FlowingMenuTransitioningDelegateTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlowingMenuTransitioningDelegateTests.swift 3 | // FlowingMenuExample 4 | // 5 | // Created by Yannick LORIOT on 04/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class FlowingMenuTransitioningDelegateTests: XCTTestCaseTemplate { 12 | var transitionManager = FlowingMenuTransitionManager() 13 | 14 | override func setUp() { 15 | super.setUp() 16 | 17 | transitionManager = FlowingMenuTransitionManager() 18 | } 19 | 20 | func testAnimationControllerForPresentedController() { 21 | let presented = UIViewController() 22 | let presenting = UIViewController() 23 | let source = UIViewController() 24 | 25 | let animation = transitionManager.animationController(forPresented: presented, presenting: presenting, source: source) 26 | 27 | XCTAssertNotNil(animation) 28 | XCTAssertEqual(transitionManager.animationMode, FlowingMenuTransitionManager.AnimationMode.presentation) 29 | } 30 | 31 | func testAnimationControllerForDismissedController() { 32 | let dismissed = UIViewController() 33 | 34 | let animation = transitionManager.animationController(forDismissed: dismissed) 35 | 36 | XCTAssertNotNil(animation) 37 | XCTAssertEqual(transitionManager.animationMode, FlowingMenuTransitionManager.AnimationMode.dismissal) 38 | } 39 | 40 | func testInteractionControllerForPresentation() { 41 | XCTAssertFalse(transitionManager.interactive) 42 | 43 | var interaction = transitionManager.interactionControllerForPresentation(using: transitionManager) 44 | 45 | XCTAssertNil(interaction) 46 | 47 | transitionManager.interactive = true 48 | 49 | interaction = transitionManager.interactionControllerForPresentation(using: transitionManager) 50 | 51 | XCTAssertNotNil(interaction) 52 | } 53 | 54 | func testInteractionControllerForDismissal() { 55 | XCTAssertFalse(transitionManager.interactive) 56 | 57 | var interaction = transitionManager.interactionControllerForDismissal(using: transitionManager) 58 | 59 | XCTAssertNil(interaction) 60 | 61 | transitionManager.interactive = true 62 | 63 | interaction = transitionManager.interactionControllerForDismissal(using: transitionManager) 64 | 65 | XCTAssertNotNil(interaction) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Example/FlowingMenuTests/FlowingMenuUIGestureRecognizerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlowingMenuUIGestureRecognizerTests.swift 3 | // FlowingMenuTests 4 | // 5 | // Created by Yannick LORIOT on 04/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class FlowingMenuUIGestureRecognizerTests: XCTTestCaseTemplate { 12 | var transitionManager = FlowingMenuTransitionManager() 13 | 14 | override func setUp() { 15 | super.setUp() 16 | 17 | transitionManager = FlowingMenuTransitionManager() 18 | } 19 | 20 | func testCurrentPath() { 21 | let defaultPath = transitionManager.currentPath() 22 | 23 | XCTAssertFalse(defaultPath.isEmpty) 24 | XCTAssertEqual(defaultPath.boundingBoxOfPath, CGRect.zero) 25 | 26 | transitionManager.controlViews[1].center = CGPoint(x: 5, y: 5) 27 | transitionManager.controlViews[2].center = CGPoint(x: 5, y: 5) 28 | transitionManager.controlViews[3].center = CGPoint(x: 0, y: 5) 29 | 30 | let customPath = transitionManager.currentPath() 31 | XCTAssertEqual(customPath.boundingBoxOfPath, CGRect(x: 0, y: 0, width: 5, height: 5)) 32 | } 33 | 34 | func testCurrentPath_Animating() { 35 | transitionManager.animating = true 36 | 37 | let defaultPath = transitionManager.currentPath() 38 | 39 | XCTAssertFalse(defaultPath.isEmpty) 40 | XCTAssertEqual(defaultPath.boundingBoxOfPath, CGRect.zero) 41 | 42 | transitionManager.controlViews[1].center = CGPoint(x: 5, y: 5) 43 | transitionManager.controlViews[2].center = CGPoint(x: 5, y: 5) 44 | transitionManager.controlViews[3].center = CGPoint(x: 0, y: 5) 45 | 46 | let customPath = transitionManager.currentPath() 47 | XCTAssertEqual(customPath.boundingBoxOfPath, CGRect(x: 0, y: 0, width: 5, height: 5)) 48 | } 49 | 50 | func testUpdateShapeLayer() { 51 | transitionManager.updateShapeLayer() 52 | 53 | XCTAssertFalse(transitionManager.shapeLayer.path!.isEmpty) 54 | XCTAssertEqual(transitionManager.shapeLayer.path!.boundingBoxOfPath, CGRect.zero) 55 | 56 | transitionManager.controlViews[1].center = CGPoint(x: 5, y: 5) 57 | transitionManager.controlViews[2].center = CGPoint(x: 5, y: 5) 58 | transitionManager.controlViews[3].center = CGPoint(x: 0, y: 5) 59 | 60 | transitionManager.updateShapeLayer() 61 | 62 | XCTAssertEqual(transitionManager.shapeLayer.path!.boundingBoxOfPath, CGRect(x: 0, y: 0, width: 5, height: 5)) 63 | } 64 | 65 | // MARK: - Test Control Views 66 | 67 | func testMoveControlViewsToPoint_Default() { 68 | transitionManager.moveControlViewsToPoint(CGPoint.zero, waveWidth: 0) 69 | 70 | for view in transitionManager.controlViews { 71 | XCTAssertEqual(view.center, CGPoint.zero) 72 | } 73 | } 74 | 75 | func testMoveControlViewsToPoint_InitializedBefore() { 76 | for view in transitionManager.controlViews { 77 | view.center = CGPoint(x: 50, y: 50) 78 | } 79 | 80 | transitionManager.moveControlViewsToPoint(CGPoint.zero, waveWidth: 0) 81 | 82 | XCTAssertNotEqual(transitionManager.controlViews[0].center, CGPoint(x: 0, y: 50)) 83 | XCTAssertNotEqual(transitionManager.controlViews[1].center, CGPoint(x: 50, y: 50)) 84 | XCTAssertNotEqual(transitionManager.controlViews[2].center, CGPoint(x: 50, y: 50)) 85 | XCTAssertEqual(transitionManager.controlViews[3].center, CGPoint.zero) 86 | XCTAssertNotEqual(transitionManager.controlViews[4].center, CGPoint(x: 50, y: 50)) 87 | XCTAssertNotEqual(transitionManager.controlViews[5].center, CGPoint(x: 50, y: 50)) 88 | XCTAssertNotEqual(transitionManager.controlViews[6].center, CGPoint(x: 50, y: 50)) 89 | XCTAssertEqual(transitionManager.controlViews[7].center, CGPoint(x: 50, y: 50)) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Example/FlowingMenuTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/FlowingMenuTests/XCTTestCaseTemplate.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * FlowingMenu 3 | * 4 | * Copyright 2015-present Yannick Loriot. 5 | * http://yannickloriot.com 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | import UIKit 28 | import XCTest 29 | 30 | class MockAppDelegate: NSObject, UIApplicationDelegate { 31 | 32 | } 33 | 34 | 35 | class XCTTestCaseTemplate: XCTestCase { 36 | override func setUp() { 37 | super.setUp() 38 | 39 | UIApplication.shared.delegate = MockAppDelegate() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Example/FlowingMenuUITests/FlowingMenuUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlowingMenuUITests.swift 3 | // FlowingMenuUITests 4 | // 5 | // Created by Yannick LORIOT on 04/12/15. 6 | // Copyright © 2015 Yannick LORIOT. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class FlowingMenuUITests: XCTestCase { 12 | override func setUp() { 13 | super.setUp() 14 | 15 | continueAfterFailure = false 16 | 17 | XCUIApplication().launch() 18 | } 19 | 20 | override func tearDown() { 21 | // Put teardown code here. This method is called after the invocation of each test method in the class. 22 | super.tearDown() 23 | } 24 | 25 | func testTransition() { 26 | let app = XCUIApplication() 27 | 28 | app.navigationBars["Contacts"].children(matching: .button).element.tap() 29 | 30 | let tablesQuery = app.tables 31 | tablesQuery.staticTexts["Inmaculada Cortes"].tap() 32 | app.navigationBars["Chat"].children(matching: .button).element.tap() 33 | 34 | tablesQuery.cells.containing(.staticText, identifier:"Silvia Herrero").staticTexts["Work"].tap() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Example/FlowingMenuUITests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /FlowingMenu.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'FlowingMenu' 3 | s.version = '3.1.0' 4 | s.license = 'MIT' 5 | s.summary = 'Interactive view transition to display menus with flowing and bouncing effects in Swift' 6 | s.homepage = 'https://github.com/yannickl/FlowingMenu.git' 7 | s.social_media_url = 'https://twitter.com/yannickloriot' 8 | s.authors = { 'Yannick Loriot' => 'contact@yannickloriot.com' } 9 | s.source = { :git => 'https://github.com/yannickl/FlowingMenu.git', :tag => s.version } 10 | s.screenshot = 'http://yannickloriot.com/resources/flowingmenu.gif' 11 | 12 | s.ios.deployment_target = '8.0' 13 | s.ios.frameworks = 'UIKit', 'QuartzCore' 14 | 15 | s.source_files = 'Sources/*.swift' 16 | s.requires_arc = true 17 | end 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present Yannick Loriot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "FlowingMenu", 6 | products: [ 7 | .library(name: "FlowingMenu", targets: ["FlowingMenu"]), 8 | ], 9 | targets: [ 10 | .target( 11 | name: "FlowingMenu", 12 | dependencies: [], 13 | path: "Sources"), 14 | ] 15 | ) 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlowingMenu 2 | 3 | [![Supported Platforms](https://cocoapod-badges.herokuapp.com/p/FlowingMenu/badge.svg)](http://cocoadocs.org/docsets/FlowingMenu/) [![Version](https://cocoapod-badges.herokuapp.com/v/FlowingMenu/badge.svg)](http://cocoadocs.org/docsets/FlowingMenu/) [![Swift Package Manager compatible](https://img.shields.io/badge/SPM-%E2%9C%93-brightgreen.svg?style=flat)](https://github.com/apple/swift-package-manager) [![Carthage compatible](https://img.shields.io/badge/Carthage-%E2%9C%93-brightgreen.svg?style=flat)](https://github.com/Carthage/Carthage) [![Build Status](https://travis-ci.org/yannickl/FlowingMenu.svg?branch=master)](https://travis-ci.org/yannickl/FlowingMenu) [![codecov.io](http://codecov.io/github/yannickl/FlowingMenu/coverage.svg?branch=master)](http://codecov.io/github/yannickl/FlowingMenu?branch=master) [![codebeat badge](https://codebeat.co/badges/5b519917-eedc-4b7b-8a2d-9dfeed597894)](https://codebeat.co/projects/github-com-yannickl-flowingmenu) 4 | 5 | **FlowingMenu** provides an interactive transition manager to display menu with a flowing and bouncing effects. The Objective-C countepart is here https://github.com/yannickl/YLFlowingMenu. 6 | 7 |

8 | FlowingMenu 9 |

10 | 11 |

12 | RequirementsUsageInstallationContributionContactLicense 13 |

14 | 15 | ## Requirements 16 | 17 | - iOS 9.0+ 18 | - Xcode 9.0+ 19 | - Swift 4.2+ 20 | 21 | ## Usage 22 | 23 | At first, import FlowingMenu: 24 | 25 | ```swift 26 | import FlowingMenu 27 | ``` 28 | 29 | Then just add a `FlowingMenuTransitionManager` object that acts as `transitioningDelegate` of the view controller you want display: 30 | 31 | ```swift 32 | let flowingMenuTransitionManager = FlowingMenuTransitionManager() 33 | 34 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 35 | let vc = segue.destination 36 | vc.transitioningDelegate = flowingMenuTransitionManager 37 | } 38 | ``` 39 | 40 | If you want interactive transition you will need to implement the `FlowingMenuDelegate` methods and defines the views which will interact with the gestures: 41 | 42 | ```swift 43 | var menu: UIViewController? 44 | 45 | override func viewDidLoad() { 46 | super.viewDidLoad() 47 | 48 | // Add the pan screen edge gesture to the current view 49 | flowingMenuTransitionManager.setInteractivePresentationView(view) 50 | 51 | // Add the delegate to respond to interactive transition events 52 | flowingMenuTransitionManager.delegate = self 53 | } 54 | 55 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 56 | let vc = segue.destination 57 | vc.transitioningDelegate = flowingMenuTransitionManager 58 | 59 | // Add the left pan gesture to the menu 60 | flowingMenuTransitionManager.setInteractiveDismissView(vc.view) 61 | 62 | // Keep a reference of the current menu 63 | menu = vc 64 | } 65 | 66 | // MARK: - FlowingMenu Delegate Methods 67 | 68 | func flowingMenuNeedsPresentMenu(_ flowingMenu: FlowingMenuTransitionManager) { 69 | performSegue(withIdentifier: "PresentSegueName", sender: self) 70 | } 71 | 72 | func flowingMenuNeedsDismissMenu(_ flowingMenu: FlowingMenuTransitionManager) { 73 | menu?.performSegue(withIdentifier: "DismissSegueName", sender: self) 74 | } 75 | ``` 76 | 77 | Have fun! :) 78 | 79 | ### For more information... 80 | 81 | To go further, take a look at the documentation and the example project. 82 | 83 | *Note: All contributions are welcome* 84 | 85 | ## Installation 86 | 87 | #### CocoaPods 88 | 89 | Install CocoaPods if not already available: 90 | 91 | ``` bash 92 | $ [sudo] gem install cocoapods 93 | $ pod setup 94 | ``` 95 | Go to the directory of your Xcode project, and Create and Edit your Podfile and add _FlowingMenu_: 96 | 97 | ``` bash 98 | $ cd /path/to/MyProject 99 | $ touch Podfile 100 | $ edit Podfile 101 | source 'https://github.com/CocoaPods/Specs.git' 102 | platform :ios, '8.0' 103 | 104 | use_frameworks! 105 | pod 'FlowingMenu', '~> 3.1.0' 106 | ``` 107 | 108 | Install into your project: 109 | 110 | ``` bash 111 | $ pod install 112 | ``` 113 | 114 | Open your project in Xcode from the .xcworkspace file (not the usual project file): 115 | 116 | ``` bash 117 | $ open MyProject.xcworkspace 118 | ``` 119 | 120 | You can now `import FlowingMenu` framework into your files. 121 | 122 | #### Carthage 123 | 124 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application. 125 | 126 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command: 127 | 128 | ```bash 129 | $ brew update 130 | $ brew install carthage 131 | ``` 132 | 133 | To integrate `FlowingMenu` into your Xcode project using Carthage, specify it in your `Cartfile` file: 134 | 135 | ```ogdl 136 | github "yannickl/FlowingMenu" >= 3.1.0 137 | ``` 138 | 139 | #### Swift Package Manager 140 | You can use [The Swift Package Manager](https://swift.org/package-manager) to install `FlowingMenu` by adding the proper description to your `Package.swift` file: 141 | ```swift 142 | import PackageDescription 143 | 144 | let package = Package( 145 | name: "YOUR_PROJECT_NAME", 146 | targets: [], 147 | dependencies: [ 148 | .Package(url: "https://github.com/yannickl/FlowingMenu.git", versions: "3.1.0" ..< Version.max) 149 | ] 150 | ) 151 | ``` 152 | 153 | Note that the [Swift Package Manager](https://swift.org/package-manager) is still in early design and development, for more information checkout its [GitHub Page](https://github.com/apple/swift-package-manager). 154 | 155 | #### Manually 156 | 157 | [Download](https://github.com/YannickL/FlowingMenu/archive/master.zip) the project and copy the `FlowingMenu` folder into your project to use it in. 158 | 159 | ## Contribution 160 | 161 | Contributions are welcomed and encouraged *♡*. 162 | 163 | ## Contact 164 | 165 | Yannick Loriot 166 | - [https://21.co/yannickl/](https://21.co/yannickl/) 167 | - [https://twitter.com/yannickloriot](https://twitter.com/yannickloriot) 168 | 169 | ## License (MIT) 170 | 171 | Copyright (c) 2015-present - Yannick Loriot 172 | 173 | Permission is hereby granted, free of charge, to any person obtaining a copy 174 | of this software and associated documentation files (the "Software"), to deal 175 | in the Software without restriction, including without limitation the rights 176 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 177 | copies of the Software, and to permit persons to whom the Software is 178 | furnished to do so, subject to the following conditions: 179 | 180 | The above copyright notice and this permission notice shall be included in 181 | all copies or substantial portions of the Software. 182 | 183 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 184 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 185 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 186 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 187 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 188 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 189 | THE SOFTWARE. 190 | -------------------------------------------------------------------------------- /Sources/FlowingMenuDelegate.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * FlowingMenu 3 | * 4 | * Copyright 2015-present Yannick Loriot. 5 | * http://yannickloriot.com 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | import UIKit 28 | 29 | /** 30 | The delegate of a `FlowMenuTransitionManager` object should adopt this protocol 31 | and implement to manage the . 32 | */ 33 | public protocol FlowingMenuDelegate: class { 34 | // MARK: - Laying Out the Menu 35 | 36 | /** 37 | Called by the flowing menu transition manager when it needs to display the 38 | menu. 39 | 40 | - parameter flowingMenu: The flowing menu transition manager requesting the 41 | width. 42 | - parameter menuView: The menu view which will be displayed. 43 | - returns: The width of the menu view. Outside the menu view a black overlay 44 | will be displayed. 45 | */ 46 | func flowingMenu(_ flowingMenu: FlowingMenuTransitionManager, widthOfMenuView menuView: UIView) -> CGFloat 47 | 48 | // MARK: - Drawing the Elastic Shape 49 | 50 | /** 51 | Asks the delegate the color of the shape drawn during an interactive 52 | transition. 53 | 54 | - parameter flowingMenu: The flowing menu transition manager requesting the 55 | color. 56 | - returns: The shape color. If nil it will use the menu background color and 57 | if menu has no background color, the shape will be black. 58 | */ 59 | func colorOfElasticShapeInFlowingMenu(_ flowingMenu: FlowingMenuTransitionManager) -> UIColor? 60 | 61 | // MARK: - Responding to Interactive Transition 62 | 63 | /** 64 | Called by the flowing menu transition manager when the interactive transition 65 | begins its presentation. You should implement this methods to present your 66 | menu view. 67 | 68 | - parameter flowingMenu: The flowing menu transition manager which needs 69 | present the menu. 70 | */ 71 | func flowingMenuNeedsPresentMenu(_ flowingMenu: FlowingMenuTransitionManager) 72 | 73 | /** 74 | Called by the flowing menu transition manager when the interactive transition 75 | begins its dismissal. You should implement this methods to dismiss your menu 76 | view. 77 | 78 | - parameter flowingMenu: The flowing menu transition manager which needs 79 | dismiss the menu. 80 | */ 81 | func flowingMenuNeedsDismissMenu(_ flowingMenu: FlowingMenuTransitionManager) 82 | } 83 | 84 | extension FlowingMenuDelegate { 85 | /// Returns the 2/3 menu view width. 86 | public func flowingMenu(_ flowingMenu: FlowingMenuTransitionManager, widthOfMenuView menuView: UIView) -> CGFloat { 87 | return menuView.bounds.width * 2 / 3 88 | } 89 | 90 | /// Use the menu background by default. 91 | public func colorOfElasticShapeInFlowingMenu(_ flowingMenu: FlowingMenuTransitionManager) -> UIColor? { 92 | return nil 93 | } 94 | 95 | /// You should implement this method to display the menu. By default nothing happens. 96 | public func flowingMenuNeedsPresentMenu(_ flowingMenu: FlowingMenuTransitionManager) { 97 | } 98 | 99 | /// You should implement this method to dismiss the menu. By default nothing happens. 100 | public func flowingMenuNeedsDismissMenu(_ flowingMenu: FlowingMenuTransitionManager) { 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Sources/FlowingMenuTransitionManager+Delegate.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * FlowingMenu 3 | * 4 | * Copyright 2015-present Yannick Loriot. 5 | * http://yannickloriot.com 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | import UIKit 28 | 29 | /// The flowing menu transition manager is it's own delegate by default. 30 | extension FlowingMenuTransitionManager: FlowingMenuDelegate { 31 | } 32 | -------------------------------------------------------------------------------- /Sources/FlowingMenuTransitionManager+UIGestureRecognizer.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * FlowingMenu 3 | * 4 | * Copyright 2015-present Yannick Loriot. 5 | * http://yannickloriot.com 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | import UIKit 28 | 29 | /** 30 | Here manage the interactive transition mainly thanks to the gestures. 31 | */ 32 | extension FlowingMenuTransitionManager { 33 | /** 34 | Defines the given view as interactive to present the menu. 35 | 36 | - parameter view: The view used to respond to the gesture events. 37 | */ 38 | public func setInteractivePresentationView(_ view: UIView) { 39 | let screenEdgePanGesture = UIScreenEdgePanGestureRecognizer() 40 | screenEdgePanGesture.edges = .left 41 | screenEdgePanGesture.addTarget(self, action:#selector(FlowingMenuTransitionManager.panToPresentAction)) 42 | 43 | view.addGestureRecognizer(screenEdgePanGesture) 44 | } 45 | 46 | /** 47 | Defines the given view as interactive to dismiss the menu. 48 | 49 | - parameter view: The view used to respond to the gesture events. 50 | */ 51 | public func setInteractiveDismissView(_ view: UIView) { 52 | let panGesture = UIPanGestureRecognizer() 53 | panGesture.maximumNumberOfTouches = 1 54 | panGesture.addTarget(self, action:#selector(FlowingMenuTransitionManager.panToDismissAction(_:))) 55 | 56 | view.addGestureRecognizer(panGesture) 57 | } 58 | 59 | /** 60 | Add the tap gesture to the given view to dismiss it when a tap occurred. 61 | 62 | - parameter view: The view used to respond to the gesture events. 63 | */ 64 | func addTapGesture(_ view: UIView) { 65 | let tapGesture = UITapGestureRecognizer() 66 | tapGesture.numberOfTapsRequired = 1 67 | tapGesture.addTarget(self, action: #selector(FlowingMenuTransitionManager.tapToDismissAction(_:))) 68 | 69 | view.addGestureRecognizer(tapGesture) 70 | } 71 | 72 | // MARK: - Responding to Gesture Events 73 | 74 | /** 75 | The screen edge pan gesture recognizer action methods. It is used to 76 | present the menu. 77 | 78 | - parameter panGesture: The `UIScreenEdgePanGestureRecognizer` sender 79 | object. 80 | */ 81 | @objc func panToPresentAction(_ panGesture: UIScreenEdgePanGestureRecognizer) { 82 | let view = panGesture.view! 83 | let translation = panGesture.translation(in: view) 84 | let menuWidth = (delegate ?? self).flowingMenu(self, widthOfMenuView: view) 85 | 86 | let yLocation = panGesture.location(in: panGesture.view).y 87 | let percentage = min(max(translation.x / (menuWidth / 2), 0), 1) 88 | 89 | switch panGesture.state { 90 | case .began: 91 | interactive = true 92 | 93 | // Asking the delegate the present the menu 94 | delegate?.flowingMenuNeedsPresentMenu(self) 95 | 96 | fallthrough 97 | case .changed: 98 | update(percentage) 99 | 100 | let waveWidth = translation.x * 0.9 101 | let left = waveWidth * 0.1 102 | 103 | // Update the control points 104 | moveControlViewsToPoint(CGPoint(x: left, y: yLocation), waveWidth: waveWidth) 105 | 106 | // Update the shape layer 107 | updateShapeLayer() 108 | default: 109 | animating = true 110 | 111 | if percentage < 1 { 112 | interactive = false 113 | 114 | moveControlViewsToPoint(CGPoint(x: 0, y: yLocation), waveWidth: 0) 115 | 116 | cancel() 117 | } 118 | else { 119 | finish() 120 | } 121 | } 122 | } 123 | 124 | /** 125 | The pan gesture recognizer action methods. It is used to dismiss the 126 | menu. 127 | 128 | - parameter panGesture: The `UIPanGestureRecognizer` sender object. 129 | */ 130 | @objc func panToDismissAction(_ panGesture: UIPanGestureRecognizer) { 131 | let view = panGesture.view! 132 | let translation = panGesture.translation(in: view) 133 | let menuWidth = (delegate ?? self).flowingMenu(self, widthOfMenuView: view) 134 | 135 | let percentage = min(max(translation.x / menuWidth * -1, 0), 1) 136 | 137 | switch panGesture.state { 138 | case .began: 139 | interactive = true 140 | 141 | delegate?.flowingMenuNeedsDismissMenu(self) 142 | case .changed: 143 | update(percentage) 144 | default: 145 | interactive = false 146 | 147 | if percentage > 0.2 { 148 | finish() 149 | } 150 | else { 151 | cancel() 152 | } 153 | } 154 | } 155 | 156 | /** 157 | The tap gesture recognizer action methods. It is used to dismiss the 158 | menu. 159 | 160 | - parameter tapGesture: The `UITapGestureRecognizer` sender object. 161 | */ 162 | @objc func tapToDismissAction(_ tapGesture: UITapGestureRecognizer) { 163 | delegate?.flowingMenuNeedsDismissMenu(self) 164 | } 165 | 166 | // MARK: - Building Paths 167 | 168 | /** 169 | Returns a bezier path using the control view positions. 170 | 171 | - returns: A bezier path. 172 | */ 173 | func currentPath() -> CGPath { 174 | let bezierPath = UIBezierPath() 175 | 176 | bezierPath.move(to: CGPoint(x: 0, y: 0)) 177 | bezierPath.addLine(to: CGPoint(x: controlViews[0].center(animating).x, y: 0)) 178 | bezierPath.addCurve(to: controlViews[2].center(animating), controlPoint1: controlViews[0].center(animating), controlPoint2: controlViews[1].center(animating)) 179 | bezierPath.addCurve(to: controlViews[4].center(animating), controlPoint1: controlViews[3].center(animating), controlPoint2: controlViews[4].center(animating)) 180 | bezierPath.addCurve(to: controlViews[6].center(animating), controlPoint1: controlViews[4].center(animating), controlPoint2: controlViews[5].center(animating)) 181 | bezierPath.addLine(to: CGPoint(x: 0, y: controlViews[7].center.y)) 182 | 183 | bezierPath.close() 184 | 185 | return bezierPath.cgPath 186 | } 187 | 188 | // MARK: - Updating Shapes 189 | 190 | /// Update the shape layer using the current control view positions. 191 | @objc func updateShapeLayer() { 192 | shapeLayer.path = currentPath() 193 | } 194 | 195 | /** 196 | Move the control view positions using a position and a wave width. 197 | 198 | - parameter position: The target position. 199 | - parameter waveWidth: The wave width in point. 200 | */ 201 | func moveControlViewsToPoint(_ position: CGPoint, waveWidth: CGFloat) { 202 | let height = controlViews[7].center.y 203 | let minTopY = min((position.y - height / 2) * 0.28, 0) 204 | let maxBottomY = max(height + (position.y - height / 2) * 0.28, height) 205 | 206 | let leftPartWidth = position.y - minTopY 207 | let rightPartWidth = maxBottomY - position.y 208 | 209 | controlViews[0].center = CGPoint(x: position.x, y: minTopY) 210 | controlViews[1].center = CGPoint(x: position.x, y: minTopY + leftPartWidth * 0.44) 211 | controlViews[2].center = CGPoint(x: position.x + waveWidth * 0.64, y: minTopY + leftPartWidth * 0.71) 212 | controlViews[3].center = CGPoint(x: position.x + waveWidth * 1.36, y: position.y) 213 | controlViews[4].center = CGPoint(x: position.x + waveWidth * 0.64, y: maxBottomY - rightPartWidth * 0.71) 214 | controlViews[5].center = CGPoint(x: position.x, y: maxBottomY - (rightPartWidth * 0.44)) 215 | controlViews[6].center = CGPoint(x: position.x, y: height) 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /Sources/FlowingMenuTransitionManager+UIViewControllerAnimatedTransitioning.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * FlowingMenu 3 | * 4 | * Copyright 2015-present Yannick Loriot. 5 | * http://yannickloriot.com 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | import UIKit 28 | 29 | /** 30 | Conforming to the `UIViewControllerAnimatedTransitioning` protocol to manage the 31 | transition animation. 32 | */ 33 | extension FlowingMenuTransitionManager: UIViewControllerAnimatedTransitioning { 34 | /** 35 | Tells your animator object to perform the transition animations. 36 | 37 | The context object containing information about the transition. 38 | */ 39 | public func animateTransition(using context: UIViewControllerContextTransitioning) { 40 | let fromVC = context.viewController(forKey: .from) 41 | let toVC = context.viewController(forKey: .to) 42 | 43 | let containerView = context.containerView 44 | let menuView = animationMode == .presentation ? toVC?.view : fromVC?.view 45 | let otherView = animationMode == .presentation ? fromVC?.view : toVC?.view 46 | 47 | let action = animationMode == .presentation ? presentMenu : dismissMenu 48 | let status = FlowingMenuTransitionStatus(context: context) 49 | 50 | action(menuView!, otherView!, containerView, status, transitionDuration(using: context)) { 51 | let canceled = context.transitionWasCancelled 52 | 53 | context.completeTransition(!canceled) 54 | 55 | if (!canceled) { 56 | if self.animationMode == .presentation { 57 | UIApplication.shared.keyWindow?.insertSubview(fromVC!.view, at:0) 58 | } 59 | } 60 | } 61 | } 62 | 63 | /** 64 | Asks your animator object for the duration (in seconds) of the transition 65 | animation. 66 | 67 | The context object containing information to use during the transition. 68 | */ 69 | public func transitionDuration(using context: UIViewControllerContextTransitioning?) -> TimeInterval { 70 | return interactive ? 0.6 : 0.2 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Sources/FlowingMenuTransitionManager+UIViewControllerTransitioningDelegate.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * FlowingMenu 3 | * 4 | * Copyright 2015-present Yannick Loriot. 5 | * http://yannickloriot.com 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | import UIKit 28 | 29 | /** 30 | Conforming to the `UIViewControllerTransitioningDelegate` protocol to define the 31 | objects used to manage a fixed-length or interactive transition between view 32 | controllers. 33 | */ 34 | extension FlowingMenuTransitionManager: UIViewControllerTransitioningDelegate { 35 | /** 36 | Asks the flowing menu transition manager for the transition animator object to 37 | use when presenting a view controller. 38 | 39 | It returns itself. 40 | */ 41 | public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 42 | animationMode = .presentation 43 | 44 | return self 45 | } 46 | 47 | /** 48 | Asks the flowing menu transition manager for the transition animator object to 49 | use when dismissing a view controller. 50 | 51 | It returns itself. 52 | */ 53 | public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 54 | animationMode = .dismissal 55 | 56 | return self 57 | } 58 | 59 | /** 60 | Asks the flowing menu transition manager for the interactive animator object to 61 | use when presenting a view controller. 62 | */ 63 | public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { 64 | animationMode = .presentation 65 | 66 | return interactive ? self : nil 67 | } 68 | 69 | /** 70 | Asks the flowing menu transition manager for the interactive animator object to 71 | use when dismissing a view controller. 72 | */ 73 | public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { 74 | animationMode = .dismissal 75 | 76 | return interactive ? self : nil 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/FlowingMenuTransitionManager.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * FlowingMenu 3 | * 4 | * Copyright 2015-present Yannick Loriot. 5 | * http://yannickloriot.com 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | import UIKit 28 | import QuartzCore 29 | 30 | /** 31 | The `FlowingMenuTransitionManager` is a concrete subclass of 32 | `UIPercentDrivenInteractiveTransition` which aims to drive the transition between 33 | two views by providing an flowing/elastic and bouncing animation effect. 34 | 35 | You must adopt the `FlowingMenuDelegate` if you want to make the transition 36 | interactive. 37 | */ 38 | public final class FlowingMenuTransitionManager: UIPercentDrivenInteractiveTransition { 39 | // MARK: - Specifying the Delegate 40 | 41 | /** 42 | The delegate for the flowing transition manager. 43 | 44 | The delegate must adopt the `FlowingMenuDelegate` protocol and implement the 45 | required methods to manage the interactive animations. 46 | */ 47 | public weak var delegate: FlowingMenuDelegate? 48 | 49 | // MARK: - Managing the Animation Mode 50 | 51 | /// Defines the animation mode of the transition. 52 | enum AnimationMode { 53 | /// Present the menu mode. 54 | case presentation 55 | /// Dismiss the menu mode. 56 | case dismissal 57 | } 58 | 59 | /// The current animation mode. 60 | var animationMode = AnimationMode.presentation 61 | 62 | // MARK: - Defining Interactive Components 63 | 64 | /// Flag to know when whether the transition is interactive. 65 | var interactive = false 66 | 67 | /// Control views aims to build the elastic shape. 68 | let controlViews = (0 ..< 8).map { _ in UIView() } 69 | /// Shaper layer used to draw the elastic view. 70 | let shapeLayer = CAShapeLayer() 71 | /// Mask to used to create the bubble effect. 72 | let shapeMaskLayer = CAShapeLayer() 73 | /// The display link used to create the bouncing effect. 74 | lazy var displayLink: CADisplayLink = { 75 | let displayLink = CADisplayLink(target: self, selector: #selector(FlowingMenuTransitionManager.updateShapeLayer)) 76 | displayLink.isPaused = true 77 | displayLink.add(to: RunLoop.main, forMode: RunLoop.Mode.default) 78 | 79 | return displayLink 80 | }() 81 | /// Flag to pause/run the display link. 82 | var animating = false { 83 | didSet { 84 | displayLink.isPaused = !animating 85 | } 86 | } 87 | 88 | // MARK: - Working with Animations 89 | 90 | /// Present menu animation. 91 | func presentMenu(_ menuView: UIView, otherView: UIView, containerView: UIView, status: FlowingMenuTransitionStatus, duration: TimeInterval, completion: @escaping () -> Void) { 92 | // Composing the view 93 | guard let ov = otherView.snapshotView(afterScreenUpdates: true) else { return } 94 | 95 | ov.autoresizingMask = [.flexibleWidth, .flexibleHeight] 96 | 97 | containerView.addSubview(ov) 98 | containerView.addSubview(menuView) 99 | 100 | // Add the tap gesture 101 | addTapGesture(ov) 102 | 103 | // Add a mask to the menu to create the bubble effect 104 | let maskLayer = CAShapeLayer() 105 | menuView.layer.mask = maskLayer 106 | 107 | let source = delegate ?? self 108 | let menuWidth = source.flowingMenu(self, widthOfMenuView: menuView) 109 | let maxSideSize = max(menuView.bounds.width, menuView.bounds.height) 110 | let beginRect = CGRect(x: 1, y: menuView.bounds.height / 2 - 1, width: 2, height: 2) 111 | let middleRect = CGRect(x: -menuWidth, y: 0, width: menuWidth * 2, height: menuView.bounds.height) 112 | let endRect = CGRect(x: -maxSideSize, y: menuView.bounds.height / 2 - maxSideSize, width: maxSideSize * 2, height: maxSideSize * 2) 113 | 114 | let beginPath = UIBezierPath(rect: menuView.bounds) 115 | beginPath.append(UIBezierPath(ovalIn: beginRect).reversing()) 116 | 117 | let middlePath = UIBezierPath(rect: menuView.bounds) 118 | middlePath.append(UIBezierPath(ovalIn: middleRect).reversing()) 119 | 120 | let endPath = UIBezierPath(rect: menuView.bounds) 121 | endPath.append(UIBezierPath(ovalIn: endRect).reversing()) 122 | 123 | // Defining the menu frame 124 | var menuFrame = menuView.frame 125 | menuFrame.size.width = menuWidth 126 | menuView.frame = menuFrame 127 | 128 | // Start the animations 129 | if !interactive { 130 | let bubbleAnim = CAKeyframeAnimation(keyPath: "path") 131 | bubbleAnim.values = [beginRect, middleRect, endRect].map { UIBezierPath(ovalIn: $0).cgPath } 132 | bubbleAnim.keyTimes = [0, 0.4, 1] 133 | bubbleAnim.duration = duration 134 | bubbleAnim.isRemovedOnCompletion = false 135 | bubbleAnim.fillMode = CAMediaTimingFillMode.forwards 136 | maskLayer.add(bubbleAnim, forKey: "bubbleAnim") 137 | } 138 | else { 139 | // Last control points help us to know the menu height 140 | controlViews[7].center = CGPoint(x: 0, y: menuView.bounds.height) 141 | 142 | // Be sure there is no animation running 143 | shapeMaskLayer.removeAllAnimations() 144 | 145 | // Retrieve the shape color 146 | let shapeColor = source.colorOfElasticShapeInFlowingMenu(self) ?? menuView.backgroundColor ?? .black 147 | shapeMaskLayer.path = UIBezierPath(rect: ov.bounds).cgPath 148 | shapeLayer.actions = ["position" : NSNull(), "bounds" : NSNull(), "path" : NSNull()] 149 | shapeLayer.backgroundColor = shapeColor.cgColor 150 | shapeLayer.fillColor = shapeColor.cgColor 151 | 152 | // Add the mask to create the bubble effect 153 | shapeLayer.mask = shapeMaskLayer 154 | 155 | // Add the shape layer to container view 156 | containerView.layer.addSublayer(shapeLayer) 157 | 158 | // If the container view change,@objc @objc we update the control points parent 159 | for view in controlViews { 160 | view.removeFromSuperview() 161 | containerView.addSubview(view) 162 | } 163 | } 164 | 165 | containerView.isUserInteractionEnabled = false 166 | 167 | UIView.animate(withDuration: duration, animations: { 168 | menuFrame.origin.x = 0 169 | menuView.frame = menuFrame 170 | otherView.alpha = 0 171 | ov.alpha = 0.4 172 | }) { _ in 173 | if self.interactive && !status.transitionWasCancelled() { 174 | self.interactive = false 175 | 176 | let bubbleAnim = CAKeyframeAnimation(keyPath: "path") 177 | bubbleAnim.values = [beginRect, middleRect, endRect].map { UIBezierPath(ovalIn: $0).cgPath } 178 | bubbleAnim.keyTimes = [0, 0.4, 1] 179 | bubbleAnim.duration = duration 180 | bubbleAnim.isRemovedOnCompletion = false 181 | bubbleAnim.fillMode = CAMediaTimingFillMode.forwards 182 | maskLayer.add(bubbleAnim, forKey: "bubbleAnim") 183 | 184 | let anim = CAKeyframeAnimation(keyPath: "path") 185 | anim.values = [beginPath, middlePath, endPath].map { $0.cgPath } 186 | anim.keyTimes = [0, 0.4, 1] 187 | anim.duration = duration 188 | anim.isRemovedOnCompletion = false 189 | anim.fillMode = CAMediaTimingFillMode.forwards 190 | self.shapeMaskLayer.add(anim, forKey: "bubbleAnim") 191 | 192 | UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0, options: [], animations: { 193 | for view in self.controlViews { 194 | view.center.x = menuWidth 195 | } 196 | }, completion: { _ in 197 | self.shapeLayer.removeFromSuperlayer() 198 | 199 | containerView.isUserInteractionEnabled = true 200 | 201 | menuView.layer.mask = nil 202 | self.animating = false 203 | 204 | completion() 205 | }) 206 | } 207 | else { 208 | menuView.layer.mask = nil 209 | self.animating = false 210 | 211 | containerView.isUserInteractionEnabled = true 212 | 213 | completion() 214 | } 215 | } 216 | } 217 | 218 | /// Dismiss menu animation. 219 | func dismissMenu(_ menuView: UIView, otherView: UIView, containerView: UIView, status: FlowingMenuTransitionStatus, duration: TimeInterval, completion: @escaping () -> Void) { 220 | otherView.frame = containerView.bounds 221 | let ov = otherView.snapshotView(afterScreenUpdates: true) 222 | 223 | var menuFrame = menuView.frame 224 | 225 | containerView.addSubview(otherView) 226 | containerView.addSubview(ov!) 227 | containerView.addSubview(menuView) 228 | 229 | otherView.alpha = 0 230 | ov?.alpha = 0.4 231 | 232 | UIView.animate(withDuration: duration, delay: 0, options: [.curveEaseOut], animations: { 233 | menuFrame.origin.x = -menuFrame.width 234 | menuView.frame = menuFrame 235 | 236 | otherView.alpha = 1 237 | ov?.alpha = 1 238 | }) { _ in 239 | completion() 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /Sources/FlowingMenuTransitionStatus.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * FlowingMenu 3 | * 4 | * Copyright 2015-present Yannick Loriot. 5 | * http://yannickloriot.com 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | import UIKit 28 | 29 | /** 30 | The FlowingMenuTransitionStatus object aims to make the transition manager 31 | testable by providing a concevient way to access the 32 | `UIViewControllerContextTransitioning` `transitionWasCancelled` method. 33 | */ 34 | final class FlowingMenuTransitionStatus { 35 | private let cancelled: Bool 36 | private let context: UIViewControllerContextTransitioning? 37 | 38 | // MARK: - Initializing a TransitionStatus Object 39 | 40 | /// Initializer for testing purpose. 41 | init(cancelled: Bool) { 42 | self.context = nil 43 | self.cancelled = cancelled 44 | } 45 | 46 | /// Initializer for running purpose. 47 | init(context: UIViewControllerContextTransitioning? = nil) { 48 | self.context = context 49 | self.cancelled = false 50 | } 51 | 52 | // MARK: - Reporting the Transition Progress 53 | 54 | /** 55 | Returns a Boolean value indicating whether the transition was canceled. 56 | 57 | true if the transition was canceled or false if it is ongoing or finished 58 | normally. 59 | 60 | - returns: true if the transition was canceled or NO if it is ongoing or 61 | finished normally. 62 | */ 63 | func transitionWasCancelled() -> Bool { 64 | return context?.transitionWasCancelled ?? cancelled 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/UIView.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * FlowingMenu 3 | * 4 | * Copyright 2015-present Yannick Loriot. 5 | * http://yannickloriot.com 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | import UIKit 28 | 29 | /// Adding an helper to UIView to get the presentation layer center point. 30 | extension UIView { 31 | /** 32 | If `usePresentationLayer` is true it'll returns the presentation layer center 33 | point. Returns the center of the view otherwise. 34 | 35 | - parameter usePresentationLayer: A boolean flag to tell which center point 36 | needs to return. 37 | - returns: The center point of the presentation layer or the view according to 38 | the flag. 39 | */ 40 | func center(_ usePresentationLayer: Bool) -> CGPoint { 41 | if usePresentationLayer, let presentationLayer = layer.presentation() as CALayer? { 42 | return presentationLayer.position 43 | } 44 | 45 | return center 46 | } 47 | } 48 | --------------------------------------------------------------------------------