├── .github ├── ISSUE_TEMPLATE │ ├── pulley-bug-report.md │ ├── pulley-feature-request.md │ └── pulley-question.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── deploy_to_cocoapods.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Package.swift ├── Pulley.podspec ├── Pulley.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ ├── Pulley.xcscheme │ └── PulleyDemo.xcscheme ├── Pulley ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── CustomMaskExample.swift ├── DrawerContentViewController.swift ├── Info.plist ├── Main.storyboard ├── PrimaryContentViewController.swift ├── PrimaryTransitionTargetViewController.swift └── Pulley.h ├── PulleyLib ├── PulleyPassthroughScrollView.swift ├── PulleyViewController.swift ├── UIView+constrainToParent.swift └── UIViewController+PulleyViewController.swift └── README.md /.github/ISSUE_TEMPLATE/pulley-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Pulley bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: ulmentflam 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | - Did you experience this behavior on in the simulator, on a physical device, or both: [e.g. simulator/device/both] 15 | - Can you reproduce the behavior in the Pulley Sample/Demo App: [e.g. Yes/No] 16 | - Steps to reproduce the behavior in the Pulley Sample/Demo App: 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Screenshots or Recordings** 26 | If applicable, add screenshots or recordings to help explain your problem. 27 | 28 | **Pulley (please complete the following information):** 29 | - Version: [e.g. 2.8.5] 30 | - Any customizations or tweaks: [e.g. Yes/No] 31 | - Did you Subclass: [e.g. Yes/No] 32 | 33 | **Xcode (please complete the following information):** 34 | - Version: [e.g. 12] 35 | - Installation: [e.g. Cocoapods] 36 | - Interface Builder: [e.g. Yes/No] 37 | 38 | 39 | **iOS Devices (please complete the following information):** 40 | - Device: [e.g. iPhone6] 41 | - OS: [e.g. iOS8.1] 42 | - Version [e.g. 22] 43 | - Simulator: [e.g. Yes/No] 44 | 45 | **Additional context** 46 | Add any other context, Xcode logs, code snippets, or useful information about the problem here. 47 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/pulley-feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Pulley feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: ulmentflam 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/pulley-question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Pulley question 3 | about: Ask a question about this project 4 | title: "[QUESTION]" 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the question** 11 | A clear and concise question about the functionality of pulley or a delegate method. 12 | 13 | **Code snippets and helpful information** 14 | Any code snippets, screenshots, recordings, or any other helpful information for addressing your question. 15 | 16 | **Pulley Information (optional):** 17 | - Version: [e.g. 2.8.5] 18 | - Any customizations or tweaks: [e.g. Yes/No] 19 | - Did you Subclass: [e.g. Yes/No] 20 | 21 | **Xcode Information (optional):** 22 | - Version: [e.g. 12] 23 | - Installation: [e.g. Cocoapods] 24 | - Interface Builder: [e.g. Yes/No] 25 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. 4 | 5 | Fixes # (issue) 6 | 7 | ## Type of change 8 | 9 | Please delete options that are not relevant. 10 | 11 | - [ ] Bug fix (non-breaking change which fixes an issue) 12 | - [ ] New feature (non-breaking change which adds functionality) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 14 | - [ ] This change requires a documentation update 15 | 16 | # How Has This Been Tested? 17 | 18 | Please describe the tests that you performed to verify your changes. Provide instructions so we can reproduce. Please provide details on how you tested supported versions of iOS and Xcode. Please make sure you have tested on all device size classes and orientations. Please also list any relevant details for your test configuration 19 | 20 | - [ ] Test iOS 14 21 | - [ ] Test iOS 13 22 | - [ ] Test iOS 12 23 | - [ ] Test iOS 11 24 | - [ ] Test iOS 10 25 | - [ ] Test iOS 9 26 | 27 | **Test Configuration** 28 | - Xcode Versions: [e.g. 12.x, 11.x, 10.x, 9.x] 29 | - Simulators: [e.g. iPad Pro (14.x), iPhone SE (12.x)] 30 | - Physical Hardware: [e.g. iPad Pro (14.x), iPhone SE (12.x)] 31 | 32 | # Checklist: 33 | 34 | - [ ] My code follows the style guidelines of this project 35 | - [ ] My code does not break backwards compatibility with earlier versions of PulleyLib 36 | - [ ] My code is fully functional with all supported device sizes and orientations 37 | - [ ] I have performed a self-review of my own code 38 | - [ ] I have commented my code, particularly in hard-to-understand areas 39 | - [ ] I have checked that my code does not break the behavior of the Sample/Demo app 40 | - [ ] I have made corresponding changes to the documentation 41 | - [ ] My changes generate no new warnings 42 | - [ ] I have tested and can prove my fix is effective or that my feature works 43 | -------------------------------------------------------------------------------- /.github/workflows/deploy_to_cocoapods.yml: -------------------------------------------------------------------------------- 1 | name: deploy_to_cocoapods 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: macOS-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v1 14 | 15 | - name: Install Cocoapods 16 | run: gem install cocoapods 17 | 18 | - name: Deploy to Cocoapods 19 | run: | 20 | set -eo pipefail 21 | export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) 22 | pod lib lint --allow-warnings 23 | pod trunk push --allow-warnings 24 | env: 25 | COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 3 | 4 | ## User settings 5 | xcuserdata/ 6 | 7 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 8 | *.xcscmblueprint 9 | *.xccheckout 10 | *.xcuserstate 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | #### 26 | # Xcode temporary files that should never be committed 27 | # 28 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this... 29 | 30 | *~.nib 31 | 32 | ## Obj-C/Swift specific 33 | *.hmap 34 | 35 | ## App packaging 36 | *.ipa 37 | *.dSYM.zip 38 | *.dSYM 39 | *.zip 40 | 41 | ## Playgrounds 42 | timeline.xctimeline 43 | playground.xcworkspace 44 | 45 | # Swift Package Manager 46 | # 47 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 48 | # Packages/ 49 | # Package.pins 50 | # Package.resolved 51 | # *.xcodeproj 52 | # 53 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 54 | # hence it is not needed unless you have added a package configuration file to your project 55 | # .swiftpm 56 | 57 | .build/ 58 | 59 | # CocoaPods 60 | # 61 | # We recommend against adding the Pods directory to your .gitignore. However 62 | # you should judge for yourself, the pros and cons are mentioned at: 63 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 64 | # 65 | # Pods/ 66 | # 67 | # Add this line if you want to avoid checking in source code from the Xcode workspace 68 | # *.xcworkspace 69 | 70 | # Carthage 71 | # 72 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 73 | # Carthage/Checkouts 74 | 75 | Carthage/Build/ 76 | 77 | # Accio dependency management 78 | Dependencies/ 79 | .accio/ 80 | 81 | # fastlane 82 | # 83 | # It is recommended to not store the screenshots in the git repo. 84 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 85 | # For more information about the recommended setup visit: 86 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 87 | 88 | fastlane/report.xml 89 | fastlane/Preview.html 90 | fastlane/screenshots/**/*.png 91 | fastlane/test_output 92 | 93 | # Code Injection 94 | # 95 | # After new code Injection tools there's a generated folder /iOSInjectionProject 96 | # https://github.com/johnno1962/injectionforxcode 97 | 98 | iOSInjectionProject/ 99 | 100 | # OSX Temp 101 | .DS_Store 102 | .AppleDouble 103 | .LSOverride 104 | 105 | # OSX Thumbnails 106 | ._* 107 | 108 | # OSX Directories potentially created on remote AFP share 109 | .AppleDB 110 | .AppleDesktop 111 | Network Trash Folder 112 | Temporary Items 113 | .apdisk 114 | 115 | # Linux trash folder which might appear on any partition or disk 116 | .Trash-* 117 | 118 | # vim 119 | *.swp 120 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at hello@52inc.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute to Pulley 2 | :tada::heart: First off, thanks for taking the time to contribute or considering to contribute! :heart::tada: 3 | 4 | The following is a set of gidelines for contributing to the Pulley project. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. Also feel free to purpose changes to the new [issue templates](https://github.com/52inc/Pulley/tree/master/.github/ISSUE_TEMPLATE) or the new [pull request template](https://github.com/52inc/Pulley/blob/master/.github/PULL_REQUEST_TEMPLATE.md) in a pull request as well. 5 | 6 | ## Code of Conduct 7 | 8 | This project and everyone participating in it is governed by the [Pulley Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [hello@52inc.com](mailto:hello@52inc.com). 9 | 10 | ## Your First Contribution 11 | 12 | Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). 13 | 14 | ## Reporting Bugs 15 | 16 | This section guides you through submitting a bug report for Pulley. 17 | 18 | Before creating bug reports, please check the open issues as you might find out that you don't need to create a new one. When you are creating a bug report, please include as many details as possible. Fill out [the required template](https://github.com/52inc/Pulley/tree/master/.github/ISSUE_TEMPLATE/pulley-bug-report.md), the information it asks for helps us resolve issues faster. 19 | 20 | > **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. 21 | 22 | ## Suggesting Features 23 | 24 | This section guides you through submitting a feature suggestion for Pulley, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions. 25 | 26 | Before creating feature suggestions, please check the open issues with the enhancement label as you might find out that you don't need to create one. When you are creating a feature suggestion, please include as many details as possible and fill in [the template](https://github.com/52inc/Pulley/blob/master/.github/ISSUE_TEMPLATE/pulley-feature-request.md). 27 | 28 | 29 | ## Submitting Code 30 | 31 | This section guides you through submitting a pull request for Pulley. The goal being to imitate the behavior of the Apple Maps drawer, maintain backwards compatability, and maintain support for all iOS device size classes. 32 | 33 | Any code change should be submitted as a pull request. The description should explain what the code does, give steps to execute it, and breifly explan what steps were taken to test your code. Please follow the [pull request template](https://github.com/52inc/Pulley/blob/master/.github/PULL_REQUEST_TEMPLATE.md) as best you can and provide as much detail as possiable. 34 | 35 | ## Code Review Process 36 | 37 | We will do our best to review your request as soon as we can, but please keep in mind we are also full time developers. The bigger the pull request and the more parts of the application it can potentially effect, the longer it will take for us review and merge. Try to test as much as you can and think about how your pull request effects previous versions of iOS or the existing Pulley API before you submit it to us for review. It is also always helpful to have as much context as possiable for your pull request. 38 | 39 | ## Questions 40 | 41 | If you have any questions, create a new issue using the [question issue template](https://github.com/52inc/Pulley/tree/master/.github/ISSUE_TEMPLATE/pulley-question.md). Please do a quick search first to see if someone else didn't ask the same question before you. 42 | You can also reach us at [hello@52inc.com](mailto:hello@52inc.com). 43 | 44 | ## Credits 45 | 46 | ### Contributors 47 | 48 | Thank you to all the people who have already contributed to Pulley! I hope you all continue to contribute because you all make the open source community a better place by contributing! 49 | 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Brendan Lee 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.0 2 | // 3 | // Package.swift 4 | // 5 | // Copyright (c) 2016 Brendan Lee 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | 26 | import PackageDescription 27 | 28 | let package = Package( 29 | name: "Pulley", 30 | platforms: [.iOS(.v9)], 31 | products: [ 32 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 33 | .library( 34 | name: "Pulley", 35 | targets: ["Pulley"]), 36 | ], 37 | dependencies: [ 38 | // Dependencies declare other packages that this package depends on. 39 | // .package(url: /* package url */, from: "1.0.0"), 40 | ], 41 | targets: [ 42 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 43 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 44 | .target(name: "Pulley", 45 | path: "PulleyLib") 46 | ] 47 | ) 48 | -------------------------------------------------------------------------------- /Pulley.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint Pulley.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'Pulley' 11 | s.version = ENV['LIB_VERSION'] || '2.9.0' 12 | s.summary = 'A library to imitate the iOS 10 Maps UI.' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | A library to provide a drawer controller that can imitate the drawer UI in iOS 10's Maps app. 22 | DESC 23 | 24 | s.homepage = 'https://github.com/52inc/Pulley' 25 | s.screenshots = 'https://camo.githubusercontent.com/ffa1a97f8e152bb25f69970a08aa2e827fe54ce0/687474703a2f2f692e696d6775722e636f6d2f35616b456a69542e676966' 26 | s.license = { :type => 'MIT', :file => 'LICENSE' } 27 | s.author = { 'Brendan Lee' => 'brendan@52inc.com' } 28 | s.source = { :git => 'https://github.com/52inc/Pulley.git', :tag => s.version.to_s } 29 | s.social_media_url = 'https://twitter.com/52_inc' 30 | 31 | s.ios.deployment_target = '9.0' 32 | s.swift_version = '5.0' 33 | 34 | s.source_files = 'PulleyLib/*.{h,swift}' 35 | 36 | # s.public_header_files = 'Pod/Classes/**/*.h' 37 | s.frameworks = 'UIKit' 38 | 39 | end 40 | -------------------------------------------------------------------------------- /Pulley.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 355DBF171E40EA4300671CDD /* Pulley.h in Headers */ = {isa = PBXBuildFile; fileRef = 355DBF151E40EA4300671CDD /* Pulley.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 355DBF1A1E40EA4300671CDD /* Pulley.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 355DBF131E40EA4300671CDD /* Pulley.framework */; }; 12 | 355DBF1B1E40EA4300671CDD /* Pulley.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 355DBF131E40EA4300671CDD /* Pulley.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 13 | 355DBF201E40EA5C00671CDD /* PulleyPassthroughScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6131BB71D305995002F27F3 /* PulleyPassthroughScrollView.swift */; }; 14 | 355DBF211E40EA5F00671CDD /* PulleyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6131BB81D305995002F27F3 /* PulleyViewController.swift */; }; 15 | 853FCF2622F9F98F006829F2 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 853FCF2422F9F98D006829F2 /* MapKit.framework */; }; 16 | C6131BB41D30429E002F27F3 /* PrimaryTransitionTargetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6131BB31D30429E002F27F3 /* PrimaryTransitionTargetViewController.swift */; }; 17 | C6C51DDD1F5108980079B57F /* UIView+constrainToParent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C51DDC1F5108980079B57F /* UIView+constrainToParent.swift */; }; 18 | C6DF73D31D2DE2B500735EBC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DF73D21D2DE2B500735EBC /* AppDelegate.swift */; }; 19 | C6DF73D51D2DE2B500735EBC /* PrimaryContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DF73D41D2DE2B500735EBC /* PrimaryContentViewController.swift */; }; 20 | C6DF73DA1D2DE2B500735EBC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C6DF73D91D2DE2B500735EBC /* Assets.xcassets */; }; 21 | C6DF73DD1D2DE2B500735EBC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C6DF73DB1D2DE2B500735EBC /* LaunchScreen.storyboard */; }; 22 | C6DF73EA1D2DFE7F00735EBC /* DrawerContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DF73E91D2DFE7F00735EBC /* DrawerContentViewController.swift */; }; 23 | C6DF73EC1D2E027500735EBC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C6DF73EB1D2E027500735EBC /* Main.storyboard */; }; 24 | D2C07C7C2129BC8B00ED38D7 /* CustomMaskExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C07C7A2129BC0F00ED38D7 /* CustomMaskExample.swift */; }; 25 | E15E853120BE56B900800173 /* UIViewController+PulleyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15E853020BE56B900800173 /* UIViewController+PulleyViewController.swift */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 355DBF181E40EA4300671CDD /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = C6DF73C71D2DE2B500735EBC /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 355DBF121E40EA4300671CDD; 34 | remoteInfo = Pulley; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXCopyFilesBuildPhase section */ 39 | 355DBF1F1E40EA4300671CDD /* Embed Frameworks */ = { 40 | isa = PBXCopyFilesBuildPhase; 41 | buildActionMask = 2147483647; 42 | dstPath = ""; 43 | dstSubfolderSpec = 10; 44 | files = ( 45 | 355DBF1B1E40EA4300671CDD /* Pulley.framework in Embed Frameworks */, 46 | ); 47 | name = "Embed Frameworks"; 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXCopyFilesBuildPhase section */ 51 | 52 | /* Begin PBXFileReference section */ 53 | 355DBF131E40EA4300671CDD /* Pulley.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pulley.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 355DBF151E40EA4300671CDD /* Pulley.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Pulley.h; sourceTree = ""; }; 55 | 355DBF161E40EA4300671CDD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 853FCF2422F9F98D006829F2 /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; 57 | C6131BB31D30429E002F27F3 /* PrimaryTransitionTargetViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimaryTransitionTargetViewController.swift; sourceTree = ""; }; 58 | C6131BB71D305995002F27F3 /* PulleyPassthroughScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PulleyPassthroughScrollView.swift; path = PulleyLib/PulleyPassthroughScrollView.swift; sourceTree = SOURCE_ROOT; }; 59 | C6131BB81D305995002F27F3 /* PulleyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PulleyViewController.swift; path = PulleyLib/PulleyViewController.swift; sourceTree = SOURCE_ROOT; }; 60 | C6C51DDC1F5108980079B57F /* UIView+constrainToParent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "UIView+constrainToParent.swift"; path = "PulleyLib/UIView+constrainToParent.swift"; sourceTree = SOURCE_ROOT; }; 61 | C6DF73CF1D2DE2B500735EBC /* PulleyDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PulleyDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | C6DF73D21D2DE2B500735EBC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 63 | C6DF73D41D2DE2B500735EBC /* PrimaryContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryContentViewController.swift; sourceTree = ""; }; 64 | C6DF73D91D2DE2B500735EBC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 65 | C6DF73DC1D2DE2B500735EBC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 66 | C6DF73DE1D2DE2B500735EBC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | C6DF73E91D2DFE7F00735EBC /* DrawerContentViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawerContentViewController.swift; sourceTree = ""; }; 68 | C6DF73EB1D2E027500735EBC /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 69 | D2C07C7A2129BC0F00ED38D7 /* CustomMaskExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomMaskExample.swift; sourceTree = ""; }; 70 | E15E853020BE56B900800173 /* UIViewController+PulleyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIViewController+PulleyViewController.swift"; path = "PulleyLib/UIViewController+PulleyViewController.swift"; sourceTree = SOURCE_ROOT; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFrameworksBuildPhase section */ 74 | 355DBF0F1E40EA4300671CDD /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | C6DF73CC1D2DE2B500735EBC /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 2147483647; 84 | files = ( 85 | 853FCF2622F9F98F006829F2 /* MapKit.framework in Frameworks */, 86 | 355DBF1A1E40EA4300671CDD /* Pulley.framework in Frameworks */, 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | /* End PBXFrameworksBuildPhase section */ 91 | 92 | /* Begin PBXGroup section */ 93 | 355DBF141E40EA4300671CDD /* Pulley */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 355DBF151E40EA4300671CDD /* Pulley.h */, 97 | E15E853020BE56B900800173 /* UIViewController+PulleyViewController.swift */, 98 | C6131BB71D305995002F27F3 /* PulleyPassthroughScrollView.swift */, 99 | C6131BB81D305995002F27F3 /* PulleyViewController.swift */, 100 | C6C51DDC1F5108980079B57F /* UIView+constrainToParent.swift */, 101 | 355DBF161E40EA4300671CDD /* Info.plist */, 102 | ); 103 | path = Pulley; 104 | sourceTree = ""; 105 | }; 106 | 853FCF2322F9F98D006829F2 /* Frameworks */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 853FCF2422F9F98D006829F2 /* MapKit.framework */, 110 | ); 111 | name = Frameworks; 112 | sourceTree = ""; 113 | }; 114 | C6131BB21D303FE0002F27F3 /* Supporting Files */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | C6DF73D91D2DE2B500735EBC /* Assets.xcassets */, 118 | C6DF73DE1D2DE2B500735EBC /* Info.plist */, 119 | C6DF73DB1D2DE2B500735EBC /* LaunchScreen.storyboard */, 120 | ); 121 | name = "Supporting Files"; 122 | sourceTree = ""; 123 | }; 124 | C6DF73C61D2DE2B500735EBC = { 125 | isa = PBXGroup; 126 | children = ( 127 | C6DF73D11D2DE2B500735EBC /* Demo App */, 128 | 355DBF141E40EA4300671CDD /* Pulley */, 129 | C6DF73D01D2DE2B500735EBC /* Products */, 130 | 853FCF2322F9F98D006829F2 /* Frameworks */, 131 | ); 132 | sourceTree = ""; 133 | }; 134 | C6DF73D01D2DE2B500735EBC /* Products */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | C6DF73CF1D2DE2B500735EBC /* PulleyDemo.app */, 138 | 355DBF131E40EA4300671CDD /* Pulley.framework */, 139 | ); 140 | name = Products; 141 | sourceTree = ""; 142 | }; 143 | C6DF73D11D2DE2B500735EBC /* Demo App */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | C6DF73EB1D2E027500735EBC /* Main.storyboard */, 147 | C6DF73D21D2DE2B500735EBC /* AppDelegate.swift */, 148 | C6DF73D41D2DE2B500735EBC /* PrimaryContentViewController.swift */, 149 | C6DF73E91D2DFE7F00735EBC /* DrawerContentViewController.swift */, 150 | C6131BB31D30429E002F27F3 /* PrimaryTransitionTargetViewController.swift */, 151 | D2C07C7A2129BC0F00ED38D7 /* CustomMaskExample.swift */, 152 | C6131BB21D303FE0002F27F3 /* Supporting Files */, 153 | ); 154 | name = "Demo App"; 155 | path = Pulley; 156 | sourceTree = ""; 157 | }; 158 | /* End PBXGroup section */ 159 | 160 | /* Begin PBXHeadersBuildPhase section */ 161 | 355DBF101E40EA4300671CDD /* Headers */ = { 162 | isa = PBXHeadersBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | 355DBF171E40EA4300671CDD /* Pulley.h in Headers */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXHeadersBuildPhase section */ 170 | 171 | /* Begin PBXNativeTarget section */ 172 | 355DBF121E40EA4300671CDD /* Pulley */ = { 173 | isa = PBXNativeTarget; 174 | buildConfigurationList = 355DBF1C1E40EA4300671CDD /* Build configuration list for PBXNativeTarget "Pulley" */; 175 | buildPhases = ( 176 | 355DBF0E1E40EA4300671CDD /* Sources */, 177 | 355DBF0F1E40EA4300671CDD /* Frameworks */, 178 | 355DBF101E40EA4300671CDD /* Headers */, 179 | 355DBF111E40EA4300671CDD /* Resources */, 180 | ); 181 | buildRules = ( 182 | ); 183 | dependencies = ( 184 | ); 185 | name = Pulley; 186 | productName = Pulley; 187 | productReference = 355DBF131E40EA4300671CDD /* Pulley.framework */; 188 | productType = "com.apple.product-type.framework"; 189 | }; 190 | C6DF73CE1D2DE2B500735EBC /* PulleyDemo */ = { 191 | isa = PBXNativeTarget; 192 | buildConfigurationList = C6DF73E11D2DE2B500735EBC /* Build configuration list for PBXNativeTarget "PulleyDemo" */; 193 | buildPhases = ( 194 | C6DF73CB1D2DE2B500735EBC /* Sources */, 195 | C6DF73CC1D2DE2B500735EBC /* Frameworks */, 196 | C6DF73CD1D2DE2B500735EBC /* Resources */, 197 | 355DBF1F1E40EA4300671CDD /* Embed Frameworks */, 198 | ); 199 | buildRules = ( 200 | ); 201 | dependencies = ( 202 | 355DBF191E40EA4300671CDD /* PBXTargetDependency */, 203 | ); 204 | name = PulleyDemo; 205 | productName = Pulley; 206 | productReference = C6DF73CF1D2DE2B500735EBC /* PulleyDemo.app */; 207 | productType = "com.apple.product-type.application"; 208 | }; 209 | /* End PBXNativeTarget section */ 210 | 211 | /* Begin PBXProject section */ 212 | C6DF73C71D2DE2B500735EBC /* Project object */ = { 213 | isa = PBXProject; 214 | attributes = { 215 | LastSwiftUpdateCheck = 0730; 216 | LastUpgradeCheck = 1200; 217 | ORGANIZATIONNAME = 52inc; 218 | TargetAttributes = { 219 | 355DBF121E40EA4300671CDD = { 220 | CreatedOnToolsVersion = 8.2.1; 221 | DevelopmentTeam = P87N88ZC7B; 222 | LastSwiftMigration = 1020; 223 | ProvisioningStyle = Automatic; 224 | }; 225 | C6DF73CE1D2DE2B500735EBC = { 226 | CreatedOnToolsVersion = 7.3.1; 227 | DevelopmentTeam = P87N88ZC7B; 228 | LastSwiftMigration = 1020; 229 | }; 230 | }; 231 | }; 232 | buildConfigurationList = C6DF73CA1D2DE2B500735EBC /* Build configuration list for PBXProject "Pulley" */; 233 | compatibilityVersion = "Xcode 3.2"; 234 | developmentRegion = en; 235 | hasScannedForEncodings = 0; 236 | knownRegions = ( 237 | en, 238 | Base, 239 | ); 240 | mainGroup = C6DF73C61D2DE2B500735EBC; 241 | productRefGroup = C6DF73D01D2DE2B500735EBC /* Products */; 242 | projectDirPath = ""; 243 | projectRoot = ""; 244 | targets = ( 245 | 355DBF121E40EA4300671CDD /* Pulley */, 246 | C6DF73CE1D2DE2B500735EBC /* PulleyDemo */, 247 | ); 248 | }; 249 | /* End PBXProject section */ 250 | 251 | /* Begin PBXResourcesBuildPhase section */ 252 | 355DBF111E40EA4300671CDD /* Resources */ = { 253 | isa = PBXResourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | ); 257 | runOnlyForDeploymentPostprocessing = 0; 258 | }; 259 | C6DF73CD1D2DE2B500735EBC /* Resources */ = { 260 | isa = PBXResourcesBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | C6DF73EC1D2E027500735EBC /* Main.storyboard in Resources */, 264 | C6DF73DD1D2DE2B500735EBC /* LaunchScreen.storyboard in Resources */, 265 | C6DF73DA1D2DE2B500735EBC /* Assets.xcassets in Resources */, 266 | ); 267 | runOnlyForDeploymentPostprocessing = 0; 268 | }; 269 | /* End PBXResourcesBuildPhase section */ 270 | 271 | /* Begin PBXSourcesBuildPhase section */ 272 | 355DBF0E1E40EA4300671CDD /* Sources */ = { 273 | isa = PBXSourcesBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | E15E853120BE56B900800173 /* UIViewController+PulleyViewController.swift in Sources */, 277 | 355DBF201E40EA5C00671CDD /* PulleyPassthroughScrollView.swift in Sources */, 278 | 355DBF211E40EA5F00671CDD /* PulleyViewController.swift in Sources */, 279 | C6C51DDD1F5108980079B57F /* UIView+constrainToParent.swift in Sources */, 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | }; 283 | C6DF73CB1D2DE2B500735EBC /* Sources */ = { 284 | isa = PBXSourcesBuildPhase; 285 | buildActionMask = 2147483647; 286 | files = ( 287 | C6131BB41D30429E002F27F3 /* PrimaryTransitionTargetViewController.swift in Sources */, 288 | D2C07C7C2129BC8B00ED38D7 /* CustomMaskExample.swift in Sources */, 289 | C6DF73D51D2DE2B500735EBC /* PrimaryContentViewController.swift in Sources */, 290 | C6DF73EA1D2DFE7F00735EBC /* DrawerContentViewController.swift in Sources */, 291 | C6DF73D31D2DE2B500735EBC /* AppDelegate.swift in Sources */, 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | /* End PBXSourcesBuildPhase section */ 296 | 297 | /* Begin PBXTargetDependency section */ 298 | 355DBF191E40EA4300671CDD /* PBXTargetDependency */ = { 299 | isa = PBXTargetDependency; 300 | target = 355DBF121E40EA4300671CDD /* Pulley */; 301 | targetProxy = 355DBF181E40EA4300671CDD /* PBXContainerItemProxy */; 302 | }; 303 | /* End PBXTargetDependency section */ 304 | 305 | /* Begin PBXVariantGroup section */ 306 | C6DF73DB1D2DE2B500735EBC /* LaunchScreen.storyboard */ = { 307 | isa = PBXVariantGroup; 308 | children = ( 309 | C6DF73DC1D2DE2B500735EBC /* Base */, 310 | ); 311 | name = LaunchScreen.storyboard; 312 | sourceTree = ""; 313 | }; 314 | /* End PBXVariantGroup section */ 315 | 316 | /* Begin XCBuildConfiguration section */ 317 | 355DBF1D1E40EA4300671CDD /* Debug */ = { 318 | isa = XCBuildConfiguration; 319 | buildSettings = { 320 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 321 | CLANG_WARN_INFINITE_RECURSION = YES; 322 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 323 | CODE_SIGN_IDENTITY = ""; 324 | CURRENT_PROJECT_VERSION = 1; 325 | DEFINES_MODULE = YES; 326 | DEVELOPMENT_TEAM = P87N88ZC7B; 327 | DYLIB_COMPATIBILITY_VERSION = 1; 328 | DYLIB_CURRENT_VERSION = 1; 329 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 330 | INFOPLIST_FILE = Pulley/Info.plist; 331 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 332 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 333 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 334 | PRODUCT_BUNDLE_IDENTIFIER = com.52inc.Pulley.Pulley; 335 | PRODUCT_NAME = "$(TARGET_NAME)"; 336 | SKIP_INSTALL = YES; 337 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 338 | SWIFT_VERSION = 5.0; 339 | VERSIONING_SYSTEM = "apple-generic"; 340 | VERSION_INFO_PREFIX = ""; 341 | }; 342 | name = Debug; 343 | }; 344 | 355DBF1E1E40EA4300671CDD /* Release */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 348 | CLANG_WARN_INFINITE_RECURSION = YES; 349 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 350 | CODE_SIGN_IDENTITY = ""; 351 | CURRENT_PROJECT_VERSION = 1; 352 | DEFINES_MODULE = YES; 353 | DEVELOPMENT_TEAM = P87N88ZC7B; 354 | DYLIB_COMPATIBILITY_VERSION = 1; 355 | DYLIB_CURRENT_VERSION = 1; 356 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 357 | INFOPLIST_FILE = Pulley/Info.plist; 358 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 359 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 360 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 361 | PRODUCT_BUNDLE_IDENTIFIER = com.52inc.Pulley.Pulley; 362 | PRODUCT_NAME = "$(TARGET_NAME)"; 363 | SKIP_INSTALL = YES; 364 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 365 | SWIFT_VERSION = 5.0; 366 | VERSIONING_SYSTEM = "apple-generic"; 367 | VERSION_INFO_PREFIX = ""; 368 | }; 369 | name = Release; 370 | }; 371 | C6DF73DF1D2DE2B500735EBC /* Debug */ = { 372 | isa = XCBuildConfiguration; 373 | buildSettings = { 374 | ALWAYS_SEARCH_USER_PATHS = NO; 375 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 376 | CLANG_ANALYZER_NONNULL = YES; 377 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 378 | CLANG_CXX_LIBRARY = "libc++"; 379 | CLANG_ENABLE_MODULES = YES; 380 | CLANG_ENABLE_OBJC_ARC = YES; 381 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 382 | CLANG_WARN_BOOL_CONVERSION = YES; 383 | CLANG_WARN_COMMA = YES; 384 | CLANG_WARN_CONSTANT_CONVERSION = YES; 385 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 386 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 387 | CLANG_WARN_EMPTY_BODY = YES; 388 | CLANG_WARN_ENUM_CONVERSION = YES; 389 | CLANG_WARN_INFINITE_RECURSION = YES; 390 | CLANG_WARN_INT_CONVERSION = YES; 391 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 392 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 393 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 394 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 395 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 396 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 397 | CLANG_WARN_STRICT_PROTOTYPES = YES; 398 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 399 | CLANG_WARN_UNREACHABLE_CODE = YES; 400 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 401 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 402 | COPY_PHASE_STRIP = NO; 403 | DEBUG_INFORMATION_FORMAT = dwarf; 404 | ENABLE_STRICT_OBJC_MSGSEND = YES; 405 | ENABLE_TESTABILITY = YES; 406 | GCC_C_LANGUAGE_STANDARD = gnu99; 407 | GCC_DYNAMIC_NO_PIC = NO; 408 | GCC_NO_COMMON_BLOCKS = YES; 409 | GCC_OPTIMIZATION_LEVEL = 0; 410 | GCC_PREPROCESSOR_DEFINITIONS = ( 411 | "DEBUG=1", 412 | "$(inherited)", 413 | ); 414 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 415 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 416 | GCC_WARN_UNDECLARED_SELECTOR = YES; 417 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 418 | GCC_WARN_UNUSED_FUNCTION = YES; 419 | GCC_WARN_UNUSED_VARIABLE = YES; 420 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 421 | MTL_ENABLE_DEBUG_INFO = YES; 422 | ONLY_ACTIVE_ARCH = YES; 423 | SDKROOT = iphoneos; 424 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 425 | TARGETED_DEVICE_FAMILY = "1,2"; 426 | }; 427 | name = Debug; 428 | }; 429 | C6DF73E01D2DE2B500735EBC /* Release */ = { 430 | isa = XCBuildConfiguration; 431 | buildSettings = { 432 | ALWAYS_SEARCH_USER_PATHS = NO; 433 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 434 | CLANG_ANALYZER_NONNULL = YES; 435 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 436 | CLANG_CXX_LIBRARY = "libc++"; 437 | CLANG_ENABLE_MODULES = YES; 438 | CLANG_ENABLE_OBJC_ARC = YES; 439 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 440 | CLANG_WARN_BOOL_CONVERSION = YES; 441 | CLANG_WARN_COMMA = YES; 442 | CLANG_WARN_CONSTANT_CONVERSION = YES; 443 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 444 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 445 | CLANG_WARN_EMPTY_BODY = YES; 446 | CLANG_WARN_ENUM_CONVERSION = YES; 447 | CLANG_WARN_INFINITE_RECURSION = YES; 448 | CLANG_WARN_INT_CONVERSION = YES; 449 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 450 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 451 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 452 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 453 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 454 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 455 | CLANG_WARN_STRICT_PROTOTYPES = YES; 456 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 457 | CLANG_WARN_UNREACHABLE_CODE = YES; 458 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 459 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 460 | COPY_PHASE_STRIP = NO; 461 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 462 | ENABLE_NS_ASSERTIONS = NO; 463 | ENABLE_STRICT_OBJC_MSGSEND = YES; 464 | GCC_C_LANGUAGE_STANDARD = gnu99; 465 | GCC_NO_COMMON_BLOCKS = YES; 466 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 467 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 468 | GCC_WARN_UNDECLARED_SELECTOR = YES; 469 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 470 | GCC_WARN_UNUSED_FUNCTION = YES; 471 | GCC_WARN_UNUSED_VARIABLE = YES; 472 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 473 | MTL_ENABLE_DEBUG_INFO = NO; 474 | SDKROOT = iphoneos; 475 | TARGETED_DEVICE_FAMILY = "1,2"; 476 | VALIDATE_PRODUCT = YES; 477 | }; 478 | name = Release; 479 | }; 480 | C6DF73E21D2DE2B500735EBC /* Debug */ = { 481 | isa = XCBuildConfiguration; 482 | buildSettings = { 483 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 484 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 485 | CURRENT_PROJECT_VERSION = 1; 486 | DEVELOPMENT_TEAM = P87N88ZC7B; 487 | INFOPLIST_FILE = Pulley/Info.plist; 488 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 489 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 490 | PRODUCT_BUNDLE_IDENTIFIER = com.52inc.Pulley; 491 | PRODUCT_NAME = PulleyDemo; 492 | SWIFT_VERSION = 5.0; 493 | }; 494 | name = Debug; 495 | }; 496 | C6DF73E31D2DE2B500735EBC /* Release */ = { 497 | isa = XCBuildConfiguration; 498 | buildSettings = { 499 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 500 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 501 | CURRENT_PROJECT_VERSION = 1; 502 | DEVELOPMENT_TEAM = P87N88ZC7B; 503 | INFOPLIST_FILE = Pulley/Info.plist; 504 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 505 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 506 | PRODUCT_BUNDLE_IDENTIFIER = com.52inc.Pulley; 507 | PRODUCT_NAME = PulleyDemo; 508 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 509 | SWIFT_VERSION = 5.0; 510 | }; 511 | name = Release; 512 | }; 513 | /* End XCBuildConfiguration section */ 514 | 515 | /* Begin XCConfigurationList section */ 516 | 355DBF1C1E40EA4300671CDD /* Build configuration list for PBXNativeTarget "Pulley" */ = { 517 | isa = XCConfigurationList; 518 | buildConfigurations = ( 519 | 355DBF1D1E40EA4300671CDD /* Debug */, 520 | 355DBF1E1E40EA4300671CDD /* Release */, 521 | ); 522 | defaultConfigurationIsVisible = 0; 523 | defaultConfigurationName = Release; 524 | }; 525 | C6DF73CA1D2DE2B500735EBC /* Build configuration list for PBXProject "Pulley" */ = { 526 | isa = XCConfigurationList; 527 | buildConfigurations = ( 528 | C6DF73DF1D2DE2B500735EBC /* Debug */, 529 | C6DF73E01D2DE2B500735EBC /* Release */, 530 | ); 531 | defaultConfigurationIsVisible = 0; 532 | defaultConfigurationName = Release; 533 | }; 534 | C6DF73E11D2DE2B500735EBC /* Build configuration list for PBXNativeTarget "PulleyDemo" */ = { 535 | isa = XCConfigurationList; 536 | buildConfigurations = ( 537 | C6DF73E21D2DE2B500735EBC /* Debug */, 538 | C6DF73E31D2DE2B500735EBC /* Release */, 539 | ); 540 | defaultConfigurationIsVisible = 0; 541 | defaultConfigurationName = Release; 542 | }; 543 | /* End XCConfigurationList section */ 544 | }; 545 | rootObject = C6DF73C71D2DE2B500735EBC /* Project object */; 546 | } 547 | -------------------------------------------------------------------------------- /Pulley.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Pulley.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Pulley.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Pulley.xcodeproj/xcshareddata/xcschemes/Pulley.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Pulley.xcodeproj/xcshareddata/xcschemes/PulleyDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /Pulley/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Pulley 4 | // 5 | // Created by Brendan Lee on 7/6/16. 6 | // Copyright © 2016 52inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Pulley 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool 18 | { 19 | // Override point for customization after application launch. 20 | 21 | window = UIWindow(frame: UIScreen.main.bounds) 22 | 23 | // To create from a Storyboard 24 | window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()! 25 | 26 | // To create in code (uncomment this block) 27 | /* 28 | let mainContentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PrimaryContentViewController") 29 | let drawerContentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DrawerContentViewController") 30 | let pulleyDrawerVC = PulleyViewController(contentViewController: mainContentVC, drawerViewController: drawerContentVC) 31 | 32 | // Uncomment this next line to give the drawer a starting position, in this case: closed. 33 | // pulleyDrawerVC.initialDrawerPosition = .closed 34 | 35 | window?.rootViewController = pulleyDrawerVC 36 | */ 37 | 38 | window?.makeKeyAndVisible() 39 | 40 | return true 41 | } 42 | 43 | func applicationWillResignActive(_ application: UIApplication) { 44 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 45 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 46 | } 47 | 48 | func applicationDidEnterBackground(_ application: UIApplication) { 49 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 50 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 51 | } 52 | 53 | func applicationWillEnterForeground(_ application: UIApplication) { 54 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 55 | } 56 | 57 | func applicationDidBecomeActive(_ application: UIApplication) { 58 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 59 | } 60 | 61 | func applicationWillTerminate(_ application: UIApplication) { 62 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /Pulley/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Pulley/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Pulley/CustomMaskExample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomMaskExample.swift 3 | // Pulley 4 | // 5 | // Created by Connor Power on 19.08.18. 6 | // Copyright © 2018 52inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct CustomMaskExample { 12 | 13 | // MARK: - Constants 14 | 15 | private struct Constants { 16 | static let cornerRadius: CGFloat = 8.0 17 | static let cutoutDistanceFromEdge: CGFloat = 32.0 18 | static let cutoutRadius: CGFloat = 8.0 19 | } 20 | 21 | // MARK: - Functions 22 | 23 | func customMask(for bounds: CGRect) -> UIBezierPath { 24 | let maxX = bounds.maxX 25 | let maxY = bounds.maxY 26 | 27 | let path = UIBezierPath() 28 | path.move(to: CGPoint(x: 0, y: maxY)) 29 | 30 | // Left hand edge 31 | path.addLine(to: CGPoint(x: 0, y: Constants.cornerRadius)) 32 | 33 | // Top left rounded corner 34 | path.addArc(withCenter: CGPoint(x: Constants.cornerRadius, y: Constants.cornerRadius), 35 | radius: Constants.cornerRadius, 36 | startAngle: CGFloat.pi, 37 | endAngle: 1.5 * CGFloat.pi, 38 | clockwise: true) 39 | 40 | // Top edge left cutout section 41 | path.addLine(to: CGPoint(x: Constants.cutoutDistanceFromEdge - Constants.cutoutRadius, y: 0)) 42 | path.addArc(withCenter: CGPoint(x: Constants.cutoutDistanceFromEdge, y: 0), 43 | radius: Constants.cutoutRadius, 44 | startAngle: CGFloat.pi, 45 | endAngle: 2.0 * CGFloat.pi, 46 | clockwise: false) 47 | path.addLine(to: CGPoint(x: Constants.cutoutDistanceFromEdge + Constants.cutoutRadius, y: 0)) 48 | 49 | // Top edge right cutout section 50 | path.addLine(to: CGPoint(x: maxX - Constants.cutoutDistanceFromEdge - Constants.cutoutRadius, y: 0)) 51 | path.addArc(withCenter: CGPoint(x: maxX - Constants.cutoutDistanceFromEdge, y: 0), 52 | radius: Constants.cutoutRadius, 53 | startAngle: CGFloat.pi, 54 | endAngle: 2.0 * CGFloat.pi, 55 | clockwise: false) 56 | path.addLine(to: CGPoint(x: maxX - Constants.cutoutDistanceFromEdge + Constants.cutoutRadius, y: 0)) 57 | path.addLine(to: CGPoint(x: maxX - Constants.cornerRadius, y: 0)) 58 | 59 | // Top right rounded corner 60 | path.addArc(withCenter: CGPoint(x: maxX - Constants.cornerRadius, y: Constants.cornerRadius), 61 | radius: Constants.cornerRadius, 62 | startAngle: 1.5 * CGFloat.pi, 63 | endAngle: 2.0 * CGFloat.pi, 64 | clockwise: true) 65 | 66 | // Right hand edge 67 | path.addLine(to: CGPoint(x: maxX, y: maxY)) 68 | 69 | // Bottom edge 70 | path.addLine(to: CGPoint(x: 0, y: maxY)) 71 | path.close() 72 | path.fill() 73 | 74 | return path 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /Pulley/DrawerContentViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DrawerPreviewContentViewController.swift 3 | // Pulley 4 | // 5 | // Created by Brendan Lee on 7/6/16. 6 | // Copyright © 2016 52inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Pulley 11 | 12 | class DrawerContentViewController: UIViewController { 13 | 14 | // Pulley can apply a custom mask to the panel drawer. This variable toggles an example. 15 | private var shouldDisplayCustomMaskExample = false 16 | 17 | @IBOutlet var tableView: UITableView! 18 | @IBOutlet var searchBar: UISearchBar! 19 | @IBOutlet var gripperView: UIView! 20 | @IBOutlet var topSeparatorView: UIView! 21 | @IBOutlet var bottomSeperatorView: UIView! 22 | 23 | @IBOutlet var gripperTopConstraint: NSLayoutConstraint! 24 | 25 | // We adjust our 'header' based on the bottom safe area using this constraint 26 | @IBOutlet var headerSectionHeightConstraint: NSLayoutConstraint! 27 | 28 | fileprivate var drawerBottomSafeArea: CGFloat = 0.0 { 29 | didSet { 30 | self.loadViewIfNeeded() 31 | 32 | // We'll configure our UI to respect the safe area. In our small demo app, we just want to adjust the contentInset for the tableview. 33 | tableView.contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: drawerBottomSafeArea, right: 0.0) 34 | } 35 | } 36 | 37 | override func viewDidLoad() { 38 | super.viewDidLoad() 39 | 40 | // Do any additional setup after loading the view. 41 | gripperView.layer.cornerRadius = 2.5 42 | } 43 | 44 | override func viewWillAppear(_ animated: Bool) { 45 | super.viewWillAppear(animated) 46 | 47 | // You must wait until viewWillAppear -or- later in the view controller lifecycle in order to get a reference to Pulley via self.parent for customization. 48 | 49 | // UIFeedbackGenerator is only available iOS 10+. Since Pulley works back to iOS 9, the .feedbackGenerator property is "Any" and managed internally as a feedback generator. 50 | if #available(iOS 10.0, *) 51 | { 52 | let feedbackGenerator = UISelectionFeedbackGenerator() 53 | self.pulleyViewController?.feedbackGenerator = feedbackGenerator 54 | } 55 | } 56 | 57 | override func viewWillLayoutSubviews() { 58 | super.viewWillLayoutSubviews() 59 | 60 | if shouldDisplayCustomMaskExample { 61 | let maskLayer = CAShapeLayer() 62 | maskLayer.path = CustomMaskExample().customMask(for: view.bounds).cgPath 63 | view.layer.mask = maskLayer 64 | } 65 | } 66 | 67 | override func viewDidAppear(_ animated: Bool) { 68 | super.viewDidAppear(animated) 69 | 70 | // The bounce here is optional, but it's done automatically after appearance as a demonstration. 71 | Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(bounceDrawer), userInfo: nil, repeats: false) 72 | } 73 | 74 | @objc fileprivate func bounceDrawer() { 75 | 76 | // We can 'bounce' the drawer to show users that the drawer needs their attention. There are optional parameters you can pass this method to control the bounce height and speed. 77 | self.pulleyViewController?.bounceDrawer() 78 | } 79 | } 80 | 81 | extension DrawerContentViewController: PulleyDrawerViewControllerDelegate { 82 | 83 | func collapsedDrawerHeight(bottomSafeArea: CGFloat) -> CGFloat 84 | { 85 | // For devices with a bottom safe area, we want to make our drawer taller. Your implementation may not want to do that. In that case, disregard the bottomSafeArea value. 86 | return 68.0 + (pulleyViewController?.currentDisplayMode == .drawer ? bottomSafeArea : 0.0) 87 | } 88 | 89 | func partialRevealDrawerHeight(bottomSafeArea: CGFloat) -> CGFloat 90 | { 91 | // For devices with a bottom safe area, we want to make our drawer taller. Your implementation may not want to do that. In that case, disregard the bottomSafeArea value. 92 | return 264.0 + (pulleyViewController?.currentDisplayMode == .drawer ? bottomSafeArea : 0.0) 93 | } 94 | 95 | func supportedDrawerPositions() -> [PulleyPosition] { 96 | return PulleyPosition.all // You can specify the drawer positions you support. This is the same as: [.open, .partiallyRevealed, .collapsed, .closed] 97 | } 98 | 99 | // This function is called by Pulley anytime the size, drawer position, etc. changes. It's best to customize your VC UI based on the bottomSafeArea here (if needed). Note: You might also find the `pulleySafeAreaInsets` property on Pulley useful to get Pulley's current safe area insets in a backwards compatible (with iOS < 11) way. If you need this information for use in your layout, you can also access it directly by using `drawerDistanceFromBottom` at any time. 100 | func drawerPositionDidChange(drawer: PulleyViewController, bottomSafeArea: CGFloat) 101 | { 102 | // We want to know about the safe area to customize our UI. Our UI customization logic is in the didSet for this variable. 103 | drawerBottomSafeArea = bottomSafeArea 104 | 105 | /* 106 | Some explanation for what is happening here: 107 | 1. Our drawer UI needs some customization to look 'correct' on devices like the iPhone X, with a bottom safe area inset. 108 | 2. We only need this when it's in the 'collapsed' position, so we'll add some safe area when it's collapsed and remove it when it's not. 109 | 3. These changes are captured in an animation block (when necessary) by Pulley, so these changes will be animated along-side the drawer automatically. 110 | */ 111 | if drawer.drawerPosition == .collapsed 112 | { 113 | headerSectionHeightConstraint.constant = 68.0 + drawerBottomSafeArea 114 | } 115 | else 116 | { 117 | headerSectionHeightConstraint.constant = 68.0 118 | } 119 | 120 | // Handle tableview scrolling / searchbar editing 121 | 122 | tableView.isScrollEnabled = drawer.drawerPosition == .open || drawer.currentDisplayMode == .panel 123 | 124 | if drawer.drawerPosition != .open 125 | { 126 | searchBar.resignFirstResponder() 127 | } 128 | 129 | if drawer.currentDisplayMode == .panel 130 | { 131 | topSeparatorView.isHidden = drawer.drawerPosition == .collapsed 132 | bottomSeperatorView.isHidden = drawer.drawerPosition == .collapsed 133 | } 134 | else 135 | { 136 | topSeparatorView.isHidden = false 137 | bottomSeperatorView.isHidden = true 138 | } 139 | } 140 | 141 | /// This function is called when the current drawer display mode changes. Make UI customizations here. 142 | func drawerDisplayModeDidChange(drawer: PulleyViewController) { 143 | 144 | print("Drawer: \(drawer.currentDisplayMode)") 145 | gripperTopConstraint.isActive = (drawer.currentDisplayMode == .drawer || drawer.currentDisplayMode == .compact) 146 | } 147 | } 148 | 149 | extension DrawerContentViewController: UISearchBarDelegate { 150 | 151 | func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { 152 | pulleyViewController?.setDrawerPosition(position: .open, animated: true) 153 | } 154 | } 155 | 156 | extension DrawerContentViewController: UITableViewDataSource { 157 | 158 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 159 | return 50 160 | } 161 | 162 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 163 | return tableView.dequeueReusableCell(withIdentifier: "SampleCell", for: indexPath) 164 | } 165 | } 166 | 167 | extension DrawerContentViewController: UITableViewDelegate { 168 | 169 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 170 | return 81.0 171 | } 172 | 173 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 174 | tableView.deselectRow(at: indexPath, animated: true) 175 | 176 | let primaryContent = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PrimaryTransitionTargetViewController") 177 | 178 | pulleyViewController?.setDrawerPosition(position: .collapsed, animated: true) 179 | 180 | pulleyViewController?.setPrimaryContentViewController(controller: primaryContent, animated: false) 181 | } 182 | } 183 | 184 | 185 | -------------------------------------------------------------------------------- /Pulley/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 2.2 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UISupportedInterfaceOrientations 26 | 27 | UIInterfaceOrientationPortrait 28 | UIInterfaceOrientationLandscapeRight 29 | UIInterfaceOrientationLandscapeLeft 30 | UIInterfaceOrientationPortraitUpsideDown 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Pulley/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 87 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 236 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 369 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | -------------------------------------------------------------------------------- /Pulley/PrimaryContentViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Pulley 4 | // 5 | // Created by Brendan Lee on 7/6/16. 6 | // Copyright © 2016 52inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MapKit 11 | import Pulley 12 | 13 | class PrimaryContentViewController: UIViewController { 14 | 15 | @IBOutlet var mapView: MKMapView! 16 | @IBOutlet var controlsContainer: UIView! 17 | @IBOutlet var temperatureLabel: UILabel! 18 | 19 | /** 20 | * IMPORTANT! If you have constraints that you use to 'follow' the drawer (like the temperature label in the demo)... 21 | * Make sure you constraint them to the bottom of the superview and NOT the superview's bottom margin. Double click the constraint, and you can change it in the dropdown in the right-side panel. If you don't, you'll have varying spacings to the drawer depending on the device. 22 | */ 23 | @IBOutlet var temperatureLabelBottomConstraint: NSLayoutConstraint! 24 | 25 | fileprivate let temperatureLabelBottomDistance: CGFloat = 8.0 26 | 27 | override func viewDidLoad() { 28 | super.viewDidLoad() 29 | // Do any additional setup after loading the view, typically from a nib. 30 | 31 | controlsContainer.layer.cornerRadius = 10.0 32 | temperatureLabel.layer.cornerRadius = 7.0 33 | } 34 | 35 | override func viewWillAppear(_ animated: Bool) { 36 | super.viewWillAppear(animated) 37 | 38 | // Customize Pulley in viewWillAppear, as the view controller's viewDidLoad will run *before* Pulley's and some changes may be overwritten. 39 | // Uncomment if you want to change the visual effect style to dark. Note: The rest of the sample app's UI isn't made for dark theme. This just shows you how to do it. 40 | // drawer.drawerBackgroundVisualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) 41 | 42 | // We want the 'side panel' layout in landscape iPhone / iPad, so we set this to 'automatic'. The default is 'bottomDrawer' for compatibility with older Pulley versions. 43 | self.pulleyViewController?.displayMode = .automatic 44 | } 45 | 46 | @IBAction func runPrimaryContentTransitionWithoutAnimation(sender: AnyObject) { 47 | let primaryContent = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PrimaryTransitionTargetViewController") 48 | 49 | self.pulleyViewController?.setPrimaryContentViewController(controller: primaryContent, animated: false) 50 | } 51 | 52 | @IBAction func runPrimaryContentTransition(sender: AnyObject) { 53 | let primaryContent = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PrimaryTransitionTargetViewController") 54 | 55 | self.pulleyViewController?.setPrimaryContentViewController(controller: primaryContent, animated: true) 56 | } 57 | } 58 | 59 | extension PrimaryContentViewController: PulleyPrimaryContentControllerDelegate { 60 | 61 | func makeUIAdjustmentsForFullscreen(progress: CGFloat, bottomSafeArea: CGFloat) 62 | { 63 | guard let drawer = self.pulleyViewController, drawer.currentDisplayMode == .drawer else { 64 | controlsContainer.alpha = 1.0 65 | return 66 | } 67 | 68 | controlsContainer.alpha = 1.0 - progress 69 | } 70 | 71 | func drawerChangedDistanceFromBottom(drawer: PulleyViewController, distance: CGFloat, bottomSafeArea: CGFloat) 72 | { 73 | // As of iOS 14, setting the constant on the constraint causes viewDidLayoutSubviews() to be call 74 | // on the PulleyViewController. This was causing issues with the drawer scroll view in 2.8.2+, see fix 75 | // for issue #400 and update 2.8.5 76 | guard drawer.currentDisplayMode == .drawer else { 77 | 78 | temperatureLabelBottomConstraint.constant = temperatureLabelBottomDistance 79 | return 80 | } 81 | 82 | if distance <= 268.0 + bottomSafeArea 83 | { 84 | temperatureLabelBottomConstraint.constant = distance + temperatureLabelBottomDistance 85 | } 86 | else 87 | { 88 | temperatureLabelBottomConstraint.constant = 268.0 + temperatureLabelBottomDistance 89 | } 90 | } 91 | } 92 | 93 | -------------------------------------------------------------------------------- /Pulley/PrimaryTransitionTargetViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrimaryTransitionTargetViewController.swift 3 | // Pulley 4 | // 5 | // Created by Brendan Lee on 7/8/16. 6 | // Copyright © 2016 52inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Pulley 11 | 12 | class PrimaryTransitionTargetViewController: UIViewController { 13 | 14 | @IBAction func goBackButtonPressed(sender: AnyObject) { 15 | // Uncomment the bellow code to create a secondary drawer content view controller 16 | // and set it's initial position with setDrawerContentViewController(controller, position, animated, completion) 17 | /* 18 | let drawerContent = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondaryDrawerContentViewController") 19 | 20 | self.pulleyViewController?.setDrawerContentViewController(controller: drawerContent, position: .open, animated: true, completion: nil) 21 | */ 22 | 23 | let primaryContent = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PrimaryContentViewController") 24 | 25 | self.pulleyViewController?.setPrimaryContentViewController(controller: primaryContent, animated: true) 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Pulley/Pulley.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | //! Project version number for Pulley. 4 | FOUNDATION_EXPORT double PulleyVersionNumber; 5 | 6 | //! Project version string for Pulley. 7 | FOUNDATION_EXPORT const unsigned char PulleyVersionString[]; 8 | 9 | // In this header, you should import all the public headers of your framework using statements like #import 10 | 11 | 12 | -------------------------------------------------------------------------------- /PulleyLib/PulleyPassthroughScrollView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PulleyPassthroughScrollView.swift 3 | // Pulley 4 | // 5 | // Created by Brendan Lee on 7/6/16. 6 | // Copyright © 2016 52inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol PulleyPassthroughScrollViewDelegate: AnyObject { 12 | 13 | func shouldTouchPassthroughScrollView(scrollView: PulleyPassthroughScrollView, point: CGPoint) -> Bool 14 | func viewToReceiveTouch(scrollView: PulleyPassthroughScrollView, point: CGPoint) -> UIView 15 | } 16 | 17 | class PulleyPassthroughScrollView: UIScrollView { 18 | 19 | weak var touchDelegate: PulleyPassthroughScrollViewDelegate? 20 | 21 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 22 | 23 | if 24 | let touchDelegate = touchDelegate, 25 | touchDelegate.shouldTouchPassthroughScrollView(scrollView: self, point: point) 26 | { 27 | return touchDelegate.viewToReceiveTouch(scrollView: self, point: point).hitTest(touchDelegate.viewToReceiveTouch(scrollView: self, point: point).convert(point, from: self), with: event) 28 | } 29 | 30 | return super.hitTest(point, with: event) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /PulleyLib/UIView+constrainToParent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+constrainToParent.swift 3 | // Pulley 4 | // 5 | // Created by Mathew Polzin on 8/22/17. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | 12 | func constrainToParent() { 13 | constrainToParent(insets: .zero) 14 | } 15 | 16 | func constrainToParent(insets: UIEdgeInsets) { 17 | guard let parent = superview else { return } 18 | 19 | translatesAutoresizingMaskIntoConstraints = false 20 | let metrics: [String : Any] = ["left" : insets.left, "right" : insets.right, "top" : insets.top, "bottom" : insets.bottom] 21 | 22 | parent.addConstraints(["H:|-(left)-[view]-(right)-|", "V:|-(top)-[view]-(bottom)-|"].flatMap { 23 | NSLayoutConstraint.constraints(withVisualFormat: $0, metrics: metrics, views: ["view": self]) 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /PulleyLib/UIViewController+PulleyViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+PulleyViewController.swift 3 | // Pulley 4 | // 5 | // Created by Guilherme Souza on 4/28/18. 6 | // Copyright © 2018 52inc. All rights reserved. 7 | // 8 | import UIKit 9 | 10 | public extension UIViewController { 11 | 12 | /// If this viewController pertences to a PulleyViewController, return it. 13 | var pulleyViewController: PulleyViewController? { 14 | var parentVC = parent 15 | while parentVC != nil { 16 | if let pulleyViewController = parentVC as? PulleyViewController { 17 | return pulleyViewController 18 | } 19 | parentVC = parentVC?.parent 20 | } 21 | return nil 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pulley 2 | 3 |

