├── .gitignore ├── .swift-version ├── .travis.yml ├── CHANGELOG.md ├── Example ├── .gitignore ├── Jelly.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ ├── Jelly-Example.xcscheme │ │ └── UnitTests.xcscheme ├── Jelly.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Jelly │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── CustomNavController.swift │ ├── DismissMeController.swift │ ├── ExampleType.swift │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-40.png │ │ │ ├── Icon-40@2x.png │ │ │ ├── Icon-40@3x.png │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ ├── Icon-72.png │ │ │ ├── Icon-72@2x.png │ │ │ ├── Icon-76.png │ │ │ ├── Icon-76@2x.png │ │ │ ├── Icon-83.5@2x.png │ │ │ ├── Icon-Small-50.png │ │ │ ├── Icon-Small-50@2x.png │ │ │ ├── Icon-Small.png │ │ │ ├── Icon-Small@2x.png │ │ │ ├── Icon-Small@3x.png │ │ │ ├── Icon.png │ │ │ ├── Icon@2x.png │ │ │ ├── NotificationIcon@2x.png │ │ │ ├── NotificationIcon@3x.png │ │ │ ├── NotificationIcon~ipad.png │ │ │ ├── NotificationIcon~ipad@2x.png │ │ │ └── ios-marketing.png │ │ ├── Contents.json │ │ └── Jelly-Logo.imageset │ │ │ ├── Contents.json │ │ │ └── Jellyfish.png │ ├── Info.plist │ ├── MainViewController.swift │ ├── MenuViewController.swift │ └── RoundedView.swift ├── Podfile ├── Podfile.lock └── UnitTests │ ├── Info.plist │ └── JellyAnimatorSpec.swift ├── Github ├── IMG_0240.TRIM.gif ├── IMG_0242.TRIM.gif ├── IMG_0244.TRIM.gif ├── IMG_0246.TRIM.gif ├── IMG_0248.TRIM.gif ├── IMG_0250.TRIM.gif ├── IMG_0252.TRIM.gif ├── IMG_0254.TRIM.gif ├── Jellyfish.png ├── author.png ├── customization.png ├── emoji.png ├── how to.png ├── installation.png ├── interactive-transitions.png ├── license.png ├── mentions.png ├── requirements.png └── update.png ├── Jelly.podspec ├── Jelly ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── private │ ├── Animators │ │ ├── Animated Animators │ │ │ ├── CoverAnimator.swift │ │ │ ├── FadeAnimator.swift │ │ │ └── SlideAnimator.swift │ │ └── Interaction Controllers │ │ │ └── InteractionController.swift │ ├── Extensions │ │ ├── AnimationCurve+AnimationOptions.swift │ │ ├── Direction+EdgeHelper.swift │ │ ├── Direction+Orientation.swift │ │ ├── Spring+Values.swift │ │ ├── UIView+RoundCorners.swift │ │ └── UIViewControllerAnimatedTransitioning+Convenience.swift │ ├── Models │ │ ├── CombinedTimingCurveProvider.swift │ │ └── PresentationType.swift │ ├── PresentationController.swift │ └── ViewControllerSizeCalculator.swift │ └── public │ ├── Animator.swift │ ├── Constants.swift │ ├── Deprecation.swift │ ├── Extensions │ └── Size+Equatable.swift │ ├── LiveUpdateError.swift │ └── Models │ ├── Configuration │ ├── BackgroundStyle.swift │ ├── Direction.swift │ ├── DragMode.swift │ ├── Duration.swift │ ├── HorizontalAlignment.swift │ ├── Orientation.swift │ ├── Presentation │ │ ├── InteractionConfiguration.swift │ │ ├── InteractionMode.swift │ │ ├── PresentationAlignment.swift │ │ ├── PresentationSize.swift │ │ ├── PresentationTiming.swift │ │ └── PresentationUIConfiguration.swift │ ├── Size.swift │ ├── Spring.swift │ └── VerticalAlignment.swift │ ├── Presentations │ ├── CoverPresentation.swift │ ├── FadePresentation.swift │ └── SlidePresentation.swift │ └── Protocols │ ├── InteractionConfigurationProvider.swift │ ├── Presentation.swift │ ├── PresentationAlignmentProvider.swift │ ├── PresentationAnimatorProvider.swift │ ├── PresentationDirectionDismissProvider.swift │ ├── PresentationDirectionShowProvider.swift │ ├── PresentationHeightProvider.swift │ ├── PresentationInteractiveAnimatorProvider.swift │ ├── PresentationMarginGuardsProvider.swift │ ├── PresentationSingleSizeProvider.swift │ ├── PresentationSizeProtocol.swift │ ├── PresentationSizeProvider.swift │ ├── PresentationSpringProvider.swift │ ├── PresentationTimingInformationProvider.swift │ ├── PresentationTimingProtocol.swift │ ├── PresentationUIConfigurationProvider.swift │ └── PresentationWidthProvider.swift ├── LICENSE ├── Package.swift ├── README.md └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | 6 | ## Build generated 7 | build/ 8 | DerivedData 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata 20 | 21 | ## Other 22 | *.xccheckout 23 | *.moved-aside 24 | *.xcuserstate 25 | *.xcscmblueprint 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | .build/ 37 | 38 | # Carthage 39 | Carthage/Build 40 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode10 6 | language: objective-c 7 | # cache: cocoapods 8 | podfile: Example/Podfile 9 | before_install: 10 | - pod repo update 11 | - gem install cocoapods # Since Travis is not always on latest version 12 | - pod install --project-directory=Example 13 | script: 14 | - set -o pipefail && xcodebuild -workspace Example/Jelly.xcworkspace -scheme Jelly-Example -sdk iphonesimulator12.0 -destination 'platform=iOS Simulator,name=iPhone X,OS=12.0' ONLY_ACTIVE_ARCH=NO | xcpretty 15 | # - pod lib lint 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.3.0 (2022-4-31) 2 | - Xcode 13 support 3 | - SPM support 4 | 5 | ## 2.2.2 (2019-5-8) 6 | - fixed iOS13 issues 7 | 8 | ## 2.2.1 (2019-5-6) 9 | - decreased minimum deployment target to iOS10 10 | 11 | ## 2.2.0 (2019-13-4) 12 | - Swift 5.0 adaptions 13 | 14 | ## 2.1.1 (2018-12-1) 15 | - flashing blur effect bugfix 16 | 17 | ## 2.1.0 (2018-12-5) 18 | - new parallax option on slide presentations 19 | - interaction configuration can now be constrained by mode (.dismiss, .present) 20 | 21 | ## 2.0.0 (2018-11-12) 22 | - interactive transitions are now possible 23 | - renamed FadeIn, ShiftIn, and SlideIn to Fade, Slide & Cover 24 | - the animator is now capable of resizing and realigning already presented viewControllers 25 | - presentations will be configured using new dedicated models for size, alignment etc. 26 | - removed the Jelly Prefix from all classes 27 | - extended example app with interactive examples like slide out menu etc. 28 | - minimum deployment target is iOS 10 now, because of UIViewPropertyAnimator 29 | - new Logo and overall design of Jelly related icons and ui 30 | 31 | ## 1.2.5 (2018-10-02) 32 | - Swift 4.2 support 33 | 34 | ## 1.2.4 (2017-11-30) 35 | - Swift 4 support 36 | - custom postion option is now available though horizontal and vertical alignment parameters 37 | - fixed dimming view alpha issue 38 | 39 | ## 1.2.3 (2017-05-26) 40 | - Code cleanup 41 | - Removed visual format language based code and replaced it using layout anchors 42 | - Carthage support 43 | - iOS 8 support 44 | 45 | ## 1.2.2 (2017-01-21) 46 | - Removed .none as backgroundStyle and replaced it with .dimmed and an associated value: .dimmed(alpha: CGFloat) 47 | - Code cleanup 48 | 49 | ## 1.2.1 (2017-01-11) 50 | - Fixed issue with background tap dismissal options 51 | 52 | ## 1.2.0 (2016-12-18) 53 | - There is a new cool animation style called shiftIn Animation. Check out the README to find out more 54 | - Rounding Corners can now be specified unsing the corners property (e.g. presentation.corners = [.leftBottom,rightTop]) 55 | - You can disable tap to dismiss by setting isTapBackgroundToDismissEnabled to false 56 | - You can now set properties without using the basic initaliziers 57 | - Code clean up 58 | - Startet with integrating Unit-Tests 59 | 60 | 61 | ## 1.0.2 (2016-11-27) 62 | 63 | - New size options (fullscreen, halfscreen and custom) for width and height instead of plain CGSize 64 | - New alignment options for NonFullscreen Presentations 65 | - New marging guards - if the size you specified is bigger than the screen, the margin guards kick in and will be applied to your vc 66 | - If you want to size your vc using margin you can use .fullscreen as a size and set the marginGuards to constraint your vcs dimensions 67 | - Better Code documentation and overall structure 68 | - Even more Example inside the Readme and Example App 69 | 70 | ## 1.0.1 (2016-11-20) 71 | 72 | Minor Version update: 73 | 74 | - updated version number so Cocoapods can reflect all changes made in the Readme File 75 | 76 | ## 1.0 (2016-11-20) 77 | 78 | - 1.0 Provides SlideIn and FadeIn Transitions 79 | - Customization Options like corner-radius, background style included 80 | - code cleanup 81 | -------------------------------------------------------------------------------- /Example/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/cocoapods,xcode,swift 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=cocoapods,xcode,swift 4 | 5 | ### CocoaPods ### 6 | ## CocoaPods GitIgnore Template 7 | 8 | # CocoaPods - Only use to conserve bandwidth / Save time on Pushing 9 | # - Also handy if you have a large number of dependant pods 10 | # - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGNORE THE LOCK FILE 11 | Pods/ 12 | 13 | ### Swift ### 14 | # Xcode 15 | # 16 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 17 | 18 | ## User settings 19 | xcuserdata/ 20 | 21 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 22 | *.xcscmblueprint 23 | *.xccheckout 24 | 25 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 26 | build/ 27 | DerivedData/ 28 | *.moved-aside 29 | *.pbxuser 30 | !default.pbxuser 31 | *.mode1v3 32 | !default.mode1v3 33 | *.mode2v3 34 | !default.mode2v3 35 | *.perspectivev3 36 | !default.perspectivev3 37 | 38 | ## Obj-C/Swift specific 39 | *.hmap 40 | 41 | ## App packaging 42 | *.ipa 43 | *.dSYM.zip 44 | *.dSYM 45 | 46 | ## Playgrounds 47 | timeline.xctimeline 48 | playground.xcworkspace 49 | 50 | # Swift Package Manager 51 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 52 | # Packages/ 53 | # Package.pins 54 | # Package.resolved 55 | # *.xcodeproj 56 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 57 | # hence it is not needed unless you have added a package configuration file to your project 58 | # .swiftpm 59 | 60 | .build/ 61 | 62 | # CocoaPods 63 | # We recommend against adding the Pods directory to your .gitignore. However 64 | # you should judge for yourself, the pros and cons are mentioned at: 65 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 66 | # Pods/ 67 | # Add this line if you want to avoid checking in source code from the Xcode workspace 68 | # *.xcworkspace 69 | 70 | # Carthage 71 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 72 | # Carthage/Checkouts 73 | 74 | Carthage/Build/ 75 | 76 | # Accio dependency management 77 | Dependencies/ 78 | .accio/ 79 | 80 | # fastlane 81 | # It is recommended to not store the screenshots in the git repo. 82 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 83 | # For more information about the recommended setup visit: 84 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 85 | 86 | fastlane/report.xml 87 | fastlane/Preview.html 88 | fastlane/screenshots/**/*.png 89 | fastlane/test_output 90 | 91 | # Code Injection 92 | # After new code Injection tools there's a generated folder /iOSInjectionProject 93 | # https://github.com/johnno1962/injectionforxcode 94 | 95 | iOSInjectionProject/ 96 | 97 | ### Xcode ### 98 | # Xcode 99 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 100 | 101 | 102 | 103 | 104 | ## Gcc Patch 105 | /*.gcno 106 | 107 | ### Xcode Patch ### 108 | *.xcodeproj/* 109 | !*.xcodeproj/project.pbxproj 110 | !*.xcodeproj/xcshareddata/ 111 | !*.xcworkspace/contents.xcworkspacedata 112 | **/xcshareddata/WorkspaceSettings.xcsettings 113 | 114 | # End of https://www.toptal.com/developers/gitignore/api/cocoapods,xcode,swift -------------------------------------------------------------------------------- /Example/Jelly.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 150401C1218C46B300BCBF88 /* CustomNavController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 150401C0218C46B300BCBF88 /* CustomNavController.swift */; }; 11 | 1510AAA5218DFDE000468AC3 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1510AAA4218DFDE000468AC3 /* MainViewController.swift */; }; 12 | 1545094A1DF6084C0096C822 /* JellyAnimatorSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 154509491DF6084C0096C822 /* JellyAnimatorSpec.swift */; }; 13 | 15B3AD2D1DE1C5CB003FF7B8 /* DismissMeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15B3AD2C1DE1C5CB003FF7B8 /* DismissMeController.swift */; }; 14 | 15DBFCDC21958B0E00E5176E /* RoundedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15DBFCDB21958B0E00E5176E /* RoundedView.swift */; }; 15 | 15DBFCDF219592C800E5176E /* ExampleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15DBFCDE219592C800E5176E /* ExampleType.swift */; }; 16 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 17 | 607FACD81AFB9204008FA782 /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* MenuViewController.swift */; }; 18 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 19 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 20 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 21 | B3BC8254507974EB7B5A1D14 /* Pods_UnitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5AFAFB92AA38A5EEFE73A472 /* Pods_UnitTests.framework */; }; 22 | B92897DBACEBF00D5323A229 /* Pods_Jelly_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 736CDAA01B6042E600C53DAC /* Pods_Jelly_Example.framework */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 31 | remoteInfo = Jelly; 32 | }; 33 | /* End PBXContainerItemProxy section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 150401C0218C46B300BCBF88 /* CustomNavController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNavController.swift; sourceTree = ""; }; 37 | 1510AAA4218DFDE000468AC3 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 38 | 152009C91E26C6EA00537CD5 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = ""; }; 39 | 154175841E337E4F00503A99 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 40 | 154509491DF6084C0096C822 /* JellyAnimatorSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JellyAnimatorSpec.swift; sourceTree = ""; }; 41 | 158575E121949581007B2E4B /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; name = .travis.yml; path = ../.travis.yml; sourceTree = ""; }; 42 | 158575E221949581007B2E4B /* .swift-version */ = {isa = PBXFileReference; lastKnownFileType = text; name = ".swift-version"; path = "../.swift-version"; sourceTree = ""; }; 43 | 158575E321949581007B2E4B /* .git */ = {isa = PBXFileReference; lastKnownFileType = folder; name = .git; path = ../.git; sourceTree = ""; }; 44 | 158575E421949581007B2E4B /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; name = .gitignore; path = ../.gitignore; sourceTree = ""; }; 45 | 15B1CF991DEB7299003B4107 /* Github */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Github; path = ../Github; sourceTree = ""; }; 46 | 15B3AD2C1DE1C5CB003FF7B8 /* DismissMeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DismissMeController.swift; sourceTree = ""; }; 47 | 15DBFCDB21958B0E00E5176E /* RoundedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedView.swift; sourceTree = ""; }; 48 | 15DBFCDE219592C800E5176E /* ExampleType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleType.swift; sourceTree = ""; }; 49 | 5AFAFB92AA38A5EEFE73A472 /* Pods_UnitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_UnitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 607FACD01AFB9204008FA782 /* Jelly_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Jelly_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 53 | 607FACD71AFB9204008FA782 /* MenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = ""; }; 54 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 55 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 56 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 57 | 607FACE51AFB9204008FA782 /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | 736CDAA01B6042E600C53DAC /* Pods_Jelly_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Jelly_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | 7B462190C38D224BBD9D6872 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 61 | 7D2A6F270568299AA6D317E2 /* Pods-Jelly_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Jelly_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Jelly_Tests/Pods-Jelly_Tests.release.xcconfig"; sourceTree = ""; }; 62 | 83C72594F63A668F7E6D6614 /* Pods-UnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.debug.xcconfig"; sourceTree = ""; }; 63 | 892F430A239ED6F02F960085 /* Pods-Jelly_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Jelly_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Jelly_Example/Pods-Jelly_Example.release.xcconfig"; sourceTree = ""; }; 64 | AFC328A4317DF517CA68D06D /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 65 | BABAA34872E633F4B994ABEC /* Pods-Jelly_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Jelly_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Jelly_Example/Pods-Jelly_Example.debug.xcconfig"; sourceTree = ""; }; 66 | C583DF0440648942A3BA1AF6 /* Pods-UnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.release.xcconfig"; sourceTree = ""; }; 67 | C61DBA24224C9AE29B62761C /* Pods-Jelly_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Jelly_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Jelly_Tests/Pods-Jelly_Tests.debug.xcconfig"; sourceTree = ""; }; 68 | E9A844FC3F84D189C8ED57C9 /* Jelly.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Jelly.podspec; path = ../Jelly.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 69 | /* End PBXFileReference section */ 70 | 71 | /* Begin PBXFrameworksBuildPhase section */ 72 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | B92897DBACEBF00D5323A229 /* Pods_Jelly_Example.framework in Frameworks */, 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | B3BC8254507974EB7B5A1D14 /* Pods_UnitTests.framework in Frameworks */, 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | /* End PBXFrameworksBuildPhase section */ 89 | 90 | /* Begin PBXGroup section */ 91 | 150401BC218C3E3800BCBF88 /* Resources */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 607FACD91AFB9204008FA782 /* Main.storyboard */, 95 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 96 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 97 | ); 98 | name = Resources; 99 | sourceTree = ""; 100 | }; 101 | 150401BD218C3E4400BCBF88 /* Sources */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 15DBFCDD21958FEC00E5176E /* Enums */, 105 | 15DBFCDA21958AF400E5176E /* Views */, 106 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 107 | 607FACD71AFB9204008FA782 /* MenuViewController.swift */, 108 | 15B3AD2C1DE1C5CB003FF7B8 /* DismissMeController.swift */, 109 | 150401C0218C46B300BCBF88 /* CustomNavController.swift */, 110 | 1510AAA4218DFDE000468AC3 /* MainViewController.swift */, 111 | ); 112 | name = Sources; 113 | sourceTree = ""; 114 | }; 115 | 15DBFCDA21958AF400E5176E /* Views */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 15DBFCDB21958B0E00E5176E /* RoundedView.swift */, 119 | ); 120 | name = Views; 121 | sourceTree = ""; 122 | }; 123 | 15DBFCDD21958FEC00E5176E /* Enums */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 15DBFCDE219592C800E5176E /* ExampleType.swift */, 127 | ); 128 | name = Enums; 129 | sourceTree = ""; 130 | }; 131 | 39CE1241BD68C0D6255E3163 /* Frameworks */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 736CDAA01B6042E600C53DAC /* Pods_Jelly_Example.framework */, 135 | 5AFAFB92AA38A5EEFE73A472 /* Pods_UnitTests.framework */, 136 | ); 137 | name = Frameworks; 138 | sourceTree = ""; 139 | }; 140 | 607FACC71AFB9204008FA782 = { 141 | isa = PBXGroup; 142 | children = ( 143 | 607FACD21AFB9204008FA782 /* Example for Jelly */, 144 | 607FACE81AFB9204008FA782 /* UnitTests */, 145 | 607FACD11AFB9204008FA782 /* Products */, 146 | A30E2D426D45D2832CB99640 /* Pods */, 147 | 39CE1241BD68C0D6255E3163 /* Frameworks */, 148 | 158575E321949581007B2E4B /* .git */, 149 | 15B1CF991DEB7299003B4107 /* Github */, 150 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 151 | 158575E421949581007B2E4B /* .gitignore */, 152 | 158575E221949581007B2E4B /* .swift-version */, 153 | 158575E121949581007B2E4B /* .travis.yml */, 154 | 154175841E337E4F00503A99 /* README.md */, 155 | 152009C91E26C6EA00537CD5 /* CHANGELOG.md */, 156 | ); 157 | sourceTree = ""; 158 | }; 159 | 607FACD11AFB9204008FA782 /* Products */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 607FACD01AFB9204008FA782 /* Jelly_Example.app */, 163 | 607FACE51AFB9204008FA782 /* UnitTests.xctest */, 164 | ); 165 | name = Products; 166 | sourceTree = ""; 167 | }; 168 | 607FACD21AFB9204008FA782 /* Example for Jelly */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | 150401BD218C3E4400BCBF88 /* Sources */, 172 | 150401BC218C3E3800BCBF88 /* Resources */, 173 | 607FACD31AFB9204008FA782 /* Supporting Files */, 174 | ); 175 | name = "Example for Jelly"; 176 | path = Jelly; 177 | sourceTree = ""; 178 | }; 179 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 607FACD41AFB9204008FA782 /* Info.plist */, 183 | ); 184 | name = "Supporting Files"; 185 | sourceTree = ""; 186 | }; 187 | 607FACE81AFB9204008FA782 /* UnitTests */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 607FACE91AFB9204008FA782 /* Supporting Files */, 191 | 154509491DF6084C0096C822 /* JellyAnimatorSpec.swift */, 192 | ); 193 | path = UnitTests; 194 | sourceTree = ""; 195 | }; 196 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | 607FACEA1AFB9204008FA782 /* Info.plist */, 200 | ); 201 | name = "Supporting Files"; 202 | sourceTree = ""; 203 | }; 204 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 205 | isa = PBXGroup; 206 | children = ( 207 | E9A844FC3F84D189C8ED57C9 /* Jelly.podspec */, 208 | AFC328A4317DF517CA68D06D /* README.md */, 209 | 7B462190C38D224BBD9D6872 /* LICENSE */, 210 | ); 211 | name = "Podspec Metadata"; 212 | sourceTree = ""; 213 | }; 214 | A30E2D426D45D2832CB99640 /* Pods */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | BABAA34872E633F4B994ABEC /* Pods-Jelly_Example.debug.xcconfig */, 218 | 892F430A239ED6F02F960085 /* Pods-Jelly_Example.release.xcconfig */, 219 | C61DBA24224C9AE29B62761C /* Pods-Jelly_Tests.debug.xcconfig */, 220 | 7D2A6F270568299AA6D317E2 /* Pods-Jelly_Tests.release.xcconfig */, 221 | 83C72594F63A668F7E6D6614 /* Pods-UnitTests.debug.xcconfig */, 222 | C583DF0440648942A3BA1AF6 /* Pods-UnitTests.release.xcconfig */, 223 | ); 224 | name = Pods; 225 | sourceTree = ""; 226 | }; 227 | /* End PBXGroup section */ 228 | 229 | /* Begin PBXNativeTarget section */ 230 | 607FACCF1AFB9204008FA782 /* Jelly_Example */ = { 231 | isa = PBXNativeTarget; 232 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "Jelly_Example" */; 233 | buildPhases = ( 234 | B2781AA82A85D96A58D0E8A3 /* [CP] Check Pods Manifest.lock */, 235 | 607FACCC1AFB9204008FA782 /* Sources */, 236 | 607FACCD1AFB9204008FA782 /* Frameworks */, 237 | 607FACCE1AFB9204008FA782 /* Resources */, 238 | 195989B18179C9E2748853BA /* [CP] Embed Pods Frameworks */, 239 | ); 240 | buildRules = ( 241 | ); 242 | dependencies = ( 243 | ); 244 | name = Jelly_Example; 245 | productName = Jelly; 246 | productReference = 607FACD01AFB9204008FA782 /* Jelly_Example.app */; 247 | productType = "com.apple.product-type.application"; 248 | }; 249 | 607FACE41AFB9204008FA782 /* UnitTests */ = { 250 | isa = PBXNativeTarget; 251 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "UnitTests" */; 252 | buildPhases = ( 253 | 1B8F42FEC6C1DDC804A21529 /* [CP] Check Pods Manifest.lock */, 254 | 607FACE11AFB9204008FA782 /* Sources */, 255 | 607FACE21AFB9204008FA782 /* Frameworks */, 256 | 607FACE31AFB9204008FA782 /* Resources */, 257 | 9D2F901A1AA044C279BC9688 /* [CP] Embed Pods Frameworks */, 258 | ); 259 | buildRules = ( 260 | ); 261 | dependencies = ( 262 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 263 | ); 264 | name = UnitTests; 265 | productName = Tests; 266 | productReference = 607FACE51AFB9204008FA782 /* UnitTests.xctest */; 267 | productType = "com.apple.product-type.bundle.unit-test"; 268 | }; 269 | /* End PBXNativeTarget section */ 270 | 271 | /* Begin PBXProject section */ 272 | 607FACC81AFB9204008FA782 /* Project object */ = { 273 | isa = PBXProject; 274 | attributes = { 275 | LastSwiftUpdateCheck = 0720; 276 | LastUpgradeCheck = 1020; 277 | ORGANIZATIONNAME = CocoaPods; 278 | TargetAttributes = { 279 | 607FACCF1AFB9204008FA782 = { 280 | CreatedOnToolsVersion = 6.3.1; 281 | DevelopmentTeam = JY9STJR6M4; 282 | }; 283 | 607FACE41AFB9204008FA782 = { 284 | CreatedOnToolsVersion = 6.3.1; 285 | DevelopmentTeam = JY9STJR6M4; 286 | LastSwiftMigration = 0810; 287 | TestTargetID = 607FACCF1AFB9204008FA782; 288 | }; 289 | }; 290 | }; 291 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "Jelly" */; 292 | compatibilityVersion = "Xcode 3.2"; 293 | developmentRegion = English; 294 | hasScannedForEncodings = 0; 295 | knownRegions = ( 296 | English, 297 | en, 298 | Base, 299 | ); 300 | mainGroup = 607FACC71AFB9204008FA782; 301 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 302 | projectDirPath = ""; 303 | projectRoot = ""; 304 | targets = ( 305 | 607FACCF1AFB9204008FA782 /* Jelly_Example */, 306 | 607FACE41AFB9204008FA782 /* UnitTests */, 307 | ); 308 | }; 309 | /* End PBXProject section */ 310 | 311 | /* Begin PBXResourcesBuildPhase section */ 312 | 607FACCE1AFB9204008FA782 /* Resources */ = { 313 | isa = PBXResourcesBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 317 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 318 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | }; 322 | 607FACE31AFB9204008FA782 /* Resources */ = { 323 | isa = PBXResourcesBuildPhase; 324 | buildActionMask = 2147483647; 325 | files = ( 326 | ); 327 | runOnlyForDeploymentPostprocessing = 0; 328 | }; 329 | /* End PBXResourcesBuildPhase section */ 330 | 331 | /* Begin PBXShellScriptBuildPhase section */ 332 | 195989B18179C9E2748853BA /* [CP] Embed Pods Frameworks */ = { 333 | isa = PBXShellScriptBuildPhase; 334 | buildActionMask = 2147483647; 335 | files = ( 336 | ); 337 | inputPaths = ( 338 | "${PODS_ROOT}/Target Support Files/Pods-Jelly_Example/Pods-Jelly_Example-frameworks.sh", 339 | "${BUILT_PRODUCTS_DIR}/Jelly/Jelly.framework", 340 | "${BUILT_PRODUCTS_DIR}/TouchVisualizer/TouchVisualizer.framework", 341 | ); 342 | name = "[CP] Embed Pods Frameworks"; 343 | outputPaths = ( 344 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Jelly.framework", 345 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TouchVisualizer.framework", 346 | ); 347 | runOnlyForDeploymentPostprocessing = 0; 348 | shellPath = /bin/sh; 349 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Jelly_Example/Pods-Jelly_Example-frameworks.sh\"\n"; 350 | showEnvVarsInLog = 0; 351 | }; 352 | 1B8F42FEC6C1DDC804A21529 /* [CP] Check Pods Manifest.lock */ = { 353 | isa = PBXShellScriptBuildPhase; 354 | buildActionMask = 2147483647; 355 | files = ( 356 | ); 357 | inputPaths = ( 358 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 359 | "${PODS_ROOT}/Manifest.lock", 360 | ); 361 | name = "[CP] Check Pods Manifest.lock"; 362 | outputPaths = ( 363 | "$(DERIVED_FILE_DIR)/Pods-UnitTests-checkManifestLockResult.txt", 364 | ); 365 | runOnlyForDeploymentPostprocessing = 0; 366 | shellPath = /bin/sh; 367 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 368 | showEnvVarsInLog = 0; 369 | }; 370 | 9D2F901A1AA044C279BC9688 /* [CP] Embed Pods Frameworks */ = { 371 | isa = PBXShellScriptBuildPhase; 372 | buildActionMask = 2147483647; 373 | files = ( 374 | ); 375 | inputPaths = ( 376 | "${PODS_ROOT}/Target Support Files/Pods-UnitTests/Pods-UnitTests-frameworks.sh", 377 | "${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework", 378 | "${BUILT_PRODUCTS_DIR}/Quick/Quick.framework", 379 | ); 380 | name = "[CP] Embed Pods Frameworks"; 381 | outputPaths = ( 382 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", 383 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", 384 | ); 385 | runOnlyForDeploymentPostprocessing = 0; 386 | shellPath = /bin/sh; 387 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-UnitTests/Pods-UnitTests-frameworks.sh\"\n"; 388 | showEnvVarsInLog = 0; 389 | }; 390 | B2781AA82A85D96A58D0E8A3 /* [CP] Check Pods Manifest.lock */ = { 391 | isa = PBXShellScriptBuildPhase; 392 | buildActionMask = 2147483647; 393 | files = ( 394 | ); 395 | inputPaths = ( 396 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 397 | "${PODS_ROOT}/Manifest.lock", 398 | ); 399 | name = "[CP] Check Pods Manifest.lock"; 400 | outputPaths = ( 401 | "$(DERIVED_FILE_DIR)/Pods-Jelly_Example-checkManifestLockResult.txt", 402 | ); 403 | runOnlyForDeploymentPostprocessing = 0; 404 | shellPath = /bin/sh; 405 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 406 | showEnvVarsInLog = 0; 407 | }; 408 | /* End PBXShellScriptBuildPhase section */ 409 | 410 | /* Begin PBXSourcesBuildPhase section */ 411 | 607FACCC1AFB9204008FA782 /* Sources */ = { 412 | isa = PBXSourcesBuildPhase; 413 | buildActionMask = 2147483647; 414 | files = ( 415 | 15DBFCDF219592C800E5176E /* ExampleType.swift in Sources */, 416 | 15B3AD2D1DE1C5CB003FF7B8 /* DismissMeController.swift in Sources */, 417 | 607FACD81AFB9204008FA782 /* MenuViewController.swift in Sources */, 418 | 1510AAA5218DFDE000468AC3 /* MainViewController.swift in Sources */, 419 | 150401C1218C46B300BCBF88 /* CustomNavController.swift in Sources */, 420 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 421 | 15DBFCDC21958B0E00E5176E /* RoundedView.swift in Sources */, 422 | ); 423 | runOnlyForDeploymentPostprocessing = 0; 424 | }; 425 | 607FACE11AFB9204008FA782 /* Sources */ = { 426 | isa = PBXSourcesBuildPhase; 427 | buildActionMask = 2147483647; 428 | files = ( 429 | 1545094A1DF6084C0096C822 /* JellyAnimatorSpec.swift in Sources */, 430 | ); 431 | runOnlyForDeploymentPostprocessing = 0; 432 | }; 433 | /* End PBXSourcesBuildPhase section */ 434 | 435 | /* Begin PBXTargetDependency section */ 436 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 437 | isa = PBXTargetDependency; 438 | target = 607FACCF1AFB9204008FA782 /* Jelly_Example */; 439 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 440 | }; 441 | /* End PBXTargetDependency section */ 442 | 443 | /* Begin PBXVariantGroup section */ 444 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = { 445 | isa = PBXVariantGroup; 446 | children = ( 447 | 607FACDA1AFB9204008FA782 /* Base */, 448 | ); 449 | name = Main.storyboard; 450 | sourceTree = ""; 451 | }; 452 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 453 | isa = PBXVariantGroup; 454 | children = ( 455 | 607FACDF1AFB9204008FA782 /* Base */, 456 | ); 457 | name = LaunchScreen.xib; 458 | sourceTree = ""; 459 | }; 460 | /* End PBXVariantGroup section */ 461 | 462 | /* Begin XCBuildConfiguration section */ 463 | 607FACED1AFB9204008FA782 /* Debug */ = { 464 | isa = XCBuildConfiguration; 465 | buildSettings = { 466 | ALWAYS_SEARCH_USER_PATHS = NO; 467 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 468 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 469 | CLANG_CXX_LIBRARY = "libc++"; 470 | CLANG_ENABLE_MODULES = YES; 471 | CLANG_ENABLE_OBJC_ARC = YES; 472 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 473 | CLANG_WARN_BOOL_CONVERSION = YES; 474 | CLANG_WARN_COMMA = YES; 475 | CLANG_WARN_CONSTANT_CONVERSION = YES; 476 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 477 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 478 | CLANG_WARN_EMPTY_BODY = YES; 479 | CLANG_WARN_ENUM_CONVERSION = YES; 480 | CLANG_WARN_INFINITE_RECURSION = YES; 481 | CLANG_WARN_INT_CONVERSION = YES; 482 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 483 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 484 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 485 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 486 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 487 | CLANG_WARN_STRICT_PROTOTYPES = YES; 488 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 489 | CLANG_WARN_UNREACHABLE_CODE = YES; 490 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 491 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 492 | COPY_PHASE_STRIP = NO; 493 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 494 | ENABLE_STRICT_OBJC_MSGSEND = YES; 495 | ENABLE_TESTABILITY = YES; 496 | GCC_C_LANGUAGE_STANDARD = gnu99; 497 | GCC_DYNAMIC_NO_PIC = NO; 498 | GCC_NO_COMMON_BLOCKS = YES; 499 | GCC_OPTIMIZATION_LEVEL = 0; 500 | GCC_PREPROCESSOR_DEFINITIONS = ( 501 | "DEBUG=1", 502 | "$(inherited)", 503 | ); 504 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 505 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 506 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 507 | GCC_WARN_UNDECLARED_SELECTOR = YES; 508 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 509 | GCC_WARN_UNUSED_FUNCTION = YES; 510 | GCC_WARN_UNUSED_VARIABLE = YES; 511 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 512 | MTL_ENABLE_DEBUG_INFO = YES; 513 | ONLY_ACTIVE_ARCH = YES; 514 | SDKROOT = iphoneos; 515 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 516 | SWIFT_VERSION = 5.0; 517 | }; 518 | name = Debug; 519 | }; 520 | 607FACEE1AFB9204008FA782 /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | buildSettings = { 523 | ALWAYS_SEARCH_USER_PATHS = NO; 524 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 525 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 526 | CLANG_CXX_LIBRARY = "libc++"; 527 | CLANG_ENABLE_MODULES = YES; 528 | CLANG_ENABLE_OBJC_ARC = YES; 529 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 530 | CLANG_WARN_BOOL_CONVERSION = YES; 531 | CLANG_WARN_COMMA = YES; 532 | CLANG_WARN_CONSTANT_CONVERSION = YES; 533 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 534 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 535 | CLANG_WARN_EMPTY_BODY = YES; 536 | CLANG_WARN_ENUM_CONVERSION = YES; 537 | CLANG_WARN_INFINITE_RECURSION = YES; 538 | CLANG_WARN_INT_CONVERSION = YES; 539 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 540 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 541 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 542 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 543 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 544 | CLANG_WARN_STRICT_PROTOTYPES = YES; 545 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 546 | CLANG_WARN_UNREACHABLE_CODE = YES; 547 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 548 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 549 | COPY_PHASE_STRIP = NO; 550 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 551 | ENABLE_NS_ASSERTIONS = NO; 552 | ENABLE_STRICT_OBJC_MSGSEND = YES; 553 | GCC_C_LANGUAGE_STANDARD = gnu99; 554 | GCC_NO_COMMON_BLOCKS = YES; 555 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 556 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 557 | GCC_WARN_UNDECLARED_SELECTOR = YES; 558 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 559 | GCC_WARN_UNUSED_FUNCTION = YES; 560 | GCC_WARN_UNUSED_VARIABLE = YES; 561 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 562 | MTL_ENABLE_DEBUG_INFO = NO; 563 | SDKROOT = iphoneos; 564 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 565 | SWIFT_VERSION = 5.0; 566 | VALIDATE_PRODUCT = YES; 567 | }; 568 | name = Release; 569 | }; 570 | 607FACF01AFB9204008FA782 /* Debug */ = { 571 | isa = XCBuildConfiguration; 572 | baseConfigurationReference = BABAA34872E633F4B994ABEC /* Pods-Jelly_Example.debug.xcconfig */; 573 | buildSettings = { 574 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 575 | DEVELOPMENT_TEAM = JY9STJR6M4; 576 | INFOPLIST_FILE = Jelly/Info.plist; 577 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 578 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 579 | MODULE_NAME = ExampleApp; 580 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.demo.Jelly; 581 | PRODUCT_NAME = "$(TARGET_NAME)"; 582 | }; 583 | name = Debug; 584 | }; 585 | 607FACF11AFB9204008FA782 /* Release */ = { 586 | isa = XCBuildConfiguration; 587 | baseConfigurationReference = 892F430A239ED6F02F960085 /* Pods-Jelly_Example.release.xcconfig */; 588 | buildSettings = { 589 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 590 | DEVELOPMENT_TEAM = JY9STJR6M4; 591 | INFOPLIST_FILE = Jelly/Info.plist; 592 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 593 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 594 | MODULE_NAME = ExampleApp; 595 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.demo.Jelly; 596 | PRODUCT_NAME = "$(TARGET_NAME)"; 597 | }; 598 | name = Release; 599 | }; 600 | 607FACF31AFB9204008FA782 /* Debug */ = { 601 | isa = XCBuildConfiguration; 602 | baseConfigurationReference = 83C72594F63A668F7E6D6614 /* Pods-UnitTests.debug.xcconfig */; 603 | buildSettings = { 604 | CLANG_ENABLE_MODULES = YES; 605 | DEVELOPMENT_TEAM = JY9STJR6M4; 606 | FRAMEWORK_SEARCH_PATHS = ( 607 | "$(SDKROOT)/Developer/Library/Frameworks", 608 | "$(inherited)", 609 | ); 610 | GCC_PREPROCESSOR_DEFINITIONS = ( 611 | "DEBUG=1", 612 | "$(inherited)", 613 | ); 614 | INFOPLIST_FILE = UnitTests/Info.plist; 615 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 616 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 617 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 618 | PRODUCT_NAME = "$(TARGET_NAME)"; 619 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 620 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Jelly_Example.app/Jelly_Example"; 621 | }; 622 | name = Debug; 623 | }; 624 | 607FACF41AFB9204008FA782 /* Release */ = { 625 | isa = XCBuildConfiguration; 626 | baseConfigurationReference = C583DF0440648942A3BA1AF6 /* Pods-UnitTests.release.xcconfig */; 627 | buildSettings = { 628 | CLANG_ENABLE_MODULES = YES; 629 | DEVELOPMENT_TEAM = JY9STJR6M4; 630 | FRAMEWORK_SEARCH_PATHS = ( 631 | "$(SDKROOT)/Developer/Library/Frameworks", 632 | "$(inherited)", 633 | ); 634 | INFOPLIST_FILE = UnitTests/Info.plist; 635 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 636 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 637 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 638 | PRODUCT_NAME = "$(TARGET_NAME)"; 639 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Jelly_Example.app/Jelly_Example"; 640 | }; 641 | name = Release; 642 | }; 643 | /* End XCBuildConfiguration section */ 644 | 645 | /* Begin XCConfigurationList section */ 646 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "Jelly" */ = { 647 | isa = XCConfigurationList; 648 | buildConfigurations = ( 649 | 607FACED1AFB9204008FA782 /* Debug */, 650 | 607FACEE1AFB9204008FA782 /* Release */, 651 | ); 652 | defaultConfigurationIsVisible = 0; 653 | defaultConfigurationName = Release; 654 | }; 655 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "Jelly_Example" */ = { 656 | isa = XCConfigurationList; 657 | buildConfigurations = ( 658 | 607FACF01AFB9204008FA782 /* Debug */, 659 | 607FACF11AFB9204008FA782 /* Release */, 660 | ); 661 | defaultConfigurationIsVisible = 0; 662 | defaultConfigurationName = Release; 663 | }; 664 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "UnitTests" */ = { 665 | isa = XCConfigurationList; 666 | buildConfigurations = ( 667 | 607FACF31AFB9204008FA782 /* Debug */, 668 | 607FACF41AFB9204008FA782 /* Release */, 669 | ); 670 | defaultConfigurationIsVisible = 0; 671 | defaultConfigurationName = Release; 672 | }; 673 | /* End XCConfigurationList section */ 674 | }; 675 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 676 | } 677 | -------------------------------------------------------------------------------- /Example/Jelly.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Jelly.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Jelly.xcodeproj/xcshareddata/xcschemes/Jelly-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Example/Jelly.xcodeproj/xcshareddata/xcschemes/UnitTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 16 | 17 | 23 | 24 | 25 | 26 | 28 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 50 | 51 | 52 | 53 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Example/Jelly.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/Jelly.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Jelly.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Jelly/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Jelly-Animators 4 | // 5 | // Created by Sebastian Boldt on 11/16/2016. 6 | // Copyright (c) 2016 Sebastian Boldt. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { 15 | return true 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Example/Jelly/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Example/Jelly/CustomNavController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import TouchVisualizer 3 | 4 | class CustomNav: UINavigationController { 5 | override var prefersStatusBarHidden: Bool { 6 | return true 7 | } 8 | 9 | override func viewDidLoad() { 10 | super.viewDidLoad() 11 | modalPresentationCapturesStatusBarAppearance = true 12 | 13 | var config = Configuration() 14 | config.color = .darkGray 15 | config.showsTouchRadius = true 16 | config.defaultSize = CGSize(width: 100, height: 100) 17 | Visualizer.start(config) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Example/Jelly/DismissMeController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Jelly-Animators 3 | // Created by Sebastian Boldt on 17.11.16. 4 | 5 | import UIKit 6 | 7 | class DismissMeController: UIViewController { 8 | var interactionAction: (() -> ())? 9 | override func viewDidLoad() { 10 | super.viewDidLoad() 11 | modalPresentationCapturesStatusBarAppearance = true 12 | } 13 | 14 | @IBAction func actionButtonPressed(_ sender: Any) { 15 | if let interactionAction = interactionAction { 16 | interactionAction() 17 | } else { 18 | self.dismiss(animated: true, completion: nil) 19 | } 20 | } 21 | 22 | override var prefersStatusBarHidden: Bool { 23 | return true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Example/Jelly/ExampleType.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum ExampleType { 4 | case interactive(type: InteractiveType) 5 | case nonInteractive(type: NotInteractiveType) 6 | case liveUpdate(type: LiveUpdateType) 7 | } 8 | 9 | enum InteractiveType { 10 | case slideMenuFromRightEdge 11 | case coverMenuFromRightCanvas 12 | case notificationFromTopCanvas 13 | case multipleDirectionsCoverCanvas 14 | } 15 | 16 | enum NotInteractiveType { 17 | case coverFromBottom 18 | case slideFromRight 19 | } 20 | 21 | enum LiveUpdateType { 22 | case updateSize 23 | case updateAlignment 24 | case updateMarginGuards 25 | case updateCornerRadius 26 | } 27 | -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "NotificationIcon@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "NotificationIcon@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-Small.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-Small@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-Small@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "57x57", 47 | "idiom" : "iphone", 48 | "filename" : "Icon.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "57x57", 53 | "idiom" : "iphone", 54 | "filename" : "Icon@2x.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "60x60", 59 | "idiom" : "iphone", 60 | "filename" : "Icon-60@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "60x60", 65 | "idiom" : "iphone", 66 | "filename" : "Icon-60@3x.png", 67 | "scale" : "3x" 68 | }, 69 | { 70 | "size" : "20x20", 71 | "idiom" : "ipad", 72 | "filename" : "NotificationIcon~ipad.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "20x20", 77 | "idiom" : "ipad", 78 | "filename" : "NotificationIcon~ipad@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "29x29", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-Small.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "29x29", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-Small@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "40x40", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-40.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "40x40", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-40@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "50x50", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-Small-50.png", 109 | "scale" : "1x" 110 | }, 111 | { 112 | "size" : "50x50", 113 | "idiom" : "ipad", 114 | "filename" : "Icon-Small-50@2x.png", 115 | "scale" : "2x" 116 | }, 117 | { 118 | "size" : "72x72", 119 | "idiom" : "ipad", 120 | "filename" : "Icon-72.png", 121 | "scale" : "1x" 122 | }, 123 | { 124 | "size" : "72x72", 125 | "idiom" : "ipad", 126 | "filename" : "Icon-72@2x.png", 127 | "scale" : "2x" 128 | }, 129 | { 130 | "size" : "76x76", 131 | "idiom" : "ipad", 132 | "filename" : "Icon-76.png", 133 | "scale" : "1x" 134 | }, 135 | { 136 | "size" : "76x76", 137 | "idiom" : "ipad", 138 | "filename" : "Icon-76@2x.png", 139 | "scale" : "2x" 140 | }, 141 | { 142 | "size" : "83.5x83.5", 143 | "idiom" : "ipad", 144 | "filename" : "Icon-83.5@2x.png", 145 | "scale" : "2x" 146 | }, 147 | { 148 | "size" : "1024x1024", 149 | "idiom" : "ios-marketing", 150 | "filename" : "ios-marketing.png", 151 | "scale" : "1x" 152 | } 153 | ], 154 | "info" : { 155 | "version" : 1, 156 | "author" : "xcode" 157 | } 158 | } -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-72.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/Icon@2x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/NotificationIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/NotificationIcon@2x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/NotificationIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/NotificationIcon@3x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/AppIcon.appiconset/ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/AppIcon.appiconset/ios-marketing.png -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/Jelly-Logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Jellyfish.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Jelly/Images.xcassets/Jelly-Logo.imageset/Jellyfish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Example/Jelly/Images.xcassets/Jelly-Logo.imageset/Jellyfish.png -------------------------------------------------------------------------------- /Example/Jelly/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Jelly 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UIRequiresFullScreen 36 | 37 | UIStatusBarHidden 38 | 39 | UISupportedInterfaceOrientations 40 | 41 | UIInterfaceOrientationPortrait 42 | 43 | UIViewControllerBasedStatusBarAppearance 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/Jelly/MainViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Jelly 3 | 4 | class MainViewController: UIViewController { 5 | var animator: Animator? 6 | var type: ExampleType? 7 | var showButtonLogic: (() -> Void)? 8 | 9 | @IBAction func didPressShowButton(_ sender: Any) { 10 | showButtonLogic?() 11 | } 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | modalPresentationCapturesStatusBarAppearance = true 16 | setup() 17 | } 18 | 19 | override var prefersStatusBarHidden: Bool { 20 | return true 21 | } 22 | 23 | func setup() { 24 | guard let type = type else { 25 | return 26 | } 27 | 28 | switch type { 29 | case .nonInteractive(let nonInteractiveType): 30 | switch nonInteractiveType { 31 | case .coverFromBottom: 32 | showButtonLogic = { 33 | let storyboard = UIStoryboard.init(name: "Main", bundle: nil) 34 | let viewControllerToPresent = storyboard.instantiateViewController(withIdentifier: "PresentMe") 35 | let marginGuards = UIEdgeInsets(top: 0, left: 16, bottom: 32, right: 16) 36 | let uiConfiguration = PresentationUIConfiguration(cornerRadius: 10, backgroundStyle: .dimmed(alpha: 0.5), isTapBackgroundToDismissEnabled: true, corners: [.layerMaxXMaxYCorner,.layerMaxXMinYCorner,.layerMinXMaxYCorner,.layerMinXMinYCorner]) 37 | let size = PresentationSize(width: .fullscreen, height: .halfscreen) 38 | let alignment = PresentationAlignment(vertical: .bottom, horizontal: .center) 39 | let presentation = CoverPresentation(directionShow: .bottom, directionDismiss: .bottom, uiConfiguration: uiConfiguration, size: size, alignment: alignment, marginGuards: marginGuards) 40 | let animator = Animator(presentation: presentation) 41 | animator.prepare(presentedViewController: viewControllerToPresent) 42 | self.animator = animator 43 | self.present(viewControllerToPresent, animated: true, completion: nil) 44 | } 45 | case .slideFromRight: 46 | showButtonLogic = { 47 | let storyboard = UIStoryboard.init(name: "Main", bundle: nil) 48 | let viewControllerToPresent = storyboard.instantiateViewController(withIdentifier: "PresentMe") 49 | let presentation = SlidePresentation(direction: .bottom, size: .halfscreen, parallax: 0.5) 50 | let animator = Animator(presentation: presentation) 51 | animator.prepare(presentedViewController: viewControllerToPresent) 52 | self.animator = animator 53 | self.present(viewControllerToPresent, animated: true, completion: nil) 54 | } 55 | } 56 | case .interactive(let interactiveType): 57 | switch interactiveType { 58 | case .coverMenuFromRightCanvas:() 59 | let storyboard = UIStoryboard.init(name: "Main", bundle: nil) 60 | let viewControllerToPresent = storyboard.instantiateViewController(withIdentifier: "PresentMe") 61 | let interactionConfiguration = InteractionConfiguration(presentingViewController: self, completionThreshold: 0.5, dragMode: .canvas) 62 | let uiConfiguration = PresentationUIConfiguration(backgroundStyle: .blurred(effectStyle: .light)) 63 | let size = PresentationSize(width: .halfscreen, height: .fullscreen) 64 | let alignment = PresentationAlignment(vertical: .center, horizontal: .left) 65 | let presentation = CoverPresentation(directionShow: .left, directionDismiss: .left, uiConfiguration: uiConfiguration, size: size, alignment: alignment, interactionConfiguration: interactionConfiguration) 66 | let animator = Animator(presentation: presentation) 67 | animator.prepare(presentedViewController: viewControllerToPresent) 68 | self.animator = animator 69 | 70 | showButtonLogic = { 71 | self.present(viewControllerToPresent, animated: true, completion: nil) 72 | } 73 | case .slideMenuFromRightEdge: 74 | let storyboard = UIStoryboard.init(name: "Main", bundle: nil) 75 | let viewControllerToPresent = storyboard.instantiateViewController(withIdentifier: "PresentMe") 76 | let interactionConfiguration = InteractionConfiguration(presentingViewController: self, completionThreshold: 0.5, dragMode: .edge) 77 | let uiConfiguration = PresentationUIConfiguration(backgroundStyle: .dimmed(alpha: 0.5)) 78 | let presentation = SlidePresentation(uiConfiguration: uiConfiguration, direction: .right, size: .fullscreen, interactionConfiguration: interactionConfiguration) 79 | let animator = Animator(presentation: presentation) 80 | animator.prepare(presentedViewController: viewControllerToPresent) 81 | self.animator = animator 82 | 83 | showButtonLogic = { 84 | self.present(viewControllerToPresent, animated: true, completion: nil) 85 | } 86 | case .multipleDirectionsCoverCanvas: 87 | let storyboard = UIStoryboard.init(name: "Main", bundle: nil) 88 | let viewControllerToPresent = storyboard.instantiateViewController(withIdentifier: "PresentMe") 89 | let interactionConfiguration = InteractionConfiguration(presentingViewController: self, completionThreshold: 0.5, dragMode: .canvas) 90 | let uiConfiguration = PresentationUIConfiguration(cornerRadius: 10, backgroundStyle: .blurred(effectStyle: .light)) 91 | let size = PresentationSize(width: .fullscreen, height: .halfscreen) 92 | let marginGuards = UIEdgeInsets(top: 50, left: 16, bottom: 50, right: 16) 93 | let alignment = PresentationAlignment(vertical: .center, horizontal: .center) 94 | let presentation = CoverPresentation(directionShow: .left, directionDismiss: .right, uiConfiguration: uiConfiguration, size: size, alignment: alignment, marginGuards: marginGuards, interactionConfiguration: interactionConfiguration) 95 | let animator = Animator(presentation: presentation) 96 | animator.prepare(presentedViewController: viewControllerToPresent) 97 | self.animator = animator 98 | 99 | showButtonLogic = { 100 | self.present(viewControllerToPresent, animated: true, completion: nil) 101 | } 102 | case .notificationFromTopCanvas: 103 | let storyboard = UIStoryboard.init(name: "Main", bundle: nil) 104 | let viewControllerToPresent = storyboard.instantiateViewController(withIdentifier: "PresentMe") 105 | let interactionConfiguration = InteractionConfiguration(presentingViewController: self, completionThreshold: 0.5, dragMode: .canvas) 106 | let uiConfiguration = PresentationUIConfiguration(cornerRadius: 10, backgroundStyle: .dimmed(alpha: 0.5)) 107 | let size = PresentationSize(width: .fullscreen, height: .custom(value: 200)) 108 | let marginGuards = UIEdgeInsets(top: 50, left: 16, bottom: 8, right: 16) 109 | let alignment = PresentationAlignment(vertical: .top, horizontal: .center) 110 | let presentation = CoverPresentation(directionShow: .top, directionDismiss: .top, uiConfiguration: uiConfiguration, size: size, alignment: alignment, marginGuards: marginGuards, interactionConfiguration: interactionConfiguration) 111 | let animator = Animator(presentation: presentation) 112 | animator.prepare(presentedViewController: viewControllerToPresent) 113 | self.animator = animator 114 | 115 | showButtonLogic = { 116 | self.present(viewControllerToPresent, animated: true, completion: nil) 117 | } 118 | 119 | } 120 | case .liveUpdate(let liveUpdateType): 121 | switch liveUpdateType { 122 | case .updateAlignment:() 123 | let storyboard = UIStoryboard.init(name: "Main", bundle: nil) 124 | let viewControllerToPresent = storyboard.instantiateViewController(withIdentifier: "PresentMe") as! DismissMeController 125 | let alignment = PresentationAlignment(vertical: .top, horizontal: .center) 126 | let uiConfig = PresentationUIConfiguration(cornerRadius: 10) 127 | let marginGuards = UIEdgeInsets(top: 50, left: 16, bottom: 50, right: 16) 128 | let presentation = CoverPresentation(directionShow: .top, directionDismiss: .top, uiConfiguration: uiConfig, size: PresentationSize(width: .fullscreen, height: .halfscreen), alignment: alignment, marginGuards: marginGuards) 129 | let animator = Animator(presentation: presentation) 130 | animator.prepare(presentedViewController: viewControllerToPresent) 131 | self.animator = animator 132 | 133 | showButtonLogic = { [weak self] in 134 | self?.present(viewControllerToPresent, animated: true, completion: nil) 135 | } 136 | 137 | viewControllerToPresent.interactionAction = { [weak self] in 138 | try! self?.animator?.updateVerticalAlignment(alignment: .bottom, duration: .medium) 139 | } 140 | case .updateSize:() 141 | let storyboard = UIStoryboard.init(name: "Main", bundle: nil) 142 | let viewControllerToPresent = storyboard.instantiateViewController(withIdentifier: "PresentMe") as! DismissMeController 143 | let alignment = PresentationAlignment(vertical: .top, horizontal: .center) 144 | let uiConfig = PresentationUIConfiguration(cornerRadius: 10) 145 | let marginGuards = UIEdgeInsets(top: 50, left: 16, bottom: 50, right: 16) 146 | let presentation = CoverPresentation(directionShow: .top, directionDismiss: .top, uiConfiguration: uiConfig, size: PresentationSize(width: .fullscreen, height: .halfscreen), alignment: alignment, marginGuards: marginGuards) 147 | let animator = Animator(presentation: presentation) 148 | animator.prepare(presentedViewController: viewControllerToPresent) 149 | self.animator = animator 150 | 151 | showButtonLogic = { [weak self] in 152 | self?.present(viewControllerToPresent, animated: true, completion: nil) 153 | } 154 | 155 | viewControllerToPresent.interactionAction = { [weak self] in 156 | let newSize = PresentationSize(width: .fullscreen, height: .fullscreen) 157 | try! self?.animator?.updateSize(presentationSize: newSize, duration: .medium) 158 | } 159 | case .updateCornerRadius:() 160 | let storyboard = UIStoryboard.init(name: "Main", bundle: nil) 161 | let viewControllerToPresent = storyboard.instantiateViewController(withIdentifier: "PresentMe") as! DismissMeController 162 | let alignment = PresentationAlignment(vertical: .top, horizontal: .center) 163 | let uiConfig = PresentationUIConfiguration(cornerRadius: 10) 164 | let marginGuards = UIEdgeInsets(top: 50, left: 16, bottom: 50, right: 16) 165 | let presentation = CoverPresentation(directionShow: .top, directionDismiss: .top, uiConfiguration: uiConfig, size: PresentationSize(width: .fullscreen, height: .halfscreen), alignment: alignment, marginGuards: marginGuards) 166 | let animator = Animator(presentation: presentation) 167 | animator.prepare(presentedViewController: viewControllerToPresent) 168 | self.animator = animator 169 | 170 | showButtonLogic = { [weak self] in 171 | self?.present(viewControllerToPresent, animated: true, completion: nil) 172 | } 173 | 174 | viewControllerToPresent.interactionAction = { [weak self] in 175 | self?.animator?.updateCorners(radius: 30, corners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], duration: .medium) 176 | } 177 | case .updateMarginGuards: 178 | let storyboard = UIStoryboard.init(name: "Main", bundle: nil) 179 | let viewControllerToPresent = storyboard.instantiateViewController(withIdentifier: "PresentMe") as! DismissMeController 180 | let alignment = PresentationAlignment(vertical: .top, horizontal: .center) 181 | let uiConfig = PresentationUIConfiguration(cornerRadius: 10) 182 | let marginGuards = UIEdgeInsets(top: 100, left: 32, bottom: 50, right: 32) 183 | let presentation = CoverPresentation(directionShow: .top, directionDismiss: .top, uiConfiguration: uiConfig, size: PresentationSize(width: .fullscreen, height: .halfscreen), alignment: alignment, marginGuards: marginGuards) 184 | let animator = Animator(presentation: presentation) 185 | animator.prepare(presentedViewController: viewControllerToPresent) 186 | self.animator = animator 187 | 188 | showButtonLogic = { [weak self] in 189 | self?.present(viewControllerToPresent, animated: true, completion: nil) 190 | } 191 | 192 | viewControllerToPresent.interactionAction = { [weak self] in 193 | let newGuards = UIEdgeInsets(top: 70, left: 8, bottom: 0, right: 8) 194 | try! self?.animator?.updateMarginGuards(marginGuards: newGuards, duration: .medium) 195 | } 196 | } 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /Example/Jelly/MenuViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Jelly-Animators 4 | // 5 | // Created by Sebastian Boldt on 11/16/2016. 6 | // Copyright (c) 2016 Sebastian Boldt. All rights reserved. 7 | // 8 | import UIKit 9 | import Jelly 10 | 11 | class MenuViewController: UITableViewController { 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | modalPresentationCapturesStatusBarAppearance = true 15 | } 16 | 17 | override var prefersStatusBarHidden: Bool { 18 | return true 19 | } 20 | 21 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 22 | guard let mainVC = segue.destination as? MainViewController else { 23 | return 24 | } 25 | 26 | guard let identifier = segue.identifier else { 27 | return 28 | } 29 | 30 | switch identifier { 31 | // Not Interactive 32 | case "NotInteractive-coverFromLeft": 33 | mainVC.type = ExampleType.nonInteractive(type: .coverFromBottom) 34 | case "NotInteractive-slideFromLeft": 35 | mainVC.type = ExampleType.nonInteractive(type: .slideFromRight) 36 | 37 | // Interactive 38 | case "Interactive-CoverMenuFromRightCanvas": 39 | mainVC.type = ExampleType.interactive(type: .coverMenuFromRightCanvas) 40 | case "Interactive-SlideMenuFromRightEdge": 41 | mainVC.type = ExampleType.interactive(type: .slideMenuFromRightEdge) 42 | case "Interactive-NotificationFromTopCanvas": 43 | mainVC.type = ExampleType.interactive(type: .notificationFromTopCanvas) 44 | case "Interactive-MultipleDirectionCoverCanvas": 45 | mainVC.type = ExampleType.interactive(type: .multipleDirectionsCoverCanvas) 46 | 47 | // Live Update 48 | case "LiveUpdate-Size": 49 | mainVC.type = ExampleType.liveUpdate(type: .updateSize) 50 | case "LiveUpdate-Alignments": 51 | mainVC.type = ExampleType.liveUpdate(type: .updateAlignment) 52 | case "LiveUpdate-MarginGuards": 53 | mainVC.type = ExampleType.liveUpdate(type: .updateMarginGuards) 54 | case "LiveUpdate-CornerRadius": 55 | mainVC.type = ExampleType.liveUpdate(type: .updateCornerRadius) 56 | default:() 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Example/Jelly/RoundedView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class RoundedView: UIView { 4 | override func layoutSubviews() { 5 | super.layoutSubviews() 6 | layer.cornerRadius = frame.size.width / 2 7 | layer.masksToBounds = true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | platform :ios, '10.0' 3 | 4 | source 'https://github.com/CocoaPods/Specs.git' 5 | 6 | target 'Jelly_Example' do 7 | pod 'Jelly' , :path => '../' 8 | pod "TouchVisualizer", '~> 4.0.0' 9 | 10 | target 'UnitTests' do 11 | inherit! :search_paths 12 | 13 | pod 'Quick' 14 | pod 'Nimble' 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Jelly (2.2.2) 3 | - Nimble (10.0.0) 4 | - Quick (5.0.1) 5 | - TouchVisualizer (4.0.0) 6 | 7 | DEPENDENCIES: 8 | - Jelly (from `../`) 9 | - Nimble 10 | - Quick 11 | - TouchVisualizer (~> 4.0.0) 12 | 13 | SPEC REPOS: 14 | https://github.com/CocoaPods/Specs.git: 15 | - Nimble 16 | - Quick 17 | - TouchVisualizer 18 | 19 | EXTERNAL SOURCES: 20 | Jelly: 21 | :path: "../" 22 | 23 | SPEC CHECKSUMS: 24 | Jelly: fdc46ef7b60776dcb0dd178d2608e3b8aefd1aec 25 | Nimble: 5316ef81a170ce87baf72dd961f22f89a602ff84 26 | Quick: 749aa754fd1e7d984f2000fe051e18a3a9809179 27 | TouchVisualizer: d342dc3a567b6dd289bc31b7ce8681302a93dfee 28 | 29 | PODFILE CHECKSUM: e5a27f5aad72c66413f81d56477d8f5f43e5d0e2 30 | 31 | COCOAPODS: 1.9.3 32 | -------------------------------------------------------------------------------- /Example/UnitTests/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/UnitTests/JellyAnimatorSpec.swift: -------------------------------------------------------------------------------- 1 | @testable import Jelly 2 | import Quick 3 | import Nimble 4 | 5 | class JellyAnimatorSpec: QuickSpec { 6 | let presentedViewController = UIViewController() 7 | let presentingViewController = UIViewController() 8 | 9 | override func spec() { 10 | describe("After Configuring a JellyAnimator") { 11 | context("with cover presentation") { 12 | it("UIViewControllerTransitioningDelegate should return right animationController for presenting") { 13 | let coverPresentation = Jelly.CoverPresentation(directionShow: .left, directionDismiss: .left, interactionConfiguration: InteractionConfiguration(presentingViewController: self.presentingViewController, completionThreshold: 0.5, dragMode: .edge)) 14 | let animator = Jelly.Animator(presentation: coverPresentation) 15 | let animationController = animator.animationController(forPresented: self.presentedViewController, presenting: self.presentingViewController, source: self.presentedViewController) 16 | expect(animationController is Jelly.CoverAnimator).to(equal(true)) 17 | } 18 | } 19 | 20 | context("with fade presentation") { 21 | it("UIViewControllerTransitioningDelegate should return right animationController for presenting") { 22 | let fadePresentation = Jelly.FadePresentation() 23 | let animator = Jelly.Animator(presentation: fadePresentation) 24 | let animationController = animator.animationController(forPresented: self.presentedViewController, presenting: self.presentingViewController, source: self.presentedViewController) 25 | expect(animationController is Jelly.FadeAnimator).to(equal(true)) 26 | } 27 | } 28 | 29 | context("with slide presentation") { 30 | it("UIViewControllerTransitioningDelegate should return right animationController for presenting") { 31 | let slidePresentation = Jelly.SlidePresentation(interactionConfiguration: InteractionConfiguration(presentingViewController: self.presentingViewController, completionThreshold: 0.5, dragMode: .edge)) 32 | let animator = Jelly.Animator(presentation: slidePresentation) 33 | let animationController = animator.animationController(forPresented: self.presentedViewController, presenting: self.presentingViewController, source: self.presentedViewController) 34 | expect(animationController is Jelly.SlideAnimator).to(equal(true)) 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Github/IMG_0240.TRIM.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/IMG_0240.TRIM.gif -------------------------------------------------------------------------------- /Github/IMG_0242.TRIM.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/IMG_0242.TRIM.gif -------------------------------------------------------------------------------- /Github/IMG_0244.TRIM.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/IMG_0244.TRIM.gif -------------------------------------------------------------------------------- /Github/IMG_0246.TRIM.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/IMG_0246.TRIM.gif -------------------------------------------------------------------------------- /Github/IMG_0248.TRIM.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/IMG_0248.TRIM.gif -------------------------------------------------------------------------------- /Github/IMG_0250.TRIM.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/IMG_0250.TRIM.gif -------------------------------------------------------------------------------- /Github/IMG_0252.TRIM.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/IMG_0252.TRIM.gif -------------------------------------------------------------------------------- /Github/IMG_0254.TRIM.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/IMG_0254.TRIM.gif -------------------------------------------------------------------------------- /Github/Jellyfish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/Jellyfish.png -------------------------------------------------------------------------------- /Github/author.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/author.png -------------------------------------------------------------------------------- /Github/customization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/customization.png -------------------------------------------------------------------------------- /Github/emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/emoji.png -------------------------------------------------------------------------------- /Github/how to.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/how to.png -------------------------------------------------------------------------------- /Github/installation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/installation.png -------------------------------------------------------------------------------- /Github/interactive-transitions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/interactive-transitions.png -------------------------------------------------------------------------------- /Github/license.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/license.png -------------------------------------------------------------------------------- /Github/mentions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/mentions.png -------------------------------------------------------------------------------- /Github/requirements.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/requirements.png -------------------------------------------------------------------------------- /Github/update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Github/update.png -------------------------------------------------------------------------------- /Jelly.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint Jelly.podspec' to ensure this is a valid spec before submitting. 3 | # Any lines starting with a # are optional, but their use is encouraged 4 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 5 | # 6 | 7 | Pod::Spec.new do |s| 8 | s.name = 'Jelly' 9 | s.version = '2.3.0' 10 | s.summary = 'Jelly is a library for animated, non-interactive & interactive viewcontroller transitions and presentations with the focus on a simple and yet flexible API.' 11 | 12 | s.description = <<-DESC 13 | Jelly is a library for animated, non-interactive & interactive viewcontroller 14 | transitions and presentations with the focus on a simple and yet flexible API. 15 | DESC 16 | 17 | s.homepage = 'https://www.sebastianboldt.com' 18 | s.license = { :type => 'MIT', :file => 'LICENSE' } 19 | s.author = { 'Sebastian Boldt' => 'self.dealloc@googlemail.com' } 20 | s.social_media_url = 'http://twitter.com/sebastianboldt' 21 | s.source = { :git => 'https://github.com/SebastianBoldt/Jelly.git', :tag => s.version.to_s } 22 | s.social_media_url = 'https://twitter.com/sebastianboldt' 23 | 24 | s.ios.deployment_target = '10.0' 25 | s.source_files = 'Jelly/Classes/**/*' 26 | s.frameworks = 'UIKit' 27 | s.swift_version = '4.2' 28 | end 29 | 30 | -------------------------------------------------------------------------------- /Jelly/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Jelly/Assets/.gitkeep -------------------------------------------------------------------------------- /Jelly/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianBoldt/Jelly/41d295af01c80b5c8386f852c1fd938b8034fa25/Jelly/Classes/.gitkeep -------------------------------------------------------------------------------- /Jelly/Classes/private/Animators/Animated Animators/CoverAnimator.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class CoverAnimator: NSObject { 4 | private let presentationType : PresentationType 5 | private let presentation : CoverPresentation 6 | private var currentPropertyAnimator: UIViewPropertyAnimator? 7 | 8 | init(presentationType: PresentationType, presentation: CoverPresentation) { 9 | self.presentationType = presentationType 10 | self.presentation = presentation 11 | super.init() 12 | } 13 | } 14 | 15 | extension CoverAnimator : UIViewControllerAnimatedTransitioning { 16 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 17 | let propertyAnimator = createPropertyAnimator(using: transitionContext) 18 | currentPropertyAnimator = propertyAnimator 19 | propertyAnimator.startAnimation() 20 | } 21 | 22 | func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating { 23 | // According to Apple's document: 24 | // "You must return the same animator object for the duration of the transition." 25 | // https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning/1829434-interruptibleanimator 26 | return currentPropertyAnimator ?? createPropertyAnimator(using: transitionContext) 27 | } 28 | 29 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 30 | return presentation.presentationTiming.duration.timeInterval 31 | } 32 | 33 | func createPropertyAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewPropertyAnimator { 34 | let key = getPresentedViewControllerKeyForPresentationType(type: self.presentationType) 35 | let isPresentation = key == .to 36 | let controllerToAnimate = transitionContext.viewController(forKey: key)! 37 | 38 | // If we animate to the ViewController we need to add the View to the ContainerView 39 | if isPresentation { 40 | transitionContext.containerView.addSubview(controllerToAnimate.view) 41 | } 42 | 43 | let presentedFrame = transitionContext.finalFrame(for: controllerToAnimate) // Frame the ViewController will have after animation completes 44 | let direction = self.presentationType == .show ? presentation.showDirection : presentation.dismissDirection 45 | let dismissedFrame = calculateDismissedFrame(from: presentedFrame, usingDirection: direction, andContext: transitionContext) // Frame the ViewController will have when he is dismissed 46 | 47 | let initialFrame = isPresentation ? dismissedFrame : presentedFrame 48 | let finalFrame = isPresentation ? presentedFrame : dismissedFrame 49 | 50 | let animationDuration = transitionDuration(using: transitionContext) 51 | controllerToAnimate.view.frame = initialFrame 52 | 53 | let timing = presentation.presentationTiming 54 | let animationCurve = isPresentation ? timing.presentationCurve : timing.dismissCurve 55 | 56 | let velocityVector = direction == .bottom || direction == .top ? CGVector(dx: 0, dy: presentation.spring.velocity) : CGVector(dx: presentation.spring.velocity, dy: 0) 57 | let springParameters = UISpringTimingParameters(dampingRatio: presentation.spring.damping, initialVelocity: velocityVector) 58 | let cubicParameters = UICubicTimingParameters(animationCurve: animationCurve) 59 | let timingCurveProvider = CombinedTimingCurveProvider(springParameters: springParameters, cubicParameters: cubicParameters) 60 | 61 | let propertyAnimator = UIViewPropertyAnimator(duration: animationDuration, timingParameters: timingCurveProvider) 62 | 63 | propertyAnimator.addAnimations { 64 | controllerToAnimate.view.frame = finalFrame 65 | } 66 | 67 | propertyAnimator.addCompletion { animatedPosition in 68 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 69 | } 70 | 71 | return propertyAnimator 72 | } 73 | } 74 | 75 | extension CoverAnimator { 76 | /// Return dismissed frame depending on provides direction 77 | /// 78 | /// - Parameters: 79 | /// - presentedFrame: frame the viewController will have if he is fully presented 80 | /// - transitionContext: nothing to say here 81 | /// - Returns: the frame the view should have afer dismissing it 82 | private func calculateDismissedFrame(from presentedFrame: CGRect, 83 | usingDirection direction: Direction , 84 | andContext transitionContext: UIViewControllerContextTransitioning) -> CGRect { 85 | var dismissedFrame: CGRect = presentedFrame 86 | switch direction { 87 | case .left: 88 | dismissedFrame.origin.x = -presentedFrame.width 89 | case .right: 90 | dismissedFrame.origin.x = transitionContext.containerView.frame.size.width 91 | case .top: 92 | dismissedFrame.origin.y = -presentedFrame.height 93 | case .bottom: 94 | dismissedFrame.origin.y = transitionContext.containerView.frame.size.height 95 | } 96 | return dismissedFrame 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Jelly/Classes/private/Animators/Animated Animators/FadeAnimator.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class FadeAnimator: NSObject { 4 | private let presentationType : PresentationType 5 | private let presentation : FadePresentation 6 | private var currentPropertyAnimator: UIViewPropertyAnimator? 7 | 8 | init(presentationType: PresentationType, presentation: FadePresentation) { 9 | self.presentationType = presentationType 10 | self.presentation = presentation 11 | super.init() 12 | } 13 | } 14 | 15 | extension FadeAnimator : UIViewControllerAnimatedTransitioning { 16 | func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating { 17 | return currentPropertyAnimator ?? createPropertyAnimator(using: transitionContext) 18 | } 19 | 20 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 21 | return presentation.presentationTiming.duration.timeInterval 22 | } 23 | 24 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 25 | let propertyAnimator = createPropertyAnimator(using: transitionContext) 26 | currentPropertyAnimator = propertyAnimator 27 | propertyAnimator.startAnimation() 28 | } 29 | 30 | private func createPropertyAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewPropertyAnimator { 31 | let key = getPresentedViewControllerKeyForPresentationType(type: self.presentationType) 32 | let isPresentation = key == .to 33 | let controllerToAnimate = transitionContext.viewController(forKey: key)! 34 | 35 | // If we animate to the ViewController we need to add the View to the ContainerView 36 | if isPresentation { 37 | transitionContext.containerView.addSubview(controllerToAnimate.view) 38 | } 39 | 40 | let presentedFrame = transitionContext.finalFrame(for: controllerToAnimate) // Frame the ViewController 41 | 42 | let finalAlpha = isPresentation ? 1.0 : 0.0 43 | let initialAlpha = isPresentation ? 0.0 : 1.0 44 | 45 | let animationDuration = transitionDuration(using: transitionContext) 46 | controllerToAnimate.view.frame = presentedFrame 47 | controllerToAnimate.view.alpha = CGFloat(initialAlpha) 48 | 49 | let timing = presentation.presentationTiming 50 | let animationCurve = isPresentation ? timing.presentationCurve : timing.dismissCurve 51 | let timingCurveProvider = UICubicTimingParameters(animationCurve: animationCurve) 52 | let propertyAnimator = UIViewPropertyAnimator(duration: animationDuration, timingParameters: timingCurveProvider) 53 | 54 | propertyAnimator.addAnimations { 55 | controllerToAnimate.view.alpha = CGFloat(finalAlpha) 56 | } 57 | 58 | propertyAnimator.addCompletion { animatedPosition in 59 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 60 | } 61 | return propertyAnimator 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Jelly/Classes/private/Animators/Animated Animators/SlideAnimator.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class SlideAnimator: NSObject { 4 | private let presentationType : PresentationType 5 | private let presentation : SlidePresentation 6 | private var currentPropertyAnimator: UIViewPropertyAnimator? 7 | 8 | init(presentationType: PresentationType, presentation: SlidePresentation) { 9 | self.presentationType = presentationType 10 | self.presentation = presentation 11 | super.init() 12 | } 13 | } 14 | 15 | extension SlideAnimator : UIViewControllerAnimatedTransitioning { 16 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 17 | return presentation.presentationTiming.duration.timeInterval 18 | } 19 | 20 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 21 | let propertyAnimator = createPropertyAnimator(using: transitionContext) 22 | currentPropertyAnimator = propertyAnimator 23 | propertyAnimator.startAnimation() 24 | } 25 | 26 | func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating { 27 | return currentPropertyAnimator ?? createPropertyAnimator(using: transitionContext) 28 | } 29 | 30 | func createPropertyAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewPropertyAnimator{ 31 | let presentedKey = getPresentedViewControllerKeyForPresentationType(type: self.presentationType) 32 | let underlyingKey = getUnderlyingViewControllerKeyForPresentationType(type: self.presentationType) 33 | 34 | let isPresentation = (presentedKey == .to) // Are we presenting or dismissing 35 | let presentedViewController = transitionContext.viewController(forKey: presentedKey)! // ViewController that will be presented and removed on dismissal 36 | let underlyingViewController = transitionContext.viewController(forKey: underlyingKey)! // Viewcontroller we present from and we need to animate in on dismissal 37 | 38 | // If we animate to the ViewController we need to add the View to the ContainerView 39 | if isPresentation { 40 | transitionContext.containerView.addSubview(presentedViewController.view) 41 | } 42 | 43 | // Frames for pushed ViewController 44 | let presentedFrameForPresented = transitionContext.finalFrame(for: presentedViewController) // Frame the ViewController will have after animation completes 45 | let dismissedFrameForPresented = calculateDismissedFrame(from: presentedFrameForPresented, usingDirection: presentation.showDirection, andContext: transitionContext) // Frame the ViewController will have when he is dismissed 46 | 47 | var presentedFrameForUnderlying = transitionContext.containerView.frame 48 | 49 | switch self.presentation.showDirection { 50 | case .left: 51 | presentedFrameForUnderlying.origin.x = presentedFrameForPresented.origin.x + (presentedFrameForPresented.size.width * presentation.parallax) 52 | case .right: 53 | presentedFrameForUnderlying.origin.x = presentedFrameForUnderlying.origin.x - (presentedFrameForPresented.size.width * presentation.parallax) 54 | case .top: 55 | presentedFrameForUnderlying.origin.y = presentedFrameForPresented.origin.y + (presentedFrameForPresented.size.height * presentation.parallax) 56 | case .bottom: 57 | presentedFrameForUnderlying.origin.y = presentedFrameForUnderlying.origin.y - (presentedFrameForPresented.size.height * presentation.parallax) 58 | } 59 | 60 | let dismissedFrameForUnderLying = transitionContext.containerView.frame 61 | 62 | // Depending on which kind of presentation we are currently doing we swift frames 63 | let initialFrameForPresented = isPresentation ? dismissedFrameForPresented : presentedFrameForPresented 64 | let finalFrameFoPresented = isPresentation ? presentedFrameForPresented : dismissedFrameForPresented 65 | 66 | let initialFrameForUnderlying = isPresentation ? dismissedFrameForUnderLying : presentedFrameForUnderlying 67 | let finalFrameForUnderlying = isPresentation ? presentedFrameForUnderlying : dismissedFrameForUnderLying 68 | 69 | let animationDuration = transitionDuration(using: transitionContext) 70 | presentedViewController.view.frame = initialFrameForPresented 71 | underlyingViewController.view.frame = initialFrameForUnderlying 72 | 73 | let animationCurve = isPresentation ? presentation.presentationTiming.presentationCurve : presentation.presentationTiming.presentationCurve 74 | let direction = presentation.showDirection 75 | let velocityVector = direction == .bottom || direction == .top ? CGVector(dx: 0, dy: presentation.spring.velocity) : CGVector(dx: presentation.spring.velocity, dy: 0) 76 | let springParameters = UISpringTimingParameters(dampingRatio: presentation.spring.damping, initialVelocity: velocityVector) 77 | let cubicParameters = UICubicTimingParameters(animationCurve: animationCurve) 78 | let timingCurveProvider = CombinedTimingCurveProvider(springParameters: springParameters, cubicParameters: cubicParameters) 79 | 80 | let propertyAnimator = UIViewPropertyAnimator(duration: animationDuration, timingParameters: timingCurveProvider) 81 | 82 | propertyAnimator.addAnimations { 83 | presentedViewController.view.frame = finalFrameFoPresented 84 | underlyingViewController.view.frame = finalFrameForUnderlying 85 | } 86 | 87 | propertyAnimator.addCompletion { animatedPosition in 88 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 89 | } 90 | 91 | return propertyAnimator 92 | } 93 | } 94 | 95 | extension SlideAnimator { 96 | /// Return dismissed frame depending on provides direction 97 | /// 98 | /// - Parameters: 99 | /// - presentedFrame: frame the viewController will have if he is fully presented 100 | /// - transitionContext: nothing to say here 101 | /// - Returns: the frame the view should have afer dismissing it 102 | private func calculateDismissedFrame(from presentedFrame: CGRect, 103 | usingDirection direction: Direction, 104 | andContext transitionContext: UIViewControllerContextTransitioning) -> CGRect { 105 | var dismissedFrame: CGRect = presentedFrame 106 | switch direction { 107 | case .left: 108 | dismissedFrame.origin.x = -presentedFrame.width 109 | case .right: 110 | dismissedFrame.origin.x = transitionContext.containerView.frame.size.width 111 | case .top: 112 | dismissedFrame.origin.y = -presentedFrame.height 113 | case .bottom: 114 | dismissedFrame.origin.y = transitionContext.containerView.frame.size.height 115 | } 116 | return dismissedFrame 117 | } 118 | } 119 | 120 | -------------------------------------------------------------------------------- /Jelly/Classes/private/Animators/Interaction Controllers/InteractionController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class InteractionController: UIPercentDrivenInteractiveTransition { 4 | var interactionInProgress = false 5 | private var shouldCompleteTransition = false 6 | 7 | private weak var presentedViewController: UIViewController! 8 | private weak var presentingViewController: UIViewController? 9 | private weak var presentationController: PresentationController? 10 | 11 | private let presentation: (InteractionConfigurationProvider & PresentationShowDirectionProvider) 12 | private let presentationType: PresentationType 13 | private let configuration: InteractionConfiguration 14 | 15 | init(configuration: InteractionConfiguration, 16 | presentedViewController: UIViewController, 17 | presentingViewController: UIViewController?, 18 | presentationType: PresentationType, 19 | presentation: (InteractionConfigurationProvider & PresentationShowDirectionProvider), 20 | presentationController: PresentationController?) { 21 | 22 | self.presentedViewController = presentedViewController 23 | self.presentingViewController = presentingViewController 24 | self.presentation = presentation 25 | self.presentationType = presentationType 26 | self.presentationController = presentationController 27 | self.configuration = configuration 28 | 29 | super.init() 30 | prepareGestureRecognizers() 31 | } 32 | 33 | public func setPresentationController(presentationController: PresentationController) { 34 | self.presentationController = presentationController 35 | } 36 | 37 | private func prepareGestureRecognizers() { 38 | switch (presentationType, configuration.dragMode) { 39 | case (.show, .canvas), (.show, .edge): 40 | if configuration.mode.contains(.present){ 41 | guard let view = presentingViewController?.view else { break } 42 | addInteractionGestureRecognizer(to: view) 43 | } 44 | case (.dismiss, .canvas): 45 | if configuration.mode.contains(.dismiss){ 46 | addInteractionGestureRecognizer(to: presentedViewController.view) 47 | } 48 | case (.dismiss, .edge): 49 | if configuration.mode.contains(.dismiss){ 50 | prepareSpecialWorkAroundForDismissGestureRecognizers() 51 | } 52 | } 53 | } 54 | 55 | /* 56 | if orientation is vertical 57 | if height is >= than screen 58 | we should add it to the presntedViewController 59 | else if 60 | we add it to the dimming and blurView 61 | else if orientation is horizontal 62 | if width is >= than screen 63 | we should add it to the presntedViewController 64 | else if 65 | we add it to the dimming and blurView 66 | */ 67 | //TODO: try to refactor this 68 | private func prepareSpecialWorkAroundForDismissGestureRecognizers() { 69 | var size: Size? 70 | if presentation.showDirection.orientation() == .vertical { 71 | if let sizeProvider = presentation as? PresentationSizeProvider { 72 | size = sizeProvider.presentationSize.height 73 | } else if let sizeProvider = presentation as? PresentationSingleSizeProvider { 74 | size = sizeProvider.size 75 | } 76 | 77 | guard let size = size else { 78 | return 79 | } 80 | 81 | switch size { 82 | case .fullscreen: 83 | addInteractionGestureRecognizer(to: presentedViewController.view) 84 | case .custom(let height): 85 | if height >= UIScreen.main.bounds.height { 86 | addInteractionGestureRecognizer(to: presentedViewController.view) 87 | } else { 88 | addInteractionGestureRecognizer(to: presentationController!.dimmingView) 89 | addInteractionGestureRecognizer(to: presentationController!.blurView) 90 | } 91 | case .halfscreen: 92 | addInteractionGestureRecognizer(to: presentationController!.dimmingView) 93 | addInteractionGestureRecognizer(to: presentationController!.blurView) 94 | 95 | } 96 | 97 | } else if presentation.showDirection.orientation() == .horizontal { 98 | if let sizeProvider = presentation as? PresentationSizeProvider { 99 | size = sizeProvider.presentationSize.width 100 | } else if let singleSizeProvider = presentation as? PresentationSingleSizeProvider { 101 | size = singleSizeProvider.size 102 | } 103 | 104 | guard let size = size else { 105 | return 106 | } 107 | 108 | switch size { 109 | case .fullscreen: 110 | addInteractionGestureRecognizer(to: presentedViewController.view) 111 | case .custom(let width): 112 | if width >= UIScreen.main.bounds.width { 113 | addInteractionGestureRecognizer(to: presentedViewController.view) 114 | } else { 115 | addInteractionGestureRecognizer(to: presentationController!.dimmingView) 116 | addInteractionGestureRecognizer(to: presentationController!.blurView) 117 | } 118 | case .halfscreen: 119 | addInteractionGestureRecognizer(to: presentationController!.dimmingView) 120 | addInteractionGestureRecognizer(to: presentationController!.blurView) 121 | } 122 | } 123 | } 124 | 125 | private func addInteractionGestureRecognizer(to view: UIView) { 126 | var dismissDirection = presentation.showDirection 127 | if let dismissProvider = presentation as? PresentationDismissDirectionProvider { 128 | dismissDirection = dismissProvider.dismissDirection 129 | } 130 | 131 | let dragDirection = presentationType == .show ? presentation.showDirection : dismissDirection 132 | switch configuration.dragMode { 133 | case .canvas: 134 | let gesture = UIPanGestureRecognizer(target: self, action: #selector(handleGesture(_:))) 135 | if #available(iOS 11.0, *) { 136 | gesture.name = Constants.gestureRecognizerIdentifier 137 | } 138 | view.addGestureRecognizer(gesture) 139 | case .edge: 140 | let gesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(handleGesture(_:))) 141 | gesture.edges = presentationType == .dismiss ? dragDirection.dismissRectEdges : dragDirection.showRectEdges 142 | if #available(iOS 11.0, *) { 143 | gesture.name = Constants.gestureRecognizerIdentifier 144 | } 145 | view.addGestureRecognizer(gesture) 146 | } 147 | } 148 | 149 | @objc func handleGesture(_ gestureRecognizer: UIPanGestureRecognizer) { 150 | guard let superView = gestureRecognizer.view?.superview else { 151 | return 152 | } 153 | 154 | var direction: Direction = presentation.showDirection 155 | if presentationType == .dismiss, let dismissProvider = presentation as? PresentationDismissDirectionProvider { 156 | direction = dismissProvider.dismissDirection 157 | } 158 | 159 | let translation = gestureRecognizer.translation(in: superView) 160 | var progress = getProgress(for: direction, translation: translation) 161 | progress = CGFloat(min(max(Float(progress), 0.0), 1.0)) 162 | updateTransition(with: progress, and: gestureRecognizer.state) 163 | } 164 | 165 | private func getProgress(for direction: Direction, translation: CGPoint) -> CGFloat { 166 | let height = presentedViewController.view.frame.height 167 | let width = presentedViewController.view.frame.width 168 | switch (direction, presentationType) { 169 | case (.top, .show), (.bottom, .dismiss): 170 | return (abs(max(0,translation.y)) / height) 171 | case (.top, .dismiss), (.bottom, .show): 172 | return (abs(min(0,translation.y)) / height) 173 | case (.left, .show), (.right, .dismiss): 174 | return (abs(max(0,translation.x)) / width) 175 | case (.right, .show),(.left, .dismiss): 176 | return (abs(min(0,translation.x)) / width) 177 | } 178 | } 179 | } 180 | 181 | extension InteractionController { 182 | func updateTransition(with progress: CGFloat, and state: UIGestureRecognizer.State) { 183 | switch state { 184 | case .began: 185 | interactionInProgress = true 186 | performTransition(for: presentationType) 187 | case .changed: 188 | shouldCompleteTransition = progress > configuration.completionThreshold 189 | update(progress) 190 | case .cancelled: 191 | interactionInProgress = false 192 | cancel() 193 | case .ended: 194 | interactionInProgress = false 195 | if (!shouldCompleteTransition && progress == 0) { 196 | presentationController?.cancelAnimationEffect() 197 | } 198 | shouldCompleteTransition ? finish() : cancel() 199 | default: 200 | break 201 | } 202 | } 203 | 204 | private func performTransition(for type: PresentationType) { 205 | switch presentationType { 206 | case .show: 207 | presentingViewController?.present(presentedViewController, animated: true, completion: nil) 208 | case .dismiss: 209 | presentedViewController.dismiss(animated: true, completion: nil) 210 | 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /Jelly/Classes/private/Extensions/AnimationCurve+AnimationOptions.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIView.AnimationCurve { 4 | var animationOptions: UIView.AnimationOptions { 5 | switch self { 6 | case .easeInOut: 7 | return UIView.AnimationOptions.curveEaseInOut 8 | case .easeIn: return 9 | UIView.AnimationOptions.curveEaseIn 10 | case .easeOut: return 11 | UIView.AnimationOptions.curveEaseOut 12 | case .linear: 13 | return UIView.AnimationOptions.curveLinear 14 | 15 | @unknown default: 16 | return UIView.AnimationOptions.curveLinear 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Jelly/Classes/private/Extensions/Direction+EdgeHelper.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension Direction { 4 | var panDirection: UISwipeGestureRecognizer.Direction { 5 | switch self { 6 | case .top: 7 | return .down 8 | case .left: 9 | return .left 10 | case .bottom: 11 | return .up 12 | case .right: 13 | return .right 14 | } 15 | } 16 | 17 | var showRectEdges: UIRectEdge { 18 | switch self { 19 | case .top: 20 | return .top 21 | case .left: 22 | return .left 23 | case .bottom: 24 | return .bottom 25 | case .right: 26 | return .right 27 | } 28 | } 29 | 30 | var dismissRectEdges: UIRectEdge { 31 | switch self { 32 | case .top: 33 | return .bottom 34 | case .bottom: 35 | return .top 36 | case .left: 37 | return .right 38 | case .right: 39 | return .left 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Jelly/Classes/private/Extensions/Direction+Orientation.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Direction { 4 | public func orientation() -> Orientation { 5 | switch self { 6 | case .left, .right : 7 | return .horizontal 8 | case .top, .bottom : 9 | return .vertical 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Jelly/Classes/private/Extensions/Spring+Values.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension Spring { 4 | var damping: CGFloat { 5 | switch self { 6 | case let .custom(_, damping): 7 | return damping 8 | case .none: 9 | return 1.0 10 | case .tight: 11 | return 0.7 12 | case .medium: 13 | return 0.5 14 | case .loose: 15 | return 0.2 16 | } 17 | } 18 | 19 | var velocity: CGFloat { 20 | switch self { 21 | case .custom(let velocity, _): 22 | return velocity 23 | case .none: 24 | return 0 25 | case .tight: 26 | return 1 27 | case .medium: 28 | return 3 29 | case .loose: 30 | return 4 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Jelly/Classes/private/Extensions/UIView+RoundCorners.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIView { 4 | func roundCorners(_ corners: CACornerMask, radius: CGFloat) { 5 | if #available(iOS 11, *) { 6 | self.layer.cornerRadius = radius 7 | self.layer.maskedCorners = corners 8 | } else { 9 | var cornerMask = UIRectCorner() 10 | if(corners.contains(.layerMinXMinYCorner)){ 11 | cornerMask.insert(.topLeft) 12 | } 13 | if(corners.contains(.layerMaxXMinYCorner)){ 14 | cornerMask.insert(.topRight) 15 | } 16 | if(corners.contains(.layerMinXMaxYCorner)){ 17 | cornerMask.insert(.bottomLeft) 18 | } 19 | if(corners.contains(.layerMaxXMaxYCorner)){ 20 | cornerMask.insert(.bottomRight) 21 | } 22 | let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: cornerMask, cornerRadii: CGSize(width: radius, height: radius)) 23 | let mask = CAShapeLayer() 24 | mask.path = path.cgPath 25 | self.layer.mask = mask 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Jelly/Classes/private/Extensions/UIViewControllerAnimatedTransitioning+Convenience.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIViewControllerAnimatedTransitioning { 4 | func getPresentedViewControllerKeyForPresentationType(type: PresentationType) -> UITransitionContextViewControllerKey { 5 | switch type { 6 | case .show: 7 | return .to 8 | case .dismiss: 9 | return .from 10 | } 11 | } 12 | 13 | func getUnderlyingViewControllerKeyForPresentationType(type: PresentationType) -> UITransitionContextViewControllerKey { 14 | switch type { 15 | case .show: 16 | return .from 17 | case .dismiss: 18 | return .to 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Jelly/Classes/private/Models/CombinedTimingCurveProvider.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @objc(CombinedTimingCurveProvider) class CombinedTimingCurveProvider: NSObject, UITimingCurveProvider { 4 | var springTimingParameters: UISpringTimingParameters? 5 | var cubicTimingParameters: UICubicTimingParameters? 6 | var timingCurveType: UITimingCurveType { 7 | return .composed 8 | } 9 | 10 | init(springParameters: UISpringTimingParameters?, cubicParameters: UICubicTimingParameters?) { 11 | self.springTimingParameters = springParameters 12 | self.cubicTimingParameters = cubicParameters 13 | } 14 | 15 | override init() { 16 | super.init() 17 | } 18 | 19 | required convenience init?(coder aDecoder: NSCoder) { 20 | self.init() 21 | } 22 | 23 | public func copy(with zone: NSZone? = nil) -> Any { 24 | return CombinedTimingCurveProvider(springParameters: self.springTimingParameters, cubicParameters: self.cubicTimingParameters) 25 | } 26 | 27 | public func encode(with aCoder: NSCoder) { 28 | return 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Jelly/Classes/private/Models/PresentationType.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum PresentationType { 4 | case show 5 | case dismiss 6 | } 7 | -------------------------------------------------------------------------------- /Jelly/Classes/private/PresentationController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | protocol PresentationControllerProtocol { 4 | func updatePresentation(presentation: Presentation, duration: Duration) 5 | var dimmingView: UIView { get } 6 | var blurView: UIVisualEffectView { get } 7 | } 8 | 9 | /// A PresentationController tells UIKit what exactly to do with the View that should be presented 10 | /// It also reacts to transtion state changes etc. 11 | /// We use this controller to setup dimmingView, blurView, positioning and resize the the presented ViewController etc. 12 | final class PresentationController: UIPresentationController, PresentationControllerProtocol { 13 | private var presentation: Presentation 14 | private let sizeCalulator: ViewControllerSizeCalculator = ViewControllerSizeCalculator() 15 | 16 | // Blur and Dimming View needs to be accessible because the Interactor attaches Gesture-Recognizer to them when 17 | // using an edge pan gesture. This must be done, because it wont work other wise with non full screen viewController transitions 18 | private(set) var dimmingView: UIView = UIView() 19 | private(set) var blurView: UIVisualEffectView = UIVisualEffectView() 20 | 21 | init(presentedViewController: UIViewController, presentingViewController: UIViewController?, presentation: Presentation) { 22 | self.presentation = presentation 23 | super.init(presentedViewController: presentedViewController, presenting: presentingViewController) 24 | presentedViewController.view.layer.masksToBounds = true 25 | let corners = presentation.presentationUIConfiguration.corners 26 | let radius = presentation.presentationUIConfiguration.cornerRadius 27 | presentedViewController.view.roundCorners(corners, radius: radius) 28 | } 29 | 30 | func updatePresentation(presentation: Presentation, duration: Duration) { 31 | self.presentation = presentation 32 | containerView?.setNeedsLayout() 33 | UIView.animate(withDuration: duration.timeInterval) { 34 | self.containerView?.layoutIfNeeded() 35 | } 36 | } 37 | 38 | override func presentationTransitionWillBegin() { 39 | let backgroundStyle = presentation.presentationUIConfiguration.backgroundStyle 40 | switch backgroundStyle { 41 | case let .blurred(effectStyle): 42 | setupBlurView() 43 | animateBlurView(effectStyle: effectStyle) 44 | case let .dimmed(alpha): 45 | setupDimmingView(withAlpha: alpha) 46 | animateDimmingView(alpha: alpha) 47 | } 48 | } 49 | 50 | override func dismissalTransitionWillBegin() { 51 | let backgroundStyle = presentation.presentationUIConfiguration.backgroundStyle 52 | switch backgroundStyle { 53 | case .blurred: 54 | animateBlurView(effectStyle: nil) 55 | case .dimmed: 56 | animateDimmingView(alpha: 0.0) 57 | } 58 | } 59 | 60 | override func containerViewWillLayoutSubviews() { 61 | presentedView?.frame = frameOfPresentedViewInContainerView 62 | let corners = presentation.presentationUIConfiguration.corners 63 | let radius = presentation.presentationUIConfiguration.cornerRadius 64 | presentedView?.roundCorners(corners, radius: radius) 65 | } 66 | 67 | override func size(forChildContentContainer container: UIContentContainer, 68 | withParentContainerSize parentSize: CGSize) -> CGSize { 69 | return sizeCalulator.size(forChildContentContainer: container, 70 | withParentContainerSize: parentSize, 71 | presentation: presentation) 72 | } 73 | 74 | override var frameOfPresentedViewInContainerView: CGRect { 75 | return sizeCalulator.getFrameOfPresentedViewInContainerView(containerView: containerView!, presentation: presentation, presentedViewController: presentedViewController) 76 | } 77 | 78 | @objc dynamic func handleTap(recognizer: UITapGestureRecognizer) { 79 | if presentation.presentationUIConfiguration.isTapBackgroundToDismissEnabled { 80 | presentingViewController.dismiss(animated: true) 81 | } 82 | } 83 | } 84 | 85 | extension PresentationController { 86 | private func animateBlurView(effectStyle: UIBlurEffect.Style?) { 87 | let effect: UIBlurEffect? = (effectStyle != nil) ? UIBlurEffect(style: effectStyle!) : nil 88 | guard let coordinator = presentedViewController.transitionCoordinator else { 89 | blurView.effect = effect 90 | return 91 | } 92 | 93 | coordinator.animate(alongsideTransition: { _ in 94 | self.blurView.effect = effect 95 | }) 96 | } 97 | 98 | private func animateDimmingView(alpha: CGFloat) { 99 | guard let coordinator = presentedViewController.transitionCoordinator else { 100 | dimmingView.alpha = alpha 101 | return 102 | } 103 | 104 | coordinator.animate(alongsideTransition: { _ in 105 | self.dimmingView.alpha = alpha 106 | }) 107 | } 108 | 109 | public func cancelAnimationEffect() { 110 | let backgroundStyle = presentation.presentationUIConfiguration.backgroundStyle 111 | switch backgroundStyle { 112 | case let .blurred(style): 113 | blurView.effect = UIBlurEffect(style: style) 114 | case let .dimmed(alpha): 115 | dimmingView.alpha = alpha 116 | } 117 | } 118 | } 119 | 120 | extension PresentationController { 121 | private func setupBlurView() { 122 | guard let containerView = containerView else { 123 | return 124 | } 125 | 126 | blurView.translatesAutoresizingMaskIntoConstraints = false 127 | let recognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(recognizer:))) 128 | recognizer.delegate = self 129 | blurView.addGestureRecognizer(recognizer) 130 | containerView.insertSubview(blurView, at: 0) 131 | 132 | blurView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true 133 | blurView.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true 134 | blurView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true 135 | blurView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true 136 | } 137 | 138 | private func setupDimmingView(withAlpha alpha: CGFloat = 0.5) { 139 | guard let containerView = containerView else { 140 | return 141 | } 142 | 143 | dimmingView.translatesAutoresizingMaskIntoConstraints = false 144 | dimmingView.alpha = 0.0 145 | dimmingView.backgroundColor = UIColor(white: 0.0, alpha: alpha) 146 | 147 | let recognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(recognizer:))) 148 | dimmingView.addGestureRecognizer(recognizer) 149 | containerView.insertSubview(dimmingView, at: 0) 150 | 151 | dimmingView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true 152 | dimmingView.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true 153 | dimmingView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true 154 | dimmingView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true 155 | } 156 | } 157 | 158 | extension PresentationController: UIGestureRecognizerDelegate { 159 | func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { 160 | return true 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /Jelly/Classes/private/ViewControllerSizeCalculator.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | protocol ViewControllerSizeCalulatorProtocol { 4 | func size(forChildContentContainer container: UIContentContainer, withParentContainerSize parentSize: CGSize, presentation: Presentation) -> CGSize 5 | func getFrameOfPresentedViewInContainerView(containerView: UIView, presentation: Presentation, presentedViewController: UIViewController) -> CGRect 6 | func getFrameForSlidePresentation(presentation: SlidePresentation, containerView: UIView) -> CGRect 7 | func getFrameForFadeOrCoverPresentation(presentation: Presentation, containerView: UIView, presentedViewController: UIViewController) -> CGRect 8 | func getSizeValue(fromPresentation presentation: SlidePresentation, containerView: UIView) -> CGFloat 9 | func applymarginGuards(toFrame frame: inout CGRect, marginGuards: UIEdgeInsets, container: CGSize) 10 | func limit(frame: inout CGRect, withSize size: CGSize) 11 | func align(frame: inout CGRect, withPresentation presentation: Presentation, containerView: UIView) 12 | } 13 | 14 | /// A ViewControllerSizeCalculator is responsible for all the fancy size and frame calculation that needs to be done because 15 | /// a PresentationController ist not capable of using autolayout for this purpose 16 | class ViewControllerSizeCalculator: ViewControllerSizeCalulatorProtocol { 17 | func size(forChildContentContainer container: UIContentContainer, withParentContainerSize parentSize: CGSize, presentation: Presentation) -> CGSize { 18 | var width: CGFloat = 0.0 19 | var height: CGFloat = 0.0 20 | guard let nonFullScreenPresentation = presentation as? PresentationSizeProvider else { 21 | return parentSize 22 | } 23 | 24 | switch nonFullScreenPresentation.presentationSize.width { 25 | case .fullscreen: 26 | width = parentSize.width 27 | case .halfscreen: 28 | width = parentSize.width / 2 29 | case let .custom(value): 30 | width = value 31 | } 32 | 33 | switch nonFullScreenPresentation.presentationSize.height { 34 | case .fullscreen: 35 | height = parentSize.height 36 | case .halfscreen: 37 | height = parentSize.height / 2 38 | case let .custom(value): 39 | height = value 40 | } 41 | 42 | return CGSize(width: width, height: height) 43 | } 44 | 45 | func getFrameOfPresentedViewInContainerView(containerView: UIView, presentation: Presentation, presentedViewController: UIViewController) -> CGRect { 46 | if let slideIn = presentation as? SlidePresentation { 47 | return getFrameForSlidePresentation(presentation: slideIn, containerView: containerView) 48 | } else { 49 | return getFrameForFadeOrCoverPresentation(presentation: presentation, containerView: containerView, presentedViewController: presentedViewController) 50 | } 51 | } 52 | 53 | func getFrameForFadeOrCoverPresentation(presentation: Presentation, containerView: UIView, presentedViewController: UIViewController) -> CGRect { 54 | guard let sizeProvider = presentation as? PresentationSizeProvider else { 55 | return CGRect(x: 0, y: 0, width: containerView.bounds.size.width, height: containerView.bounds.size.height) 56 | } 57 | 58 | var frame: CGRect = .zero 59 | frame.size = size(forChildContentContainer: presentedViewController, withParentContainerSize: containerView.bounds.size, presentation: presentation) 60 | limit(frame: &frame, withSize: frame.size) 61 | let marginGuards = (sizeProvider as? PresentationMarginGuardsProvider)?.marginGuards ?? .zero 62 | align(frame: &frame, withPresentation: presentation, containerView: containerView) 63 | applymarginGuards(toFrame: &frame, marginGuards: marginGuards, container: containerView.bounds.size) 64 | return frame 65 | } 66 | 67 | func getFrameForSlidePresentation(presentation: SlidePresentation, containerView: UIView) -> CGRect { 68 | var slideInFrame: CGRect = .zero 69 | let size = getSizeValue(fromPresentation: presentation, containerView: containerView) 70 | switch presentation.showDirection { 71 | case .left: 72 | slideInFrame = CGRect(x: 0, y: 0, width: size, height: containerView.bounds.size.height) 73 | case .right: 74 | slideInFrame = CGRect(x: containerView.bounds.size.width - size, y: 0, width: size, height: containerView.bounds.size.height) 75 | case .top: 76 | slideInFrame = CGRect(x: 0, y: 0, width: containerView.bounds.size.width, height: size) 77 | case .bottom: 78 | slideInFrame = CGRect(x: 0, y: containerView.bounds.size.height - size, width: containerView.bounds.size.width, height: size) 79 | } 80 | limit(frame: &slideInFrame, withSize: containerView.bounds.size) 81 | return slideInFrame 82 | } 83 | 84 | func getSizeValue(fromPresentation presentation: SlidePresentation, containerView: UIView) -> CGFloat { 85 | switch presentation.size { 86 | case let .custom(value): 87 | return value 88 | case .halfscreen: 89 | if presentation.showDirection.orientation() == .horizontal { 90 | return (containerView.frame.size.width) / 2 91 | } else { 92 | return (containerView.frame.size.height) / 2 93 | } 94 | case .fullscreen: 95 | if presentation.showDirection.orientation() == .horizontal { 96 | return (containerView.frame.size.width) 97 | } else { 98 | return (containerView.frame.size.height) 99 | } 100 | } 101 | } 102 | 103 | func applymarginGuards(toFrame frame: inout CGRect, marginGuards: UIEdgeInsets, container: CGSize) { 104 | // Apply Width 105 | if frame.width > container.width - (marginGuards.left + marginGuards.right) { 106 | let width = frame.width - (marginGuards.left + marginGuards.right) 107 | frame.size = CGSize(width: width, height: frame.height) 108 | } 109 | 110 | if frame.origin.x < marginGuards.left { 111 | frame.origin.x = marginGuards.left 112 | } 113 | 114 | if (frame.origin.x + frame.size.width) >= container.width { 115 | frame.origin.x = frame.origin.x - marginGuards.right 116 | } 117 | 118 | // Apply Height 119 | if frame.height > container.height - (marginGuards.top + marginGuards.bottom) { 120 | let height = frame.height - (marginGuards.top + marginGuards.bottom) 121 | frame.size = CGSize(width: frame.width, height: height) 122 | } 123 | 124 | if frame.origin.y < marginGuards.top { 125 | frame.origin.y = marginGuards.top 126 | } 127 | 128 | if (frame.origin.y + frame.size.height) >= container.height { 129 | frame.origin.y = frame.origin.y - marginGuards.bottom 130 | } 131 | } 132 | 133 | /// If the Frame is to big, limit resizes it to the size passed in 134 | /// 135 | /// - Parameters: 136 | /// - frame: frame to limit 137 | /// - size: size to apply 138 | func limit(frame: inout CGRect, withSize size: CGSize) { 139 | if frame.size.height > size.height { 140 | frame.origin.y = 0 141 | frame.size.height = size.height 142 | } 143 | 144 | if frame.size.width > size.width { 145 | frame.origin.x = 0 146 | frame.size.width = size.width 147 | } 148 | } 149 | 150 | /// Align alignes the Frame using the alignment options specified inside the presentation object 151 | /// 152 | /// - Parameters: 153 | /// - frame: frame to align 154 | /// - presentation: presentation which will be used to provide the alignment options 155 | func align(frame: inout CGRect, withPresentation presentation: Presentation, containerView: UIView) { 156 | if let alignablePresentation = presentation as? PresentationAlignmentProvider { 157 | // Prepare Horizontal Alignment 158 | switch alignablePresentation.presentationAlignment.horizontalAlignment { 159 | case .center: 160 | frame.origin.x = (containerView.frame.size.width / 2) - (frame.size.width / 2) 161 | case .left: 162 | frame.origin.x = 0 163 | case .right: 164 | frame.origin.x = containerView.frame.size.width - frame.size.width 165 | case let .custom(x): 166 | frame.origin.x = x 167 | } 168 | 169 | // Prepare Vertical Alignment 170 | switch alignablePresentation.presentationAlignment.verticalAlignemt { 171 | case .center: 172 | frame.origin.y = (containerView.frame.size.height / 2) - (frame.size.height / 2) 173 | case .top: 174 | frame.origin.y = 0 175 | case .bottom: 176 | frame.origin.y = containerView.frame.size.height - frame.size.height 177 | case let .custom(y): 178 | frame.origin.y = y 179 | } 180 | } else { 181 | frame.origin.x = (containerView.frame.size.width / 2) - (frame.size.width / 2) 182 | frame.origin.y = (containerView.frame.size.height / 2) - (frame.size.height / 2) 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Animator.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol AnimatorProtocol { 4 | init(presentation: Presentation) 5 | } 6 | 7 | public protocol LiveUpdatable { 8 | func prepare(presentedViewController: UIViewController) 9 | func updateAlignment(alignment: PresentationAlignmentProtocol, duration: Duration) throws 10 | func updateVerticalAlignment(alignment: VerticalAlignment, duration: Duration) throws 11 | func updateHorizontalAlignment(alignment: HorizontalAlignment, duration: Duration) throws 12 | func updateSize(presentationSize: PresentationSize, duration: Duration) throws 13 | func updateWidth(width: Size, duration: Duration) throws 14 | func updateHeight(height: Size, duration: Duration) throws 15 | func updateMarginGuards(marginGuards: UIEdgeInsets, duration: Duration) throws 16 | func updateCorners(radius: CGFloat, corners: CACornerMask, duration: Duration) 17 | } 18 | 19 | /// # Animator 20 | /// An Animator is an UIViewControllerTransitionsDelegate with some extra candy. 21 | /// Basically the Animator is the main class to use when working with Jelly. 22 | public class Animator: NSObject { 23 | private var presentation: Presentation 24 | 25 | private var currentPresentationController: PresentationController! 26 | private weak var presentedViewController: UIViewController! 27 | 28 | private var showInteractionController: InteractionController? 29 | private var dismissInteractionController: InteractionController? 30 | 31 | /// ## designated initializer 32 | /// - Parameter presentation: a custom Presentation Object 33 | public init(presentation: Presentation) { 34 | self.presentation = presentation 35 | super.init() 36 | } 37 | 38 | /// ## prepare 39 | /// Call this function to prepare the viewController you want to present 40 | /// - Parameter viewController: presentedViewController that should be presented in a custom way 41 | public func prepare(presentedViewController: UIViewController) { 42 | // Create InteractionController over here because it needs a reference to the PresentationController 43 | if let interactivePresentation = presentation as? (PresentationShowDirectionProvider & InteractionConfigurationProvider), 44 | let configuration = interactivePresentation.interactionConfiguration, let presentingViewController = configuration.presentingViewController { 45 | self.showInteractionController = InteractionController(configuration: configuration, presentedViewController: presentedViewController, presentingViewController: presentingViewController, presentationType: .show, presentation: interactivePresentation, presentationController: nil) 46 | } 47 | presentedViewController.modalPresentationStyle = .custom 48 | presentedViewController.transitioningDelegate = self 49 | self.presentedViewController = presentedViewController 50 | } 51 | } 52 | 53 | /// ## UIViewControllerTransitioningDelegate Implementation 54 | /// The Animator needs to conform to the UIViewControllerTransitioningDelegate protocol 55 | /// it will provide a custom Presentation-Controller that tells UIKit which extra Views the presentation should have 56 | /// it also returns the interaction controllers for interaction with via gesuture recognizers 57 | extension Animator: UIViewControllerTransitioningDelegate { 58 | /// Gets called from UIKit if presentatioStyle is custom and transitionDelegate is set 59 | public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { 60 | let presentationController = PresentationController(presentedViewController: presented, presentingViewController: presenting, presentation: presentation) 61 | currentPresentationController = presentationController 62 | if let interactivePresentation = presentation as? (InteractionConfigurationProvider & PresentationShowDirectionProvider), 63 | let configuration = interactivePresentation.interactionConfiguration, let presentingViewController = configuration.presentingViewController { 64 | self.dismissInteractionController = InteractionController(configuration: configuration, presentedViewController: presentedViewController, presentingViewController: presentingViewController, presentationType: .dismiss, presentation: interactivePresentation, presentationController: currentPresentationController) 65 | } 66 | return presentationController 67 | } 68 | 69 | /// Each Presentation has two directions 70 | /// Inside a Presention Object you can specify some extra parameters 71 | /// This Parameters will be passed to a custom animator that handles the presentation animation (duration, direction etc.) 72 | public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 73 | return presentation.showAnimator 74 | } 75 | 76 | public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 77 | return presentation.dismissAnimator 78 | } 79 | 80 | public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { 81 | guard dismissInteractionController?.interactionInProgress == true else { 82 | return nil 83 | } 84 | return dismissInteractionController 85 | } 86 | 87 | public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { 88 | guard showInteractionController?.interactionInProgress == true else { 89 | return nil 90 | } 91 | return showInteractionController 92 | } 93 | } 94 | 95 | extension Animator: LiveUpdatable { 96 | public func updateAlignment(alignment: PresentationAlignmentProtocol, duration: Duration) throws { 97 | guard !(presentation is SlidePresentation), var presentation = presentation as? (Presentation & PresentationAlignmentProvider) else { 98 | throw LiveUpdateError.notSupportedOnSlide 99 | } 100 | 101 | presentation.presentationAlignment = alignment 102 | self.presentation = presentation 103 | currentPresentationController.updatePresentation(presentation: presentation, duration: duration) 104 | } 105 | 106 | public func updateVerticalAlignment(alignment: VerticalAlignment, duration: Duration) throws { 107 | guard !(presentation is SlidePresentation), var presentation = presentation as? (Presentation & PresentationAlignmentProvider) else { 108 | throw LiveUpdateError.notSupportedOnSlide 109 | } 110 | 111 | presentation.presentationAlignment = PresentationAlignment(vertical: alignment, horizontal: presentation.presentationAlignment.horizontalAlignment) 112 | self.presentation = presentation 113 | currentPresentationController.updatePresentation(presentation: presentation, duration: duration) 114 | } 115 | 116 | public func updateHorizontalAlignment(alignment: HorizontalAlignment, duration: Duration) throws { 117 | guard !(presentation is SlidePresentation), var presentation = presentation as? (Presentation & PresentationAlignmentProvider) else { 118 | throw LiveUpdateError.notSupportedOnSlide 119 | } 120 | 121 | presentation.presentationAlignment = PresentationAlignment(vertical: presentation.presentationAlignment.verticalAlignemt, horizontal: alignment) 122 | self.presentation = presentation 123 | currentPresentationController.updatePresentation(presentation: presentation, duration: duration) 124 | } 125 | 126 | public func updateSize(presentationSize: PresentationSize, duration: Duration) throws { 127 | guard !(presentation is SlidePresentation), var presentation = presentation as? (PresentationSizeProvider & Presentation) else { 128 | throw LiveUpdateError.notSupportedOnSlide 129 | } 130 | 131 | presentation.presentationSize = presentationSize 132 | self.presentation = presentation 133 | currentPresentationController.updatePresentation(presentation: presentation, duration: duration) 134 | } 135 | 136 | public func updateWidth(width: Size, duration: Duration) throws { 137 | guard !(presentation is SlidePresentation) else { 138 | throw LiveUpdateError.notSupportedOnSlide 139 | } 140 | 141 | if var presentation = presentation as? FadePresentation { 142 | presentation.presentationSize = PresentationSize(width: width, height: presentation.presentationSize.height) 143 | self.presentation = presentation 144 | currentPresentationController.updatePresentation(presentation: presentation, duration: duration) 145 | } else if var presentation = presentation as? CoverPresentation { 146 | presentation.presentationSize = PresentationSize(width: width, height: presentation.presentationSize.height) 147 | self.presentation = presentation 148 | currentPresentationController.updatePresentation(presentation: presentation, duration: duration) 149 | } 150 | } 151 | 152 | public func updateHeight(height: Size, duration: Duration) throws { 153 | guard !(presentation is SlidePresentation) else { 154 | throw LiveUpdateError.notSupportedOnSlide 155 | } 156 | 157 | if var presentation = presentation as? FadePresentation { 158 | presentation.presentationSize = PresentationSize(width: presentation.presentationSize.height, height: height) 159 | self.presentation = presentation 160 | currentPresentationController.updatePresentation(presentation: presentation, duration: duration) 161 | } else if var presentation = presentation as? CoverPresentation { 162 | presentation.presentationSize = PresentationSize(width: presentation.presentationSize.height, height: height) 163 | self.presentation = presentation 164 | currentPresentationController.updatePresentation(presentation: presentation, duration: duration) 165 | } 166 | } 167 | 168 | public func updateMarginGuards(marginGuards: UIEdgeInsets, duration: Duration) throws { 169 | guard !(presentation is SlidePresentation), var presentation = presentation as? (Presentation & PresentationMarginGuardsProvider) else { 170 | throw LiveUpdateError.notSupportedOnSlide 171 | } 172 | 173 | presentation.marginGuards = marginGuards 174 | self.presentation = presentation 175 | currentPresentationController.updatePresentation(presentation: presentation, duration: duration) 176 | } 177 | 178 | public func updateCorners(radius: CGFloat, corners: CACornerMask, duration: Duration) { 179 | var presentation = self.presentation 180 | presentation.presentationUIConfiguration.cornerRadius = radius 181 | presentation.presentationUIConfiguration.corners = corners 182 | self.presentation = presentation 183 | currentPresentationController.updatePresentation(presentation: presentation, duration: duration) 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Constants.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Constants { 4 | // This is only available on iOS 11 and later 5 | public static let gestureRecognizerIdentifier = "JellyInteractivePanGestureRecognizer" 6 | } 7 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Deprecation.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @available(*, renamed: "SlidePresentation") 4 | public typealias JellyShiftInPresentation = SlidePresentation 5 | 6 | @available(*, renamed: "CoverPresentation") 7 | public typealias JellySlideInPresentation = CoverPresentation 8 | 9 | @available(*, renamed: "FadePresentation") 10 | public typealias JellyFadeInPresentation = FadePresentation 11 | 12 | @available(*, renamed: "Animator") 13 | public typealias JellyAnimator = Animator 14 | 15 | @available(*, renamed: "Spring") 16 | public typealias Jellyness = Spring 17 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Extensions/Size+Equatable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Size: Equatable { 4 | public static func == (lhs: Size, rhs: Size) -> Bool { 5 | switch (lhs, rhs) { 6 | case (.fullscreen,.fullscreen), (.halfscreen,.halfscreen): 7 | return true 8 | case (.custom(let lhsValue), .custom(let rhsValue)): 9 | return lhsValue == rhsValue 10 | default: 11 | return false 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Jelly/Classes/public/LiveUpdateError.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum LiveUpdateError: Error { 4 | case notSupportedOnSlide 5 | } 6 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/BackgroundStyle.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public enum BackgroundStyle { 4 | case dimmed(alpha: CGFloat) 5 | case blurred(effectStyle: UIBlurEffect.Style) 6 | } 7 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/Direction.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum Direction { 4 | case top 5 | case bottom 6 | case left 7 | case right 8 | } 9 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/DragMode.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum DragMode { 4 | case edge 5 | case canvas 6 | } 7 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/Duration.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum Duration { 4 | case custom(duration: TimeInterval) 5 | case ultraSlow 6 | case slow 7 | case medium 8 | case normal 9 | case fast 10 | case reallyFast 11 | 12 | var timeInterval: TimeInterval { 13 | switch self { 14 | case .custom(let duration): 15 | return duration 16 | case .ultraSlow: 17 | return 2.0 18 | case .slow: 19 | return 1.0 20 | case .medium: 21 | return 0.5 22 | case .normal: 23 | return 0.35 24 | case .fast: 25 | return 0.2 26 | case .reallyFast: 27 | return 0.1 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/HorizontalAlignment.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public enum HorizontalAlignment { 4 | case left 5 | case right 6 | case center 7 | case custom(x: CGFloat) 8 | } 9 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/Orientation.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum Orientation { 4 | case horizontal 5 | case vertical 6 | } 7 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/Presentation/InteractionConfiguration.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct InteractionConfiguration { 4 | let completionThreshold: CGFloat 5 | let dragMode: DragMode 6 | let mode: InteractionMode 7 | 8 | weak var presentingViewController: UIViewController! 9 | 10 | public init(presentingViewController: UIViewController, completionThreshold: CGFloat = 0.5, dragMode: DragMode, mode: InteractionMode = [.dismiss, .present]) { 11 | self.completionThreshold = completionThreshold 12 | self.dragMode = dragMode 13 | self.presentingViewController = presentingViewController 14 | self.mode = mode 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/Presentation/InteractionMode.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct InteractionMode: OptionSet { 4 | public let rawValue: Int 5 | 6 | public init(rawValue: Int) { 7 | self.rawValue = rawValue 8 | } 9 | 10 | public static let present = InteractionMode(rawValue: 1 << 0) 11 | public static let dismiss = InteractionMode(rawValue: 1 << 1) 12 | } 13 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/Presentation/PresentationAlignment.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct PresentationAlignment: PresentationAlignmentProtocol { 4 | public var verticalAlignemt: VerticalAlignment 5 | public var horizontalAlignment: HorizontalAlignment 6 | 7 | public static var centerAlignment: PresentationAlignment { 8 | return PresentationAlignment(vertical: .center, horizontal: .center) 9 | } 10 | 11 | public init(vertical: VerticalAlignment, horizontal: HorizontalAlignment) { 12 | self.verticalAlignemt = vertical 13 | self.horizontalAlignment = horizontal 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/Presentation/PresentationSize.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct PresentationSize: PresentationSizeProtocol { 4 | public var width: Size 5 | public var height: Size 6 | 7 | public init(width: Size = .fullscreen, 8 | height: Size = .fullscreen) { 9 | self.width = width 10 | self.height = height 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/Presentation/PresentationTiming.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct PresentationTiming: PresentationTimingProtocol { 4 | public var duration: Duration 5 | public var presentationCurve: UIView.AnimationCurve 6 | public var dismissCurve: UIView.AnimationCurve 7 | 8 | public init(duration: Duration = .medium, 9 | presentationCurve: UIView.AnimationCurve = .linear, 10 | dismissCurve: UIView.AnimationCurve = .linear) { 11 | self.duration = duration 12 | self.presentationCurve = presentationCurve 13 | self.dismissCurve = dismissCurve 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/Presentation/PresentationUIConfiguration.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol PresentationUIConfigurationProtocol { 4 | var cornerRadius: CGFloat { get set } 5 | var backgroundStyle: BackgroundStyle { get set } 6 | var isTapBackgroundToDismissEnabled: Bool { get set } 7 | var corners: CACornerMask { get set } 8 | } 9 | 10 | public struct PresentationUIConfiguration: PresentationUIConfigurationProtocol { 11 | public var cornerRadius: CGFloat 12 | public var backgroundStyle: BackgroundStyle 13 | public var isTapBackgroundToDismissEnabled: Bool 14 | public var corners: CACornerMask 15 | 16 | public init(cornerRadius: CGFloat = 0.0, backgroundStyle: BackgroundStyle = .dimmed(alpha: 0.5), isTapBackgroundToDismissEnabled: Bool = true, corners: CACornerMask = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner]) { 17 | self.cornerRadius = cornerRadius 18 | self.backgroundStyle = backgroundStyle 19 | self.isTapBackgroundToDismissEnabled = isTapBackgroundToDismissEnabled 20 | self.corners = corners 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/Size.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public enum Size { 4 | case fullscreen 5 | case halfscreen 6 | case custom(value: CGFloat) 7 | } 8 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/Spring.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public enum Spring { 4 | case custom(velocity: CGFloat, damping: CGFloat) 5 | case none 6 | case tight 7 | case medium 8 | case loose 9 | } 10 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Configuration/VerticalAlignment.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public enum VerticalAlignment { 4 | case top 5 | case bottom 6 | case center 7 | case custom(y: CGFloat) 8 | } 9 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Presentations/CoverPresentation.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct CoverPresentation: Presentation, 4 | PresentationDismissDirectionProvider, 5 | PresentationShowDirectionProvider, 6 | PresentationMarginGuardsProvider, 7 | PresentationSizeProvider, 8 | PresentationAlignmentProvider, 9 | PresentationSpringProvider, 10 | InteractionConfigurationProvider { 11 | public var showDirection: Direction 12 | public var dismissDirection: Direction 13 | public var presentationTiming: PresentationTimingProtocol 14 | public var presentationUIConfiguration: PresentationUIConfigurationProtocol 15 | public var presentationSize: PresentationSizeProtocol 16 | public var presentationAlignment: PresentationAlignmentProtocol 17 | public var spring: Spring 18 | public var marginGuards: UIEdgeInsets 19 | public var interactionConfiguration: InteractionConfiguration? 20 | 21 | public init(directionShow: Direction, 22 | directionDismiss: Direction, 23 | uiConfiguration: PresentationUIConfigurationProtocol = PresentationUIConfiguration(), 24 | size: PresentationSizeProtocol = PresentationSize(), 25 | alignment: PresentationAlignmentProtocol = PresentationAlignment.centerAlignment, 26 | marginGuards: UIEdgeInsets = .zero, 27 | timing: PresentationTimingProtocol = PresentationTiming(), 28 | spring: Spring = .none, 29 | interactionConfiguration: InteractionConfiguration? = nil) { 30 | dismissDirection = directionDismiss 31 | showDirection = directionShow 32 | presentationTiming = timing 33 | presentationUIConfiguration = uiConfiguration 34 | presentationSize = size 35 | presentationAlignment = alignment 36 | self.spring = spring 37 | self.marginGuards = marginGuards 38 | presentationAlignment = alignment 39 | self.interactionConfiguration = interactionConfiguration 40 | } 41 | } 42 | 43 | extension CoverPresentation: PresentationAnimatorProvider { 44 | public var showAnimator: UIViewControllerAnimatedTransitioning { 45 | return CoverAnimator(presentationType: .show, presentation: self) 46 | } 47 | 48 | public var dismissAnimator: UIViewControllerAnimatedTransitioning { 49 | return CoverAnimator(presentationType: .dismiss, presentation: self) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Presentations/FadePresentation.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct FadePresentation: Presentation, 4 | PresentationAlignmentProvider, 5 | PresentationSizeProvider, 6 | PresentationMarginGuardsProvider { 7 | public var presentationAlignment: PresentationAlignmentProtocol 8 | public var presentationSize: PresentationSizeProtocol 9 | public var presentationUIConfiguration: PresentationUIConfigurationProtocol 10 | public var marginGuards: UIEdgeInsets 11 | public var presentationTiming: PresentationTimingProtocol 12 | 13 | public init(alignment: PresentationAlignmentProtocol = PresentationAlignment.centerAlignment, 14 | size: PresentationSizeProtocol = PresentationSize(width: .fullscreen, height: .fullscreen), 15 | marginGuards: UIEdgeInsets = .zero, 16 | timing: PresentationTimingProtocol = PresentationTiming(duration: .normal, presentationCurve: .linear, dismissCurve: .linear), 17 | ui: PresentationUIConfigurationProtocol = PresentationUIConfiguration(cornerRadius: 0.0, backgroundStyle: .dimmed(alpha: 0.5), isTapBackgroundToDismissEnabled: true, corners: [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner])) { 18 | presentationAlignment = alignment 19 | presentationSize = size 20 | presentationTiming = timing 21 | presentationUIConfiguration = ui 22 | self.marginGuards = marginGuards 23 | } 24 | } 25 | 26 | extension FadePresentation: PresentationAnimatorProvider { 27 | public var showAnimator: UIViewControllerAnimatedTransitioning { 28 | return FadeAnimator(presentationType: .show, presentation: self) 29 | } 30 | 31 | public var dismissAnimator: UIViewControllerAnimatedTransitioning { 32 | return FadeAnimator(presentationType: .dismiss, presentation: self) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Presentations/SlidePresentation.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct SlidePresentation: Presentation, 4 | PresentationShowDirectionProvider, 5 | PresentationSingleSizeProvider, 6 | InteractionConfigurationProvider { 7 | public var presentationTiming: PresentationTimingProtocol 8 | public var presentationUIConfiguration: PresentationUIConfigurationProtocol 9 | public var showDirection: Direction 10 | public var size: Size 11 | public var spring: Spring 12 | public var interactionConfiguration: InteractionConfiguration? 13 | public var parallax: CGFloat 14 | 15 | public init(timing: PresentationTimingProtocol = PresentationTiming(), 16 | uiConfiguration: PresentationUIConfigurationProtocol = PresentationUIConfiguration(), 17 | direction: Direction = .bottom, 18 | size: Size = .fullscreen, 19 | spring: Spring = .none, 20 | parallax: CGFloat = 1.0, 21 | interactionConfiguration: InteractionConfiguration? = nil) { 22 | presentationUIConfiguration = uiConfiguration 23 | presentationTiming = timing 24 | self.size = size 25 | self.spring = spring 26 | showDirection = direction 27 | self.interactionConfiguration = interactionConfiguration 28 | self.parallax = parallax 29 | } 30 | } 31 | 32 | extension SlidePresentation: PresentationAnimatorProvider { 33 | public var showAnimator: UIViewControllerAnimatedTransitioning { 34 | return SlideAnimator(presentationType: .show, presentation: self) 35 | } 36 | 37 | public var dismissAnimator: UIViewControllerAnimatedTransitioning { 38 | return SlideAnimator(presentationType: .dismiss, presentation: self) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/InteractionConfigurationProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol InteractionConfigurationProvider { 4 | var interactionConfiguration: InteractionConfiguration? { get } 5 | } 6 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/Presentation.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol Presentation: PresentationTimingInformationProvider, 4 | PresentationUIConfigurationProvider, 5 | PresentationAnimatorProvider { 6 | } 7 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationAlignmentProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol PresentationAlignmentProvider { 4 | var presentationAlignment: PresentationAlignmentProtocol { get set } 5 | } 6 | 7 | public protocol PresentationAlignmentProtocol { 8 | var verticalAlignemt : VerticalAlignment { get set } 9 | var horizontalAlignment : HorizontalAlignment { get set } 10 | } 11 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationAnimatorProvider.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol PresentationAnimatorProvider { 4 | var showAnimator: UIViewControllerAnimatedTransitioning { get } 5 | var dismissAnimator: UIViewControllerAnimatedTransitioning { get } 6 | } 7 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationDirectionDismissProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol PresentationDismissDirectionProvider { 4 | var dismissDirection: Direction { get set } 5 | } 6 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationDirectionShowProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol PresentationShowDirectionProvider { 4 | var showDirection: Direction { get set } 5 | } 6 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationHeightProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol PresentationHeightProvider { 4 | var height: Size { get set } 5 | } 6 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationInteractiveAnimatorProvider.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol PresentationInteractiveAnimatorProvider { 4 | var interactiveShowAnimator: UIViewControllerInteractiveTransitioning? { get } 5 | var interactiveDismissAnimator: UIViewControllerInteractiveTransitioning? { get } 6 | } 7 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationMarginGuardsProvider.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol PresentationMarginGuardsProvider { 4 | /* 5 | If the width or height is bigger than the container we are working in, 6 | marginGuards will kick in and limit the size the specified margins 7 | */ 8 | var marginGuards: UIEdgeInsets { get set } 9 | } 10 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationSingleSizeProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol PresentationSingleSizeProvider { 4 | var size: Size { get set } 5 | } 6 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationSizeProtocol.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol PresentationSizeProtocol: PresentationWidthProvider, PresentationHeightProvider { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationSizeProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol PresentationSizeProvider { 4 | var presentationSize: PresentationSizeProtocol { get set } 5 | } 6 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationSpringProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol PresentationSpringProvider { 4 | var spring: Spring { get set } 5 | } 6 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationTimingInformationProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol PresentationTimingInformationProvider { 4 | var presentationTiming: PresentationTimingProtocol { get set} 5 | } 6 | 7 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationTimingProtocol.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol PresentationTimingProtocol { 4 | var duration: Duration { get set } 5 | var presentationCurve: UIView.AnimationCurve { get set } 6 | var dismissCurve: UIView.AnimationCurve { get set } 7 | } 8 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationUIConfigurationProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol PresentationUIConfigurationProvider { 4 | var presentationUIConfiguration: PresentationUIConfigurationProtocol { get set } 5 | } 6 | 7 | 8 | -------------------------------------------------------------------------------- /Jelly/Classes/public/Models/Protocols/PresentationWidthProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol PresentationWidthProvider { 4 | var width: Size { get set } 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Sebastian Boldt 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Jelly", 6 | platforms: [.iOS(.v10)], 7 | products: [.library(name: "Jelly", targets: ["Jelly"])], 8 | targets: [.target(name: "Jelly", path: "Jelly")], 9 | swiftLanguageVersions: [.v5] 10 | ) 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Jelly-Animators: Elegant Viewcontroller Animations in Swift](https://github.com/SebastianBoldt/Jelly/blob/master/Github/Jellyfish.png?raw=true) 2 | 3 | current version 4 | current version 5 | twitter handle 6 | Swift 4.2 compatible 7 | platform 8 | SPM-Compatible 9 | carthage support 10 | license 11 | 12 | Jelly is a library for animated, non-interactive & interactive viewcontroller
13 | transitions and presentations with the focus on a simple and yet flexible API. 14 | 15 | 17 | 19 | 21 | 23 | 25 | 27 | 29 | 31 | 32 | With a few lines of source code, interactive UIViewController transitions
33 | and custom resizable UIViewController presentations can be created,
34 | without the use of the cumbersome UIKit Transitioning API. 35 | 36 | ```swift 37 | 38 | var slidePresentation = SlidePresentation(direction: .left) 39 | let animator = Animator(presentation: slidePresentation) 40 | animator.prepare(viewController: viewController) 41 | present(viewController, animated: true, completion: nil) 42 | ``` 43 | 44 | 45 | 46 | 1. Create a `Presentation` Object 47 | 2. Configure an `Animator` with the *Presentation* 48 | 3. Call the `prepare` Function 49 | 4. Use the native `UIViewController` presentation function. 50 | 51 | ```swift 52 | class ViewController : UIViewController { 53 | var animator: Jelly.Animator? 54 | override func viewDidLoad() { 55 | super.viewDidLoad() 56 | let viewController = YourViewController() 57 | let presentation = SlidePresentation(direction: .left) 58 | animator = Animator(presentation:presentation) 59 | animator?.prepare(presentedViewController: viewController) 60 | present(viewController, animated: true, completion: nil) 61 | } 62 | } 63 | ``` 64 | 65 | ***DO NOT FORGET TO KEEP A STRONG 💪 REFERENCE*** 66 | 67 | Because the `transitioningDelegate` of a `UIViewController` is weak, you need to 68 | hold a strong reference to the `Animator` inside the `UIViewController` you are presenting from or 69 | the central object that maintains your presentations. 70 | 71 | 72 | 73 | Interactive transitions can be activated for the *slide* and the *cover* transitions. 74 | If the transitions are to be interactive, only an `InteractionConfiguration` object has to be passed to the presentation. 75 | 76 | 78 | 80 | 82 | 84 | 85 | Here 3 parameters play an important role. First, the `completionThreshold`, which determines the percentage of the animation that is automatically completed as soon as the user finishes the interaction. 86 | The second parameter is the actual type of interaction. Jelly offers the `.edge` and the `.canvas` type. 87 | In an `.edge` transition, the user must execute the gesture from the edge of the screen. 88 | When using the `.canvas` type, gesture recognizers are configured so that direct interaction with the presenting and presented view leads to the transition. 89 | The last parameter is called `mode`. Using the mode you can limit the interaction to presentation or dismiss interaction (default = `[.present,.dismiss]`). 90 | 91 | ```swift 92 | let viewController = YourViewController() 93 | let interaction = InteractionConfiguration(presentingViewController: self, completionThreshold: 0.5, dragMode: .edge, mode: .dismiss) 94 | let presentation = SlidePresentation(direction: .right, interactionConfiguration: interaction) 95 | let animator = Animator(presentation: presentation) 96 | animator.prepare(presentedViewController: viewController) 97 | 98 | ``` 99 | 100 | 101 | Jelly 2.0 also provides a new feature called *LIVE UPDATE*. 102 | Using Jellys new `Live Update API` it is now possible to update the `alignment`, `size`, `margin-guards` and `corner radius` when the viewcontroller is already visible. 103 | 104 | 106 | 108 | 109 | These are the new live update functions provided by the Animator. 110 | 111 | * `updateAlignment(alignment: PresentationAlignment, duration: Duration)` - Cover & Fade 112 | * `updateVerticalAlignment(alignment: VerticalAlignment, duration: Duration)` - Cover & Fade 113 | * `updateHorizontalAlignment(alignment: HorizontalAlignment, duration: Duration)` - Cover & Fade 114 | * `updateSize(presentationSize: PresentationSize, duration: Duration)` - Cover & Fade 115 | * `updateWidth(width: Size, duration: Duration)` - Cover, Fade and horizontal Slide 116 | * `updateHeight(height: Size, duration: Duration)` - Cover, Fade and vertical Slide 117 | * `updateMarginGuards(marginGuards: UIEdgeInsets, duration: Duration)` - Cover & Fade 118 | * `updateCorners(radius: CGFloat, corners: CACornerMask, duration: Duration)` - Cover & Fade & Slide 119 | 120 | Some of them will throw an exception if used on a not supported presentationType.
121 | For example: It is currently not possible to update the size on a Slide-Presentation. 122 | 123 | 124 | 125 | The presentation types can be configured with various settings: 126 | 127 | * `size` 128 | * `margin guards` 129 | * `direction` 130 | * `horizontal & vertical alignment` 131 | * `dimmed and blurred backgroundStyle` 132 | * `duration` 133 | * `presentation and dismiss curve` 134 | * `spring damping & velocity` 135 | * `corner specification` & `corner radius` 136 | * `completion threshold` 137 | * `interactive drag mode` (edge or pan) 138 | * `interaction mode` (present, dismiss) 139 | * `parallax` (Just available on slide Presentations) 140 | 141 | Each component is explained in more detail in the Jelly Wiki. 143 | 144 | 145 | 146 | 147 | Deployment target of your App is >= iOS 11.0 148 | 149 | 150 | 151 | 152 | Jelly is available through [CocoaPods](http://cocoapods.org). To install 153 | it, simply add the following line to your Podfile: 154 | 155 | ```ruby 156 | pod 'Jelly', '~> 2.2.2' 157 | ``` 158 | 159 | or use SPM 160 | 161 | 162 | 163 | * Mentioned in iOS Dev Weekly by @Dave Verwer - Issue NO. 112 164 | 165 | * Mentioned in This Week in Swift by @Natasha the Robot - Issue No. 279 166 | 167 | 168 | 169 | 170 | 171 | Sebastian Boldt, https://www.sebastianboldt.com 172 | 173 | I am a mobile software architect and developer specializing in iOS. 174 | Passionate about creating awesome user experiences, designing beautiful user interfaces, 175 | and writing maintainable, structured, and best-practice orientated code. 176 | Continuously trying to improve skills and learn new technologies. 177 | 178 | current version 179 | 180 | 181 | 182 | Jelly is available under the MIT license. See the LICENSE file for more info. 183 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------