4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 |

12 | 13 | A library to imitate the drawer in Maps for iOS 10/11. The master branch follows the latest currently released version of Swift. If you need an older version of Swift, you can specify it's version (e.g. 1.0.x) in your Podfile or use the code on the branch for that version. Older branches are unsupported. 14 | 15 | ### Update / Migration Info 16 | 17 | **ATTENTION:** 18 | Pulley 2.9.0 has new properties to support a new displayMode. The base functionality should work without any significant changes. The biggest change being the new displayMode of `.compact` to replicate Apple Maps Behavior on the iPhone SE size class devices. This is an exact replica of the behavior of the Apple Maps drawer, therefor when the `currentDisplayMode` of the `PulleyViewController` is `.compact` then the only `supportedDrawerPositions` for the view controller when in `.compact` mode are `.open`, `.closed`, and `.collapsed`. This mode also has new @IBInspectable properties, `compactInsets` and `compactWidth`. This mode behaves in a very similar way to `.panel` mode. See the pull request [here](https://github.com/52inc/Pulley/pull/347) for the motivation behind this feature. Also in this release, `setDrawerContentViewController(controller: UIViewController, position: PulleyPosition? = nil, animated: Bool = true, completion: PulleyAnimationCompletionBlock?)` has a new optional parameter `position` to set a new drawer position the drawer when a new `DrawerContentViewController` is set. See [this](https://github.com/52inc/Pulley/pull/349) pull request for the motivation behind this feature. 19 | 20 | 21 | Pulley 2.5.0 had significant renaming changes to support new features. Although property names have changed, the functionality should work without any significant changes (aside from renaming). See [this thread](https://github.com/52inc/Pulley/issues/252) for additional information. 22 | 23 | 24 | Pulley 2.4.0 changed PulleyPosition from an enum to a class. This won't affect most uses, but may affect your switch statements. Continue to use the static PulleyPosition values as usual and add a default case. This was done to allow marking some `PulleyDrawerViewControllerDelegate` methods as optional so they don't need to be implemented if you aren't using certain positions (or wish to use the default values). If you have questions, please open an issue. 25 | 26 | _Technical reason: Optional protocol methods require the @objc attribute. Arrays of Swift enums can't be exposed to Objective-C, and supportedDrawerPositions previously returned an array of PulleyPosition enums. This change allows for marking the protocol @objc so methods can be marked optional._ 27 | 28 | ### Introduction 29 | Pulley is an easy to use drawer library meant to imitate the drawer in iOS 10/11's Maps app. It exposes a simple API that allows you to use any UIViewController subclass as the drawer content or the primary content. 30 | 31 | **Here's a preview (apologies for the potato gif):** 32 | 33 | ![Pulley Preview](http://i.imgur.com/bmEWqy7.gif) 34 | 35 | ![Pulley iPad Preview](https://i.imgur.com/HwsdMSO.png) 36 | 37 | ### Installation 38 | 39 | ##### Installation with Cocoapods 40 | `pod 'Pulley'` 41 | 42 | ##### Installation with Carthage 43 | `github "52inc/Pulley"` 44 | Please read this [issue](https://github.com/52inc/Pulley/issues/331#issue-435421067) regarding setup if using Carthage. 45 | 46 | ##### Installation with Swift Package Manager 47 | Follow the [developer documentation](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app) for Swift Package Manager (versions 2.8.x) 48 | 49 | ##### Manual Installation 50 | Simply copy the files in the PulleyLib folder into your project. 51 | 52 | ### How To use 53 | 54 | #### Interface Builder 55 | 56 | Pulley supports loading embedded view controllers from Interface Builder. In order to use Pulley with Interface Builder, you'll need to setup your `PulleyViewController` like this: 57 | 58 | 1. Add 2 container views to the `PulleyViewController` view. One for the drawer content and one for the primary (background) content. 59 | 2. Connect the container view for the primary (background) content to the outlet named **primaryContentContainerView**. 60 | 3. Connect the container view for the drawer content to the outlet named **drawerContentContainerView**. 61 | 4. Create an 'embed' segue between each container view and the view controller you want to display for that part of the drawer. 62 | 5. Make sure you set the Module for the view controller to 'Pulley'. [See this issue.](https://github.com/52inc/Pulley/issues/29) 63 | 64 | If you would like to customize the height of the "Collapsed" or "Partially Revealed" states of the drawer, have your Drawer Content view controller implement `PulleyDrawerViewControllerDelegate`. You can provide the height for your drawer content for both the Collapsed and Partially Revealed states. 65 | 66 | ![Interface Builder Screenshot](http://i.imgur.com/htzo50L.png=500x) 67 | 68 | 69 | #### Programmatically 70 | 71 | Pulley supports loading view controllers programmatically. In order to use Pulley programmatically, please consider the following code snippet: 72 | 73 | ```swift 74 | let mainContentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("PrimaryContentViewController") 75 | 76 | let drawerContentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("DrawerContentViewController") 77 | 78 | let pulleyController = PulleyViewController(contentViewController: mainContentVC, drawerViewController: drawerContentVC) 79 | ``` 80 | 81 | ### API 82 | 83 | **Important:** The background of the internal drawer view is clear. If your view controller's view is also clear then you'll see the shadow rendering below where the view is. I'd recommend giving your view a color or using a UIVisualEffectView to make sure you don't see the shadow. You can set the shadow opacity to 0.0 if you want the shadow to be hidden. 84 | 85 | **Important:** Drawer Content views are made **20pt too long** in order to account for the bounce animation. Make sure your drawer content view is aware that the bottom 20pts will be offscreen. 86 | 87 | **Important:** PulleyViewController is not accessible as a parent or as `self.pulleyViewController` until _during or after_ -viewWillAppear: if you're loading Pulley from Storyboards. 88 | 89 | #### iOS 11, Safe Areas, and the iPhone X 90 | Pulley has support for safe areas and the iPhone X. The sample project includes full support for this, and does a couple of UI tricks to make things look better. These are documented throughout the sample project. 91 | 92 | The basic concepts of using Pulley post-iOS 11 are: 93 | 94 | 1. The -topInset property is _from_ the top safe area, not the top of the screen. 95 | 2. Most delegate methods have a new parameter that tells you the current bottom safe area. 96 | 3. The drawer itself doesn't do anything special for the bottom safe area because everyone's UI will want to treat it a little differently. HOWEVER: The delegate methods have been updated to deliver you the current bottom safe area anytime that a value for a drawer position is requested from you. You can use this variable to compute the value you want to return for the drawer position. Checkout the sample project for a simple example on an easy approach to this. 97 | 4. If you have UI bottom safe area customizations that you want to perform, I recommend using the delegate method `drawerPositionDidChange(drawer:bottomSafeArea:)` to modify your UI based on the value of bottomSafeArea. Any time the size of the Pulley view controller changes, this method will be called with a new bottom safe area height. The sample project uses this to modify the drawer 'header' height, as well as to adjust the contentInset for the UITableView. It's not automatically taken care of for you, but it should be a fairly simple thing to add. 98 | 5. I do _not_ recommend constraining views to the safe are of the drawer content view controller. It won't actually work for the safe areas. 99 | 6. If you want the map (or other UI) in the primary view controller to render under the status bar (or in the ears of the iPhone X), make sure you constrain it directly to the superview's 'top'. You may need to double click on the constraint, and then make sure it _isn't_ constrained 'relative to margin'. 100 | 7. For backwards compatibility, iOS 9/10 use topLayoutGuide as the top safe area. Your implementation shouldn't need to worry about iOS versions, as that's taken care of for you by Pulley. 101 | 102 | If you have any problems / questions while updating Pulley to iOS 11 SDK, please feel free to create an issue if the above information didn't solve your problem. 103 | 104 | Even if you've already seen the example project, I highly encourage looking at the new post-iOS 11 version of the sample project. It may have something that could help your iPhone X / safe area implementation. 105 | 106 | #### 3 protocols exist for you to use: 107 | 108 | * `PulleyDelegate`: The protocol the other protocols inherit from. It's exposed as the .delegate property of `PulleyViewController`. NOTE: If the object you're wanting to receive delegate callbacks is either the Primary Content or Drawer Content view controllers...don't use the .delegate property. Continue reading for the other protocols. 109 | * `PulleyDrawerViewControllerDelegate`: Includes all of the methods from `PulleyDelegate` and adds methods for providing custom heights for the Collapsed and Partially Revealed states. Your Drawer Content view controller should implement this protocol if it wants to receive callbacks for changes in the drawer state or to provide custom heights for the aforementioned drawer states. Implementing this protocol is optional for the Drawer Content view controller, but if you don't then defaults will be used instead. 110 | * `PulleyPrimaryContentControllerDelegate`: This is currently identical to `PulleyDelegate`. However, this protocol may be implemented by your Primary Content view controller if you want to receive callbacks for changes in drawer state. Eventually specialized methods may be added to this protocol. 111 | 112 | #### Changing view controllers after creation: 113 | 114 | You'll likely need to change out the contents of the drawer or the primary view controller after creation. Here's how to do that programmatically. 115 | 116 | **NOTE:** If you pass animated: true then you'll get a subtle crossfade animation. This doesn't work well with all views / view hierarchies (Notably UIVisualEffectView). You've been warned. 117 | 118 | **Changing the Primary Content View Controller:** 119 | 120 | ```swift 121 | if let drawer = self.parentViewController as? PulleyViewController 122 | { 123 | let primaryContent = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("PrimaryContentViewController") 124 | 125 | drawer.setPrimaryContentViewController(primaryContent, animated: true) 126 | } 127 | ``` 128 | 129 | **Changing the Drawer Content View Controller:** 130 | 131 | ```swift 132 | if let drawer = self.parentViewController as? PulleyViewController 133 | { 134 | let drawerContent = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("DrawerContentViewController") 135 | 136 | drawer.setDrawerContentViewController(drawerContent, animated: false) 137 | } 138 | ``` 139 | 140 | #### Customizing the drawer 141 | 142 | 1. See the 3 protocols above. 143 | 2. You can adjust the inset from the top of the screen in the "Open" state by setting the -topInset property on the `PulleyViewController`. 144 | 3. You can enable / disable drawer positions by implementing `PulleyDrawerViewControllerDelegate` in your 'drawer' view controller. If you need to change it, call `setNeedsSupportedDrawerPositionsUpdate()` on the `PulleyViewController` so it will recalculate the drawer based on your new settings. 145 | 4. You can adjust the corner radius applied to the drawer by setting the -drawerCornerRadius property on the `PulleyViewController`. 146 | 5. You can adjust the shadow opacity applied to the drawer by setting the -shadowOpacity property on the `PulleyViewController`. 147 | 6. You can adjust the shadow radius applied to the drawer by setting the -shadowRadius property on the `PulleyViewController`. 148 | 7. You can adjust the background dimming color by setting the -backgroundDimmingColor to an opaque color on the `PulleyViewController`. 149 | 8. You can adjust / remove the background blur effect by setting the -drawerBackgroundVisualEffectView property on the `PulleyViewController`. 150 | 9. You can adjust the alpha of the background dimming color by setting the -backgroundDimmingOpacity property on the `PulleyViewController`. 151 | 10. You can change the drawer position by calling setDrawerPosition( : ) on the `PulleyViewController`. 152 | 11. If an object needs to receive delegate callbacks and _isn't_ one of the view controller's presented then you can use the -delegate property on the `PulleyViewController`. 153 | 12. The Swift Interface for `PulleyViewController` is documented in case you want to see real documentation instead of a numbered list of useful things. 154 | 13. You can set the initial drawer position by using the initialDrawerPosition property on the `PulleyViewController`. 155 | 14. Most settings for the `PulleyViewController` are exposed in Interface Builder. Select the `PulleyViewController` View Controller (not the view) to access them via IBInspectable. 156 | 15. By default, Pulley will only use the 'bottom' display mode (to preserve backwards compatibility). If you want to use the iPad / iPhone landscape modes, you can use 'panel' for the display mode. If you want it to automatically switch like Maps.app on iOS, you can set the display mode to 'automatic'. 157 | 16. You can apply a custom mask to the Pulley drawer by setting your drawerViewController's view.layer.mask property to a CAShapeLayer. That mask will also be applied to the drawer in Pulley. 158 | 17. You can specify which corner you'd like the panel to display in (when in 'panel' displayMode) by using the 'panelCornerPlacement` property. 159 | 160 | ## Requirements 161 | 162 | - iOS 9.0+ 163 | - Swift 4.0+ 164 | 165 | 166 | --------------------------------------------------------------------------------