├── .gitignore ├── LICENSE ├── Logo.png ├── Package.swift ├── README.md ├── ResizableController.podspec ├── ResizableController.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ ├── ResizableController.xcscheme │ └── ResizableControllerUnitTests.xcscheme ├── ResizableController ├── 0.0.1 │ └── ResizableController.podspec ├── Info.plist ├── ResizableAnimatedController.swift ├── ResizableController.h ├── ResizableControllerObserver.swift ├── ResizableTransitioningController.swift └── UIView+Extension.swift ├── ResizableControllerSample ├── .gitignore ├── LICENSE ├── Podfile ├── README.md ├── ResizableControllerSample.xcodeproj │ └── project.pbxproj ├── ResizableControllerSample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── FixedHeightPresentedViewController.swift │ ├── Info.plist │ ├── PresentingViewController.swift │ ├── ResizablePresentedViewController.swift │ └── SceneDelegate.swift └── ResizableControllerSampleUITests │ ├── Info.plist │ └── ResizableControllerSampleUITests.swift ├── ResizableControllerUnitTests ├── Info.plist └── ResizableControllerUnitTests.swift └── Sources └── ResizableController ├── ResizableAnimatedController.swift ├── ResizableControllerObserver.swift ├── ResizableTransitioningController.swift └── UIView+Extension.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | 4 | ## Build generated 5 | build/ 6 | DerivedData/ 7 | 8 | ## Various settings 9 | *.pbxuser 10 | !default.pbxuser 11 | *.mode1v3 12 | !default.mode1v3 13 | *.mode2v3 14 | !default.mode2v3 15 | *.perspectivev3 16 | !default.perspectivev3 17 | xcuserdata 18 | xcuserdata/ 19 | 20 | ## Workspace file 21 | *.xcworkspace/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcuserstate 27 | *.xcscmblueprint 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | *.ipa 32 | *.dSYM.zip 33 | *.dSYM 34 | *.app.dSYM.zip 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Paytm Money 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paytmmoney/ResizableController/e598e1b0acceb864f82b86a9bd9594989c95deb9/Logo.png -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "ResizableController", 8 | platforms: [ 9 | .iOS(.v11) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries a package produces, and make them visible to other packages. 13 | .library( 14 | name: "ResizableController", 15 | targets: ["ResizableController"]), 16 | ], 17 | targets: [ 18 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 19 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 20 | .target( 21 | name: "ResizableController", 22 | dependencies: []), 23 | .testTarget( 24 | name: "ResizableControllerTests", 25 | dependencies: ["ResizableController"]), 26 | ] 27 | ) 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | ResizableController 3 |

4 | 5 |

6 | iOS 7 | 8 | 9 | Swift Package Manager 10 | 11 | 12 | Swift Package Manager 13 | 14 | 15 | Twitter: @PaytmMoney 16 | 17 |

18 | 19 | ## Overview 20 | 21 | **ResizableController** is the custom model presentation style written in swift. 22 | 23 | 24 | | Dark Mode | Light Mode | 25 | |:----------|:-----| 26 | |![BasicFlow](https://user-images.githubusercontent.com/74349937/99520326-fa993d00-29b8-11eb-85cb-b91abbbb81a7.gif)|![DismissFlow_light](https://user-images.githubusercontent.com/74349937/99521441-6def7e80-29ba-11eb-8caf-cd15ffced50e.gif)| 27 | |![OnTopOfModelPresentation](https://user-images.githubusercontent.com/74349937/99520487-2c120880-29b9-11eb-9208-540650c6ae1e.gif)|![OnModelPresentation](https://user-images.githubusercontent.com/74349937/99521540-8a8bb680-29ba-11eb-8c77-cae890a65f21.gif)| 28 | 29 | 30 | ## Features 31 | 32 | - [X] Resizable View Controller with **custom initial/final height.** 33 | - [X] Works on all screens and devices supporting **iOS 11.0+** 34 | - [X] Provides Backward compatibility of iOS 13's automatic presentation style to lower versions. 35 | - [X] 100% compatible with other presentation styles. 36 | - [X] Simple to Integrate, merely couple of lines of code. 37 | - [X] Slick transition animations. 38 | - [X] Dedicated Callbacks when view controller size changes. 39 | - [X] Controller can be dismissed via swipe down and background tap. 40 | - [X] Light and Dark mode compatible. 41 | 42 | ## Installation 43 | This version is Swift 5 compatible. 44 | 45 | ### CocoaPods 46 | 47 | ResizableController is available through [CocoaPods](http://cocoapods.org). To install 48 | it, simply add the following line to your Podfile: 49 | 50 | ```ruby 51 | pod 'ResizableController', '~> 1.0' 52 | ``` 53 | 54 | ### Swift Package Manager 55 | 56 | ResizableController is available through [Swift Package Manager](https://swift.org/package-manager/). To install 57 | it, add package dependency from Url: 58 | 59 | ```swift 60 | https://github.com/paytmmoney/ResizableController 61 | ``` 62 | 63 | ## Usage 64 | ### ResizableControllerPositionHandler 65 | View Controller to be presented, using resizable transition, needs to conform to protocol `ResizableControllerPositionHandler`. This protocol inherits UIViewController at implementattion level. 66 | 67 | ```swift 68 | public protocol ResizableControllerPositionHandler: UIViewController { 69 | var shouldShowSlideUpIndication: Bool { get } 70 | var sliderBackgroundColor: UIColor { get } 71 | 72 | var initialTopOffset: CGFloat { get } 73 | var finalTopOffset: CGFloat { get } 74 | 75 | func willMoveTopOffset(value: CGFloat) 76 | func didMoveTopOffset(value: CGFloat) 77 | } 78 | ``` 79 | All the above properties are optional and can be added to achive different result. 80 | 81 | ### shouldShowSlideUpIndication: 82 | 83 | Override this property if you do not want to include intuitive slide up indicator. Disabled by default for non-resizable views controllers. 84 | 85 | ### sliderBackgroundColor: 86 | 87 | Override this property to give differnent colour to Slider up indicator. Defaults to darkGrey with alpha 0.5 88 | 89 | ### initialTopOffset: 90 | 91 | Override this property to give initial custom height, calculated from top. 92 | 93 | Suggestion: Think about this offset as a topAnchor constraint we apply to a view. 94 | 95 | ### finalTopOffset: 96 | 97 | Override this property to give custom final height, calculated from top. Resizable controller will change its height from initialTopOffset to finalTopOffset. 98 | 99 | ### willMoveTopOffset: 100 | 101 | Override this property to add behaviours to view controller before it changes it size. 102 | If you override this property, make sure you dismiss view controller manually as default behaviur will be overridden. 103 | 104 | ### didMoveTopOffset: 105 | Similar to willMoveTopOffset, but will trigger when animation is complete and view controller is resized. 106 | 107 | 108 | ## Example 109 | 110 | ## Rezisable View Controller 111 | 112 | For creating a resizable controller. Conform to `ResizableControllerPositionHandler` and override `initialTopOffset`. 113 | 114 | ```swift 115 | import ResizableController 116 | 117 | class ResizablePresentedViewController: UIViewController { 118 | 119 | override func viewDidLoad() { 120 | super.viewDidLoad() 121 | } 122 | } 123 | 124 | extension ResizablePresentedViewController: ResizableControllerPositionHandler { 125 | // This height 500 is from topAnchor 126 | var initialTopOffset: CGFloat { 127 | 500 128 | } 129 | } 130 | ``` 131 | 132 | ## Fixed Height View Controller 133 | 134 | For creating a fixed height controller. Conform to `ResizableControllerPositionHandler` and override `initialTopOffset` & `finalTopOffset`. 135 | 136 | ```swift 137 | import ResizableController 138 | 139 | class FixedHeightPresentedViewController: UIViewController { 140 | 141 | override func viewDidLoad() { 142 | super.viewDidLoad() 143 | } 144 | } 145 | 146 | extension FixedHeightPresentedViewController: ResizableControllerPositionHandler { 147 | // This height 500 is from topAnchor 148 | var initialTopOffset: CGFloat { 149 | 500 150 | } 151 | 152 | // Both initial and final height should be same to make it a fixed height controller 153 | var finalTopOffset: CGFloat { 154 | 500 155 | } 156 | } 157 | ``` 158 | 159 | or: 160 | 161 | Do not override any property, this will give you iOS 13's automatic presentation, which can be used with lower iOS versions. 162 | 163 | ```swift 164 | import ResizableController 165 | 166 | class FixedHeightPresentedViewController: UIViewController { 167 | 168 | override func viewDidLoad() { 169 | super.viewDidLoad() 170 | } 171 | } 172 | 173 | extension ResizablePresentedViewController: ResizableControllerPositionHandler {} 174 | ``` 175 | 176 | ## How to modally Present 177 | 178 | To present, call this below overload method 179 | 180 | ```swift 181 | func present(_ viewControllerToPresent: ResizableControllerPositionHandler, 182 | animationDuration: TimeInterval = 0.3, 183 | completion: (() -> Void)? = nil) 184 | ``` 185 | Example: 186 | 187 | ```swift 188 | let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "ResizablePresentedViewController") as ResizablePresentedViewController 189 | self.present(viewController) 190 | ``` 191 | 192 | # Author 193 | 194 | 195 | **Arjun Baru**, iOS Engineer at Paytmoney Ltd, [paytmmoney.com](https://www.paytmmoney.com/)
196 | You can reach out to me at **arjun.baru@paytmmoney.com** 197 | 198 | In collaboration with: 199 | 200 | [Shahrukh Alam](https://github.com/shahrukh-alam) & [Rahul Mathur](https://github.com/PaytmMoneyOpenSource) 201 | 202 | 203 | # License 204 | 205 | ResizableController is available under the MIT license. See the LICENSE file for more info. 206 | 207 | ## Sample Project 208 | 209 | Here is the sample project implementing ResizableController [Sample Project](https://github.com/paytmmoney/ResizableController/tree/main) 210 | 211 | 212 | ## Suggestions or feedback? 213 | 214 | Feel free to create a pull request, open an issue or find [us on Twitter](https://twitter.com/PaytmMoney). 215 | 216 | -------------------------------------------------------------------------------- /ResizableController.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint ResizableController.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | spec.name = "ResizableController" 12 | spec.version = "1.0.0" 13 | spec.summary = "ResizableController for Custom Model Presentation" 14 | spec.description = "A Custom Presentation which acts as a custom-sizing view as well as full presented view. While doing this presentation, it manages to render background view which gives the view hierarchy, a nice 3D effect. Check screenshot for more details." 15 | 16 | spec.source_files = "ResizableController/**/*.{swift,h}" 17 | spec.homepage = "https://github.com/paytmmoney/ResizableController" 18 | spec.source = { :git => "https://github.com/paytmmoney/ResizableController.git", 19 | :tag => '1.0.0' } 20 | 21 | spec.license = { :type => "MIT", :file => "LICENSE" } 22 | 23 | spec.author = { "Arjun Baru" => "arjun.baru@paytmmoney.com" } 24 | 25 | spec.platform = :ios, "11.0" 26 | spec.swift_version = "5" 27 | end 28 | -------------------------------------------------------------------------------- /ResizableController.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 420FF919254155C2000FEDAD /* ResizableController.h in Headers */ = {isa = PBXBuildFile; fileRef = 420FF917254155C2000FEDAD /* ResizableController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 420FF921254157C4000FEDAD /* ResizableControllerObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420FF920254157C3000FEDAD /* ResizableControllerObserver.swift */; }; 12 | 420FF924254157DB000FEDAD /* ResizableTransitioningController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420FF923254157DB000FEDAD /* ResizableTransitioningController.swift */; }; 13 | 420FF927254157EC000FEDAD /* ResizableAnimatedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420FF926254157EC000FEDAD /* ResizableAnimatedController.swift */; }; 14 | 420FF92A25417524000FEDAD /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420FF92925417524000FEDAD /* UIView+Extension.swift */; }; 15 | 42EFACA7255AC2B3000CB6BC /* ResizableControllerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42EFACA6255AC2B3000CB6BC /* ResizableControllerUnitTests.swift */; }; 16 | 42EFACA9255AC2B3000CB6BC /* ResizableController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 420FF914254155C2000FEDAD /* ResizableController.framework */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | 42EFACAA255AC2B3000CB6BC /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = 420FF90B254155C2000FEDAD /* Project object */; 23 | proxyType = 1; 24 | remoteGlobalIDString = 420FF913254155C2000FEDAD; 25 | remoteInfo = ResizableController; 26 | }; 27 | /* End PBXContainerItemProxy section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 420FF914254155C2000FEDAD /* ResizableController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ResizableController.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | 420FF917254155C2000FEDAD /* ResizableController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ResizableController.h; sourceTree = ""; }; 32 | 420FF918254155C2000FEDAD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 420FF920254157C3000FEDAD /* ResizableControllerObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResizableControllerObserver.swift; sourceTree = ""; }; 34 | 420FF923254157DB000FEDAD /* ResizableTransitioningController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResizableTransitioningController.swift; sourceTree = ""; }; 35 | 420FF926254157EC000FEDAD /* ResizableAnimatedController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResizableAnimatedController.swift; sourceTree = ""; }; 36 | 420FF92925417524000FEDAD /* UIView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; }; 37 | 42EFACA4255AC2B3000CB6BC /* ResizableControllerUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ResizableControllerUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 42EFACA6255AC2B3000CB6BC /* ResizableControllerUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizableControllerUnitTests.swift; sourceTree = ""; }; 39 | 42EFACA8255AC2B3000CB6BC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | /* End PBXFileReference section */ 41 | 42 | /* Begin PBXFrameworksBuildPhase section */ 43 | 420FF911254155C2000FEDAD /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | 42EFACA1255AC2B3000CB6BC /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | 42EFACA9255AC2B3000CB6BC /* ResizableController.framework in Frameworks */, 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | 420FF90A254155C2000FEDAD = { 62 | isa = PBXGroup; 63 | children = ( 64 | 420FF916254155C2000FEDAD /* ResizableController */, 65 | 42EFACA5255AC2B3000CB6BC /* ResizableControllerUnitTests */, 66 | 420FF915254155C2000FEDAD /* Products */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | 420FF915254155C2000FEDAD /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | 420FF914254155C2000FEDAD /* ResizableController.framework */, 74 | 42EFACA4255AC2B3000CB6BC /* ResizableControllerUnitTests.xctest */, 75 | ); 76 | name = Products; 77 | sourceTree = ""; 78 | }; 79 | 420FF916254155C2000FEDAD /* ResizableController */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 420FF926254157EC000FEDAD /* ResizableAnimatedController.swift */, 83 | 420FF923254157DB000FEDAD /* ResizableTransitioningController.swift */, 84 | 420FF92925417524000FEDAD /* UIView+Extension.swift */, 85 | 420FF920254157C3000FEDAD /* ResizableControllerObserver.swift */, 86 | 420FF917254155C2000FEDAD /* ResizableController.h */, 87 | 420FF918254155C2000FEDAD /* Info.plist */, 88 | ); 89 | path = ResizableController; 90 | sourceTree = ""; 91 | }; 92 | 42EFACA5255AC2B3000CB6BC /* ResizableControllerUnitTests */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 42EFACA6255AC2B3000CB6BC /* ResizableControllerUnitTests.swift */, 96 | 42EFACA8255AC2B3000CB6BC /* Info.plist */, 97 | ); 98 | path = ResizableControllerUnitTests; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXHeadersBuildPhase section */ 104 | 420FF90F254155C2000FEDAD /* Headers */ = { 105 | isa = PBXHeadersBuildPhase; 106 | buildActionMask = 2147483647; 107 | files = ( 108 | 420FF919254155C2000FEDAD /* ResizableController.h in Headers */, 109 | ); 110 | runOnlyForDeploymentPostprocessing = 0; 111 | }; 112 | /* End PBXHeadersBuildPhase section */ 113 | 114 | /* Begin PBXNativeTarget section */ 115 | 420FF913254155C2000FEDAD /* ResizableController */ = { 116 | isa = PBXNativeTarget; 117 | buildConfigurationList = 420FF91C254155C2000FEDAD /* Build configuration list for PBXNativeTarget "ResizableController" */; 118 | buildPhases = ( 119 | 420FF90F254155C2000FEDAD /* Headers */, 120 | 420FF910254155C2000FEDAD /* Sources */, 121 | 420FF911254155C2000FEDAD /* Frameworks */, 122 | 420FF912254155C2000FEDAD /* Resources */, 123 | ); 124 | buildRules = ( 125 | ); 126 | dependencies = ( 127 | ); 128 | name = ResizableController; 129 | productName = ResizableController; 130 | productReference = 420FF914254155C2000FEDAD /* ResizableController.framework */; 131 | productType = "com.apple.product-type.framework"; 132 | }; 133 | 42EFACA3255AC2B3000CB6BC /* ResizableControllerUnitTests */ = { 134 | isa = PBXNativeTarget; 135 | buildConfigurationList = 42EFACAC255AC2B3000CB6BC /* Build configuration list for PBXNativeTarget "ResizableControllerUnitTests" */; 136 | buildPhases = ( 137 | 42EFACA0255AC2B3000CB6BC /* Sources */, 138 | 42EFACA1255AC2B3000CB6BC /* Frameworks */, 139 | 42EFACA2255AC2B3000CB6BC /* Resources */, 140 | ); 141 | buildRules = ( 142 | ); 143 | dependencies = ( 144 | 42EFACAB255AC2B3000CB6BC /* PBXTargetDependency */, 145 | ); 146 | name = ResizableControllerUnitTests; 147 | productName = ResizableControllerUnitTests; 148 | productReference = 42EFACA4255AC2B3000CB6BC /* ResizableControllerUnitTests.xctest */; 149 | productType = "com.apple.product-type.bundle.unit-test"; 150 | }; 151 | /* End PBXNativeTarget section */ 152 | 153 | /* Begin PBXProject section */ 154 | 420FF90B254155C2000FEDAD /* Project object */ = { 155 | isa = PBXProject; 156 | attributes = { 157 | LastSwiftUpdateCheck = 1200; 158 | LastUpgradeCheck = 1200; 159 | TargetAttributes = { 160 | 420FF913254155C2000FEDAD = { 161 | CreatedOnToolsVersion = 12.0.1; 162 | LastSwiftMigration = 1200; 163 | }; 164 | 42EFACA3255AC2B3000CB6BC = { 165 | CreatedOnToolsVersion = 12.0.1; 166 | }; 167 | }; 168 | }; 169 | buildConfigurationList = 420FF90E254155C2000FEDAD /* Build configuration list for PBXProject "ResizableController" */; 170 | compatibilityVersion = "Xcode 9.3"; 171 | developmentRegion = en; 172 | hasScannedForEncodings = 0; 173 | knownRegions = ( 174 | en, 175 | Base, 176 | ); 177 | mainGroup = 420FF90A254155C2000FEDAD; 178 | productRefGroup = 420FF915254155C2000FEDAD /* Products */; 179 | projectDirPath = ""; 180 | projectRoot = ""; 181 | targets = ( 182 | 420FF913254155C2000FEDAD /* ResizableController */, 183 | 42EFACA3255AC2B3000CB6BC /* ResizableControllerUnitTests */, 184 | ); 185 | }; 186 | /* End PBXProject section */ 187 | 188 | /* Begin PBXResourcesBuildPhase section */ 189 | 420FF912254155C2000FEDAD /* Resources */ = { 190 | isa = PBXResourcesBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | ); 194 | runOnlyForDeploymentPostprocessing = 0; 195 | }; 196 | 42EFACA2255AC2B3000CB6BC /* Resources */ = { 197 | isa = PBXResourcesBuildPhase; 198 | buildActionMask = 2147483647; 199 | files = ( 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | }; 203 | /* End PBXResourcesBuildPhase section */ 204 | 205 | /* Begin PBXSourcesBuildPhase section */ 206 | 420FF910254155C2000FEDAD /* Sources */ = { 207 | isa = PBXSourcesBuildPhase; 208 | buildActionMask = 2147483647; 209 | files = ( 210 | 420FF927254157EC000FEDAD /* ResizableAnimatedController.swift in Sources */, 211 | 420FF921254157C4000FEDAD /* ResizableControllerObserver.swift in Sources */, 212 | 420FF92A25417524000FEDAD /* UIView+Extension.swift in Sources */, 213 | 420FF924254157DB000FEDAD /* ResizableTransitioningController.swift in Sources */, 214 | ); 215 | runOnlyForDeploymentPostprocessing = 0; 216 | }; 217 | 42EFACA0255AC2B3000CB6BC /* Sources */ = { 218 | isa = PBXSourcesBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | 42EFACA7255AC2B3000CB6BC /* ResizableControllerUnitTests.swift in Sources */, 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | /* End PBXSourcesBuildPhase section */ 226 | 227 | /* Begin PBXTargetDependency section */ 228 | 42EFACAB255AC2B3000CB6BC /* PBXTargetDependency */ = { 229 | isa = PBXTargetDependency; 230 | target = 420FF913254155C2000FEDAD /* ResizableController */; 231 | targetProxy = 42EFACAA255AC2B3000CB6BC /* PBXContainerItemProxy */; 232 | }; 233 | /* End PBXTargetDependency section */ 234 | 235 | /* Begin XCBuildConfiguration section */ 236 | 420FF91A254155C2000FEDAD /* Debug */ = { 237 | isa = XCBuildConfiguration; 238 | buildSettings = { 239 | ALWAYS_SEARCH_USER_PATHS = NO; 240 | CLANG_ANALYZER_NONNULL = YES; 241 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 242 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 243 | CLANG_CXX_LIBRARY = "libc++"; 244 | CLANG_ENABLE_MODULES = YES; 245 | CLANG_ENABLE_OBJC_ARC = YES; 246 | CLANG_ENABLE_OBJC_WEAK = YES; 247 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 248 | CLANG_WARN_BOOL_CONVERSION = YES; 249 | CLANG_WARN_COMMA = YES; 250 | CLANG_WARN_CONSTANT_CONVERSION = YES; 251 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 252 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 253 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 254 | CLANG_WARN_EMPTY_BODY = YES; 255 | CLANG_WARN_ENUM_CONVERSION = YES; 256 | CLANG_WARN_INFINITE_RECURSION = YES; 257 | CLANG_WARN_INT_CONVERSION = YES; 258 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 259 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 260 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 261 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 262 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 263 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 264 | CLANG_WARN_STRICT_PROTOTYPES = YES; 265 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 266 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 267 | CLANG_WARN_UNREACHABLE_CODE = YES; 268 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 269 | COPY_PHASE_STRIP = NO; 270 | CURRENT_PROJECT_VERSION = 1; 271 | DEBUG_INFORMATION_FORMAT = dwarf; 272 | ENABLE_STRICT_OBJC_MSGSEND = YES; 273 | ENABLE_TESTABILITY = YES; 274 | GCC_C_LANGUAGE_STANDARD = gnu11; 275 | GCC_DYNAMIC_NO_PIC = NO; 276 | GCC_NO_COMMON_BLOCKS = YES; 277 | GCC_OPTIMIZATION_LEVEL = 0; 278 | GCC_PREPROCESSOR_DEFINITIONS = ( 279 | "DEBUG=1", 280 | "$(inherited)", 281 | ); 282 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 284 | GCC_WARN_UNDECLARED_SELECTOR = YES; 285 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 286 | GCC_WARN_UNUSED_FUNCTION = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 289 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 290 | MTL_FAST_MATH = YES; 291 | ONLY_ACTIVE_ARCH = YES; 292 | SDKROOT = iphoneos; 293 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 294 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 295 | VERSIONING_SYSTEM = "apple-generic"; 296 | VERSION_INFO_PREFIX = ""; 297 | }; 298 | name = Debug; 299 | }; 300 | 420FF91B254155C2000FEDAD /* Release */ = { 301 | isa = XCBuildConfiguration; 302 | buildSettings = { 303 | ALWAYS_SEARCH_USER_PATHS = NO; 304 | CLANG_ANALYZER_NONNULL = YES; 305 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 306 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 307 | CLANG_CXX_LIBRARY = "libc++"; 308 | CLANG_ENABLE_MODULES = YES; 309 | CLANG_ENABLE_OBJC_ARC = YES; 310 | CLANG_ENABLE_OBJC_WEAK = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 318 | CLANG_WARN_EMPTY_BODY = YES; 319 | CLANG_WARN_ENUM_CONVERSION = YES; 320 | CLANG_WARN_INFINITE_RECURSION = YES; 321 | CLANG_WARN_INT_CONVERSION = YES; 322 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 323 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 324 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 326 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 327 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 328 | CLANG_WARN_STRICT_PROTOTYPES = YES; 329 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 330 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | COPY_PHASE_STRIP = NO; 334 | CURRENT_PROJECT_VERSION = 1; 335 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 336 | ENABLE_NS_ASSERTIONS = NO; 337 | ENABLE_STRICT_OBJC_MSGSEND = YES; 338 | GCC_C_LANGUAGE_STANDARD = gnu11; 339 | GCC_NO_COMMON_BLOCKS = YES; 340 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 341 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 342 | GCC_WARN_UNDECLARED_SELECTOR = YES; 343 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 344 | GCC_WARN_UNUSED_FUNCTION = YES; 345 | GCC_WARN_UNUSED_VARIABLE = YES; 346 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 347 | MTL_ENABLE_DEBUG_INFO = NO; 348 | MTL_FAST_MATH = YES; 349 | SDKROOT = iphoneos; 350 | SWIFT_COMPILATION_MODE = wholemodule; 351 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 352 | VALIDATE_PRODUCT = YES; 353 | VERSIONING_SYSTEM = "apple-generic"; 354 | VERSION_INFO_PREFIX = ""; 355 | }; 356 | name = Release; 357 | }; 358 | 420FF91D254155C2000FEDAD /* Debug */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | CLANG_ENABLE_MODULES = YES; 362 | CODE_SIGN_STYLE = Automatic; 363 | DEFINES_MODULE = YES; 364 | DEVELOPMENT_TEAM = 885LR8YVNT; 365 | DYLIB_COMPATIBILITY_VERSION = 1; 366 | DYLIB_CURRENT_VERSION = 1; 367 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 368 | INFOPLIST_FILE = ResizableController/Info.plist; 369 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 370 | LD_RUNPATH_SEARCH_PATHS = ( 371 | "$(inherited)", 372 | "@executable_path/Frameworks", 373 | "@loader_path/Frameworks", 374 | ); 375 | MARKETING_VERSION = 1.0; 376 | PRODUCT_BUNDLE_IDENTIFIER = com.paytmmoney.ResizableController; 377 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 378 | SKIP_INSTALL = YES; 379 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 380 | SWIFT_VERSION = 5.0; 381 | TARGETED_DEVICE_FAMILY = "1,2"; 382 | }; 383 | name = Debug; 384 | }; 385 | 420FF91E254155C2000FEDAD /* Release */ = { 386 | isa = XCBuildConfiguration; 387 | buildSettings = { 388 | CLANG_ENABLE_MODULES = YES; 389 | CODE_SIGN_STYLE = Automatic; 390 | DEFINES_MODULE = YES; 391 | DEVELOPMENT_TEAM = 885LR8YVNT; 392 | DYLIB_COMPATIBILITY_VERSION = 1; 393 | DYLIB_CURRENT_VERSION = 1; 394 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 395 | INFOPLIST_FILE = ResizableController/Info.plist; 396 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 397 | LD_RUNPATH_SEARCH_PATHS = ( 398 | "$(inherited)", 399 | "@executable_path/Frameworks", 400 | "@loader_path/Frameworks", 401 | ); 402 | MARKETING_VERSION = 1.0; 403 | PRODUCT_BUNDLE_IDENTIFIER = com.paytmmoney.ResizableController; 404 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 405 | SKIP_INSTALL = YES; 406 | SWIFT_VERSION = 5.0; 407 | TARGETED_DEVICE_FAMILY = "1,2"; 408 | }; 409 | name = Release; 410 | }; 411 | 42EFACAD255AC2B3000CB6BC /* Debug */ = { 412 | isa = XCBuildConfiguration; 413 | buildSettings = { 414 | CODE_SIGN_STYLE = Automatic; 415 | DEVELOPMENT_TEAM = V83QP83683; 416 | INFOPLIST_FILE = ResizableControllerUnitTests/Info.plist; 417 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 418 | LD_RUNPATH_SEARCH_PATHS = ( 419 | "$(inherited)", 420 | "@executable_path/Frameworks", 421 | "@loader_path/Frameworks", 422 | ); 423 | PRODUCT_BUNDLE_IDENTIFIER = com.paytmmoney.opensource.ResizableControllerUnitTests; 424 | PRODUCT_NAME = "$(TARGET_NAME)"; 425 | SWIFT_VERSION = 5.0; 426 | TARGETED_DEVICE_FAMILY = "1,2"; 427 | }; 428 | name = Debug; 429 | }; 430 | 42EFACAE255AC2B3000CB6BC /* Release */ = { 431 | isa = XCBuildConfiguration; 432 | buildSettings = { 433 | CODE_SIGN_STYLE = Automatic; 434 | DEVELOPMENT_TEAM = V83QP83683; 435 | INFOPLIST_FILE = ResizableControllerUnitTests/Info.plist; 436 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 437 | LD_RUNPATH_SEARCH_PATHS = ( 438 | "$(inherited)", 439 | "@executable_path/Frameworks", 440 | "@loader_path/Frameworks", 441 | ); 442 | PRODUCT_BUNDLE_IDENTIFIER = com.paytmmoney.opensource.ResizableControllerUnitTests; 443 | PRODUCT_NAME = "$(TARGET_NAME)"; 444 | SWIFT_VERSION = 5.0; 445 | TARGETED_DEVICE_FAMILY = "1,2"; 446 | }; 447 | name = Release; 448 | }; 449 | /* End XCBuildConfiguration section */ 450 | 451 | /* Begin XCConfigurationList section */ 452 | 420FF90E254155C2000FEDAD /* Build configuration list for PBXProject "ResizableController" */ = { 453 | isa = XCConfigurationList; 454 | buildConfigurations = ( 455 | 420FF91A254155C2000FEDAD /* Debug */, 456 | 420FF91B254155C2000FEDAD /* Release */, 457 | ); 458 | defaultConfigurationIsVisible = 0; 459 | defaultConfigurationName = Release; 460 | }; 461 | 420FF91C254155C2000FEDAD /* Build configuration list for PBXNativeTarget "ResizableController" */ = { 462 | isa = XCConfigurationList; 463 | buildConfigurations = ( 464 | 420FF91D254155C2000FEDAD /* Debug */, 465 | 420FF91E254155C2000FEDAD /* Release */, 466 | ); 467 | defaultConfigurationIsVisible = 0; 468 | defaultConfigurationName = Release; 469 | }; 470 | 42EFACAC255AC2B3000CB6BC /* Build configuration list for PBXNativeTarget "ResizableControllerUnitTests" */ = { 471 | isa = XCConfigurationList; 472 | buildConfigurations = ( 473 | 42EFACAD255AC2B3000CB6BC /* Debug */, 474 | 42EFACAE255AC2B3000CB6BC /* Release */, 475 | ); 476 | defaultConfigurationIsVisible = 0; 477 | defaultConfigurationName = Release; 478 | }; 479 | /* End XCConfigurationList section */ 480 | }; 481 | rootObject = 420FF90B254155C2000FEDAD /* Project object */; 482 | } 483 | -------------------------------------------------------------------------------- /ResizableController.xcodeproj/xcshareddata/xcschemes/ResizableController.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 54 | 60 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /ResizableController.xcodeproj/xcshareddata/xcschemes/ResizableControllerUnitTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 54 | 60 | 61 | 63 | 64 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /ResizableController/0.0.1/ResizableController.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint ResizableController.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | spec.name = "ResizableController" 12 | spec.version = "0.0.1" 13 | spec.summary = "ResizableController for Custom Model Presentation" 14 | spec.description = <<-DESC 15 | A Custom Presentation which acts as a custom-sizing view as well as full presented view. While doing this presentation, it manages to render background view which gives the view hierarchy, a nice 3D effect. Check screenshot for more details. 16 | DESC 17 | 18 | spec.source_files = "ResizableController/**/*.{swift,h}" 19 | spec.homepage = "https://rupee.paytmmoney.com/ios/ResizableController" 20 | spec.source = { :git => "git@github.com:paytmmoney/ResizableController.git", 21 | :branch => 'dev/0.1' } 22 | 23 | spec.license = { :type => "MIT", :file => "LICENSE" } 24 | 25 | spec.author = { "Arjun Baru" => "arjun.baru@paytmmoney.com" } 26 | spec.social_media_url = "https://linkedin.com/in/arjun-baru-800785111" 27 | 28 | spec.platform = :ios, "11.0" 29 | spec.swift_version = "5" 30 | end 31 | -------------------------------------------------------------------------------- /ResizableController/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /ResizableController/ResizableAnimatedController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResizableAnimatedController.swift 3 | // 4 | // Created by Arjun Baru on 25/04/20. 5 | // Copyright © 2020 Paytm Money 🚀. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | /// Handles presentation cases for all type of presentation 11 | enum PresentingViewType { 12 | case custom, `default`, none 13 | } 14 | 15 | /// Manages scaling for presenting view controller 16 | enum ViewControlerScale { 17 | case backgroundPopUpScale 18 | case backgroundFullScreenScale 19 | case reset 20 | 21 | var transform: CATransform3D { 22 | switch self { 23 | case .backgroundPopUpScale: 24 | return CATransform3DMakeScale(0.92, 0.92, 1) 25 | case .backgroundFullScreenScale: 26 | return CATransform3DMakeScale(0.86, 0.86, 1) 27 | case .reset: 28 | return CATransform3DMakeScale(1, 1, 1) 29 | } 30 | } 31 | } 32 | 33 | /// Provides transitionContext for view controller's custom presentation 34 | class ResizableAnimatedController: NSObject { 35 | 36 | let initialTopOffset: CGFloat 37 | var estimatedFinalTopOffset: CGFloat 38 | let animationDuration: TimeInterval 39 | var isPresenting: Bool 40 | 41 | private var presntingViewControlerMinY: CGFloat? 42 | private weak var viewToBeDismissed: UIViewController? 43 | private let tapGesture = UITapGestureRecognizer() 44 | 45 | private lazy var dimmingView: UIView = { 46 | let view = UIView() 47 | view.isOpaque = false 48 | view.addGestureRecognizer(tapGesture) 49 | tapGesture.addTarget(self, action: #selector(onTapOfDimmingView)) 50 | view.backgroundColor = UIColor.black 51 | view.alpha = 0 52 | return view 53 | }() 54 | 55 | init?(initialTopOffset: CGFloat, 56 | animationDuration: TimeInterval, 57 | isPresenting: Bool, 58 | estimatedFinalTopOffset: CGFloat) { 59 | 60 | guard initialTopOffset >= ResizableConstants.maximumTopOffset else { return nil } 61 | 62 | self.animationDuration = animationDuration 63 | self.initialTopOffset = initialTopOffset 64 | self.isPresenting = isPresenting 65 | self.estimatedFinalTopOffset = estimatedFinalTopOffset 66 | } 67 | 68 | @objc func onTapOfDimmingView() { 69 | viewToBeDismissed?.dismiss(animated: true, completion: nil) 70 | } 71 | 72 | } 73 | 74 | // MARK: Transitioning Delegate Implementation 75 | extension ResizableAnimatedController: UIViewControllerAnimatedTransitioning { 76 | 77 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 78 | return animationDuration 79 | } 80 | 81 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 82 | guard let fromVC = transitionContext.viewController(forKey: .from), 83 | let toVC = transitionContext.viewController(forKey: .to) else { return } 84 | 85 | let containerView = transitionContext.containerView 86 | 87 | if isPresenting { 88 | viewToBeDismissed = toVC 89 | 90 | toVC.view.frame = CGRect(x: 0.0, y: fromVC.view.frame.maxY, 91 | width: UIScreen.main.bounds.width, 92 | height: UIScreen.main.bounds.height - estimatedFinalTopOffset) 93 | 94 | containerView.addSubview(dimmingView) 95 | dimmingView.edgesToSuperView() 96 | containerView.addSubview(toVC.view) 97 | 98 | toVC.setupViewCorners(radius: 12) 99 | fromVC.setupViewCorners(radius: 12) 100 | 101 | fromVC.beginAppearanceTransition(false, animated: true) 102 | toVC.beginAppearanceTransition(true, animated: true) 103 | toVC.modalPresentationCapturesStatusBarAppearance = true 104 | 105 | UIView.animate(withDuration: animationDuration, animations: { 106 | toVC.view.frame.origin.y = self.initialTopOffset 107 | fromVC.view.layer.transform = ViewControlerScale.backgroundPopUpScale.transform 108 | self.dimmingView.alpha = 0.2 109 | }, completion: { _ in 110 | self.presntingViewControlerMinY = fromVC.view.frame.minY 111 | fromVC.endAppearanceTransition() 112 | toVC.endAppearanceTransition() 113 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 114 | }) 115 | } else { 116 | containerView.addSubview(fromVC.view) 117 | 118 | fromVC.beginAppearanceTransition(false, animated: true) 119 | toVC.beginAppearanceTransition(true, animated: true) 120 | UIView.animate(withDuration: animationDuration, animations: { 121 | fromVC.view.frame.origin.y = UIScreen.main.bounds.maxY 122 | self.dimmingView.alpha = 0 123 | switch toVC.viewPresentationStyle() { 124 | case .custom, .default: 125 | toVC.view.frame.origin.y = self.presntingViewControlerMinY ?? 0 126 | toVC.view.layer.transform = ViewControlerScale.reset.transform 127 | case .none: 128 | toVC.view.layer.transform = ViewControlerScale.reset.transform 129 | toVC.view.roundedCorners(withRadius: 0) 130 | } 131 | }, completion: { _ in 132 | fromVC.endAppearanceTransition() 133 | toVC.endAppearanceTransition() 134 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 135 | }) 136 | } 137 | } 138 | } 139 | 140 | // MARK: Helper extension of UIViewController 141 | extension UIViewController { 142 | func viewPresentationStyle() -> PresentingViewType { 143 | if self.modalPresentationStyle == .custom { 144 | return .custom 145 | } else if self.presentingViewController != nil && self.modalPresentationStyle != .custom { 146 | return .default 147 | } else { 148 | return .none 149 | } 150 | } 151 | 152 | func setupViewCorners(radius: CGFloat) { 153 | view.roundedCorners(withRadius: 12) 154 | view.clipsToBounds = true 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /ResizableController/ResizableController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ResizableController.h 3 | // ResizableController 4 | // 5 | // Created by Arjun Baru on 22/10/20. 6 | // Copyright © 2020 Paytm Money 🚀. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ResizableController. 12 | FOUNDATION_EXPORT double ResizableControllerVersionNumber; 13 | 14 | //! Project version string for ResizableController. 15 | FOUNDATION_EXPORT const unsigned char ResizableControllerVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /ResizableController/ResizableControllerObserver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResizableControllerObserver.swift 3 | // 4 | // Created by Arjun Baru on 05/05/20. 5 | // Copyright © 2020 Paytm Money 🚀. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | /// Protocol to integrate Resizable Controller model presentation. To be conformed by the modally presented controller 11 | public protocol ResizableControllerPositionHandler: UIViewController { 12 | 13 | /// Override this property if you do not want to include intuitive slide up indicator. Disabled by default for non-resizable views controllers. 14 | var shouldShowSlideUpIndication: Bool { get } 15 | 16 | /// Override this property to give differnent colour to Slider up indicator. Defaults to darkGrey with alpha 0.5 17 | var sliderBackgroundColor: UIColor { get } 18 | 19 | /// Override this property to give initial custom height, calculated from top. 20 | var initialTopOffset: CGFloat { get } 21 | 22 | /// Override this property to give custom final height, calculated from top. Resizable controller will change its height from initialTopOffset to finalTopOffset. 23 | var finalTopOffset: CGFloat { get } 24 | 25 | /// Override this property to add behaviours to view controller before it changes it size. 26 | /// - Parameter value: new top offset to which view controller will shifted position. 27 | func willMoveTopOffset(value: CGFloat) 28 | 29 | /// Override this property to add additional behaviours to view controller after it changes it size. 30 | /// - Parameter value: new top offset after view controller has shifted position 31 | func didMoveTopOffset(value: CGFloat) 32 | } 33 | 34 | 35 | extension ResizableControllerPositionHandler { 36 | var onView: UIView { 37 | return self.view 38 | } 39 | } 40 | 41 | // MARK: Public default Implementation for protocol 42 | 43 | public extension ResizableControllerPositionHandler { 44 | 45 | func willMoveTopOffset(value: CGFloat) { 46 | if initialTopOffset == finalTopOffset || value > initialTopOffset { 47 | self.dismiss(animated: true, completion: nil) 48 | } 49 | } 50 | 51 | var sliderBackgroundColor: UIColor { 52 | UIColor.darkGray.withAlphaComponent(0.5) 53 | } 54 | 55 | var initialTopOffset: CGFloat { 56 | return ResizableConstants.maximumTopOffset 57 | } 58 | 59 | var finalTopOffset: CGFloat { 60 | return ResizableConstants.maximumTopOffset 61 | } 62 | 63 | var shouldShowSlideUpIndication: Bool { 64 | return initialTopOffset != finalTopOffset 65 | } 66 | 67 | func didMoveTopOffset(value: CGFloat) {} 68 | } 69 | 70 | 71 | /// This class is responsible for handling swipe related recoganisation. 72 | /// It provides call backs on to ResizableControllerPositionHandler protocol conformed class once user interacts with the view controller. 73 | /// Refer to ResizableControllerPositionHandler to see what observations can be subscibed 74 | class ResizableControllerObserver: NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate { 75 | 76 | private let panGesture = UIPanGestureRecognizer() 77 | private var viewPosition: SliderPosition = .present 78 | 79 | private weak var view: UIView? 80 | private let animationDuration: TimeInterval 81 | 82 | weak var delegate: ResizableControllerPositionHandler? 83 | weak var presentingVC: UIViewController? 84 | 85 | var estimatedFinalTopOffset = UIScreen.main.bounds.height * 0.08 86 | var estimatedInitialTopOffset = UIScreen.main.bounds.height * 0.55 87 | var presentingVCminY: CGFloat = 0 88 | private var gestureDidEndedState = true 89 | 90 | private lazy var slideIndicativeView: UIView = { 91 | let view = UIView() 92 | view.backgroundColor = delegate?.sliderBackgroundColor 93 | view.widthAnchor.constraint(equalToConstant: 55).isActive = true 94 | view.heightAnchor.constraint(equalToConstant: 5).isActive = true 95 | view.layer.cornerRadius = 2 96 | view.translatesAutoresizingMaskIntoConstraints = false 97 | return view 98 | }() 99 | 100 | init(in view: UIView, duration: TimeInterval = 0.3, delegate: ResizableControllerPositionHandler? = nil) { 101 | self.view = view 102 | self.animationDuration = duration 103 | super.init() 104 | 105 | setupDelegate(delegate) 106 | commonInit() 107 | } 108 | 109 | private func commonInit() { 110 | setupGestureRecoganisers() 111 | addSliderView() 112 | } 113 | 114 | private func setupGestureRecoganisers() { 115 | guard let view = view else { return } 116 | self.panGesture.addTarget(self, action: #selector(handlePan)) 117 | self.panGesture.delegate = self 118 | view.addGestureRecognizer(panGesture) 119 | } 120 | 121 | fileprivate func setupDelegate(_ delegate: ResizableControllerPositionHandler?) { 122 | self.delegate = delegate 123 | 124 | self.estimatedFinalTopOffset = delegate?.finalTopOffset ?? ResizableConstants.maximumTopOffset 125 | self.estimatedInitialTopOffset = delegate?.initialTopOffset ?? ResizableConstants.maximumTopOffset 126 | } 127 | 128 | 129 | /// handles user's swipe interactions 130 | @objc private func handlePan(_ gestureRecognizer: UIGestureRecognizer) { 131 | 132 | guard let currentView = panGesture.view, 133 | gestureRecognizer == panGesture, 134 | let _ = view else { return } 135 | 136 | switch panGesture.state { 137 | case .changed: 138 | guard gestureDidEndedState else { return } 139 | switch panGesture.dragDirection(inView: currentView) { 140 | case .upwards where !isHeightEqualToEstimatedHeight: 141 | translate(value: estimatedFinalTopOffset) 142 | case .downwards where isHeightEqualToEstimatedHeight: 143 | translate(value: estimatedInitialTopOffset) 144 | case .downwards: 145 | delegate?.willMoveTopOffset(value: UIScreen.main.bounds.maxY) 146 | delegate?.didMoveTopOffset(value: UIScreen.main.bounds.maxY) 147 | default: 148 | break 149 | } 150 | gestureDidEndedState = false 151 | case .ended, .failed, .cancelled: 152 | gestureDidEndedState = true 153 | default: break 154 | } 155 | } 156 | } 157 | 158 | // MARK: PanGesture Delegates 159 | extension ResizableControllerObserver { 160 | 161 | func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { 162 | 163 | guard let currentView = panGesture.view, gestureRecognizer == panGesture else { 164 | return false 165 | } 166 | 167 | switch panGesture.dragDirection(inView: currentView) { 168 | case .upwards where !isHeightEqualToEstimatedHeight: 169 | guard delegate?.initialTopOffset != delegate?.finalTopOffset else { return false } 170 | return true 171 | case .downwards: 172 | return true 173 | case .idle where !isHeightEqualToEstimatedHeight: 174 | return true 175 | default: return false 176 | } 177 | } 178 | 179 | func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, 180 | shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool { 181 | guard let currentView = gestureRecognizer.view, 182 | let otherView = otherGestureRecognizer.view else { 183 | return false 184 | } 185 | 186 | let isPanGesture = gestureRecognizer == panGesture 187 | let isDescendant = otherView.isDescendant(of: currentView) 188 | 189 | guard isPanGesture && isDescendant else { 190 | return false 191 | } 192 | 193 | guard let scrollView = otherView as? UIScrollView else { 194 | return true 195 | } 196 | 197 | return scrollView.contentOffset.y == 0 198 | } 199 | } 200 | 201 | // MARK: All About View Transaltion 202 | 203 | private extension ResizableControllerObserver { 204 | 205 | var isHeightEqualToEstimatedHeight: Bool { 206 | guard let view = view else { return false } 207 | return Int(view.frame.minY) == Int(estimatedFinalTopOffset) 208 | } 209 | 210 | /// performs resizable transformation for presented and presenting view controllers 211 | func translate(value: CGFloat) { 212 | delegate?.willMoveTopOffset(value: value) 213 | UIView.animate(withDuration: animationDuration, animations: { 214 | self.view?.frame.origin.y = value 215 | self.presentingViewTransaltion(transaltion: value) 216 | }, completion: { _ in 217 | self.delegate?.didMoveTopOffset(value: value) 218 | self.panGesture.setTranslation(.zero, in: self.view) 219 | }) 220 | } 221 | 222 | func addSliderView() { 223 | guard let currentView = view, delegate?.shouldShowSlideUpIndication == true else { return } 224 | 225 | currentView.addSubview(slideIndicativeView) 226 | 227 | NSLayoutConstraint.activate([ 228 | slideIndicativeView.centerXAnchor.constraint(equalTo: currentView.centerXAnchor), 229 | slideIndicativeView.topAnchor.constraint(equalTo: currentView.topAnchor, constant: 15) 230 | ]) 231 | 232 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { 233 | self.addSliderAnimation() 234 | } 235 | } 236 | 237 | /// Scales presenting view controller as per translation 238 | func presentingViewTransaltion(transaltion: CGFloat) { 239 | guard let viewController = presentingVC else { return } 240 | self.viewPosition.toggle(on: self.slideIndicativeView) 241 | 242 | switch viewController.viewPresentationStyle() { 243 | 244 | case .custom where estimatedFinalTopOffset == transaltion: 245 | presentingVCminY = viewController.view.frame.minY 246 | viewController.view.frame.origin.y = estimatedFinalTopOffset - 15 247 | case .custom where estimatedInitialTopOffset == transaltion: 248 | viewController.view.layer.transform = ViewControlerScale.backgroundPopUpScale.transform 249 | viewController.view.frame.origin.y = presentingVCminY 250 | 251 | case .default where estimatedFinalTopOffset == transaltion: 252 | presentingVCminY = viewController.view.frame.minY 253 | presentingVC?.view.frame.origin.y = .zero 254 | case .default where transaltion == estimatedInitialTopOffset: 255 | presentingVC?.view.frame.origin.y = presentingVCminY 256 | 257 | case .none where estimatedFinalTopOffset == transaltion: 258 | viewController.view.layer.transform = ViewControlerScale.backgroundFullScreenScale.transform 259 | case .none where estimatedInitialTopOffset == transaltion: 260 | viewController.view.layer.transform = ViewControlerScale.backgroundPopUpScale.transform 261 | default: 262 | viewController.view.layer.transform = ViewControlerScale.reset.transform 263 | } 264 | } 265 | 266 | /// adds slider bar animation 267 | func addSliderAnimation() { 268 | let group = CAAnimationGroup() 269 | 270 | let animation = CABasicAnimation(keyPath: "position") 271 | animation.fromValue = NSValue(cgPoint: CGPoint(x: self.slideIndicativeView.layer.position.x, 272 | y: self.slideIndicativeView.layer.position.y)) 273 | animation.toValue = NSValue(cgPoint: CGPoint(x: self.slideIndicativeView.layer.position.x, 274 | y: self.slideIndicativeView.layer.position.y - 6)) 275 | 276 | let animationForOpactity = CABasicAnimation(keyPath: "opacity") 277 | animationForOpactity.fromValue = 1 278 | animationForOpactity.toValue = 0.7 279 | 280 | group.animations = [animation, animationForOpactity] 281 | group.duration = 0.6 282 | group.autoreverses = true 283 | group.repeatCount = 2 284 | group.speed = 2 285 | group.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) 286 | 287 | self.slideIndicativeView.layer.add(group, forKey: "position") 288 | } 289 | } 290 | 291 | // MARK: Pan helper functions to derive swipe direction 292 | extension UIPanGestureRecognizer { 293 | enum DraggingState { 294 | case upwards, downwards, idle 295 | } 296 | 297 | func dragDirection(inView view: UIView) -> DraggingState { 298 | let velocity = self.velocity(in: view) 299 | guard abs(velocity.x) < abs(velocity.y) else { return .idle } 300 | return velocity.y < 0 ? .upwards : .downwards 301 | } 302 | } 303 | 304 | enum SliderPosition { 305 | case present 306 | case dismiss 307 | 308 | mutating func toggle(on view: UIView) { 309 | switch self { 310 | case .present: 311 | view.alpha = 0 312 | self = .dismiss 313 | case .dismiss: 314 | view.alpha = 1 315 | self = .present 316 | } 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /ResizableController/ResizableTransitioningController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResizableTransitioningController.swift 3 | // 4 | // Created by Arjun Baru on 25/04/20. 5 | // Copyright © 2020 Paytm Money 🚀. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | /// Manages present and dismiss for custom presented view controller 11 | class ResizableTransitioningController: NSObject, UIViewControllerTransitioningDelegate { 12 | 13 | private let animationDuration: TimeInterval 14 | private var gestureRecoganiser: ResizableControllerObserver! 15 | private var transitionincontroller: ResizableAnimatedController? 16 | private var shouldProceedWithTransitioning = true 17 | 18 | init(animationDuration: TimeInterval = ResizableConstants.animationDuration) { 19 | self.animationDuration = animationDuration 20 | } 21 | 22 | func animationController(forPresented presented: UIViewController, 23 | presenting: UIViewController, 24 | source: UIViewController) 25 | -> UIViewControllerAnimatedTransitioning? { 26 | 27 | guard let presentedChildVc = presented.children.first as? ResizableControllerPositionHandler else { 28 | shouldProceedWithTransitioning = false 29 | return nil 30 | } 31 | 32 | gestureRecoganiser = ResizableControllerObserver(in: presented.view, duration: animationDuration, delegate: presentedChildVc) 33 | gestureRecoganiser.presentingVC = presenting 34 | gestureRecoganiser.estimatedInitialTopOffset = presentedChildVc.initialTopOffset 35 | 36 | self.transitionincontroller = ResizableAnimatedController( 37 | initialTopOffset: presentedChildVc.initialTopOffset, 38 | animationDuration: animationDuration, 39 | isPresenting: true, 40 | estimatedFinalTopOffset: presentedChildVc.finalTopOffset) 41 | 42 | transitionincontroller?.isPresenting = true 43 | return transitionincontroller 44 | } 45 | 46 | func animationController(forDismissed dismissed: UIViewController) 47 | -> UIViewControllerAnimatedTransitioning? { 48 | guard shouldProceedWithTransitioning else { return nil } 49 | transitionincontroller?.isPresenting = false 50 | return transitionincontroller 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ResizableController/UIView+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Extension.swift 3 | // 4 | // Created by Arjun Baru on 22/10/20. 5 | // Copyright © 2020 Paytm Money 🚀. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | /// Adds rounded corner to the view 12 | public func roundedCorners(withRadius radius: CGFloat) { 13 | layer.cornerRadius = radius 14 | } 15 | 16 | /// Helper function to layout constraint to edges 17 | /// - Parameter insets: Add inset to give padding from different edges 18 | public func edgesToSuperView(insets: UIEdgeInsets = .zero) { 19 | 20 | guard let superView = superview else { return } 21 | self.translatesAutoresizingMaskIntoConstraints = false 22 | NSLayoutConstraint.activate([ 23 | self.topAnchor.constraint(equalTo: superView.topAnchor, constant: insets.top), 24 | self.bottomAnchor.constraint(equalTo: superView.bottomAnchor, constant: insets.bottom), 25 | self.leadingAnchor.constraint(equalTo: superView.leadingAnchor, constant: insets.left), 26 | self.trailingAnchor.constraint(equalTo: superView.trailingAnchor, constant: insets.right) 27 | ]) 28 | } 29 | } 30 | 31 | public struct ResizableConstants { 32 | public static let maximumTopOffset = UIScreen.main.bounds.height * 0.08 33 | public static let animationDuration: TimeInterval = 0.3 34 | } 35 | 36 | 37 | public extension UIViewController { 38 | 39 | /// An overload to implement Resizable Controller model presentation style. 40 | /// - Parameters: 41 | /// - viewControllerToPresent: ViewController which conforms to ResizableControllerPositionHandler protocol, Refer to ResizableControllerPositionHandler documentation for use case 42 | /// - animationDuration: Duration for animtion of view controller presenattion. Defaults to 0.3 seconds 43 | /// - completion: Optional parameter to provide additional functionllity after controller is presented. 44 | func present(_ viewControllerToPresent: ResizableControllerPositionHandler, 45 | animationDuration: TimeInterval = 0.3, 46 | completion: (() -> Void)? = nil) { 47 | let viewController = ResizableContainerViewController(animationDuration: animationDuration, 48 | childVC: viewControllerToPresent) 49 | self.present(viewController, animated: true, completion: completion) 50 | } 51 | } 52 | 53 | 54 | /// Helper ViewController to layout Resizable Controller Style. 55 | class ResizableContainerViewController: UIViewController { 56 | private var transitionAnimator: UIViewControllerTransitioningDelegate? 57 | init(animationDuration: TimeInterval, 58 | childVC: UIViewController) { 59 | self.transitionAnimator = ResizableTransitioningController(animationDuration: animationDuration) 60 | super.init(nibName: nil, bundle: nil) 61 | 62 | setup() 63 | addChildVC(childVC) 64 | } 65 | 66 | override var preferredStatusBarStyle: UIStatusBarStyle { 67 | return .lightContent 68 | } 69 | 70 | required init?(coder: NSCoder) { 71 | fatalError("init(coder:) has not been implemented") 72 | } 73 | private func setup() { 74 | modalPresentationStyle = .custom 75 | transitioningDelegate = transitionAnimator 76 | } 77 | private func addChildVC(_ child: UIViewController) { 78 | view.addSubview(child.view) 79 | addChild(child) 80 | child.didMove(toParent: self) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /ResizableControllerSample/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Xcode 3 | .DS_Store 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | xcuserdata/ 20 | 21 | ResizableControllerSample.xcworkspace 22 | ## Workspace file 23 | 24 | ## Other 25 | *.moved-aside 26 | *.xccheckout 27 | *.xcuserstate 28 | *.xcscmblueprint 29 | 30 | ## Obj-C/Swift specific 31 | *.hmap 32 | *.ipa 33 | *.dSYM.zip 34 | *.dSYM 35 | *.app.dSYM.zip -------------------------------------------------------------------------------- /ResizableControllerSample/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Paytm Money 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ResizableControllerSample/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'ResizableControllerSample' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for ResizableControllerSample 9 | 10 | 11 | pod 'ResizableController' 12 | 13 | end 14 | -------------------------------------------------------------------------------- /ResizableControllerSample/README.md: -------------------------------------------------------------------------------- 1 | # ResizableControllerSample -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 420CFFC62552B0A0005168E2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420CFFC52552B0A0005168E2 /* AppDelegate.swift */; }; 11 | 420CFFC82552B0A0005168E2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420CFFC72552B0A0005168E2 /* SceneDelegate.swift */; }; 12 | 420CFFCA2552B0A0005168E2 /* PresentingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420CFFC92552B0A0005168E2 /* PresentingViewController.swift */; }; 13 | 420CFFCD2552B0A0005168E2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 420CFFCB2552B0A0005168E2 /* Main.storyboard */; }; 14 | 420CFFCF2552B0A2005168E2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 420CFFCE2552B0A2005168E2 /* Assets.xcassets */; }; 15 | 420CFFD22552B0A2005168E2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 420CFFD02552B0A2005168E2 /* LaunchScreen.storyboard */; }; 16 | 420CFFE02552B32D005168E2 /* FixedHeightPresentedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420CFFDF2552B32D005168E2 /* FixedHeightPresentedViewController.swift */; }; 17 | 420CFFFF2553BF73005168E2 /* ResizablePresentedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420CFFFE2553BF73005168E2 /* ResizablePresentedViewController.swift */; }; 18 | 42589E21255D752200F109F5 /* ResizableController in Frameworks */ = {isa = PBXBuildFile; productRef = 42589E20255D752200F109F5 /* ResizableController */; }; 19 | 42EFAC8F255AAD82000CB6BC /* ResizableControllerSampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42EFAC8E255AAD82000CB6BC /* ResizableControllerSampleUITests.swift */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | 42EFAC91255AAD82000CB6BC /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = 420CFFBA2552B0A0005168E2 /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = 420CFFC12552B0A0005168E2; 28 | remoteInfo = ResizableControllerSample; 29 | }; 30 | /* End PBXContainerItemProxy section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 420CFFC22552B0A0005168E2 /* ResizableControllerSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ResizableControllerSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 420CFFC52552B0A0005168E2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 35 | 420CFFC72552B0A0005168E2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 36 | 420CFFC92552B0A0005168E2 /* PresentingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentingViewController.swift; sourceTree = ""; }; 37 | 420CFFCC2552B0A0005168E2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 38 | 420CFFCE2552B0A2005168E2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 39 | 420CFFD12552B0A2005168E2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 40 | 420CFFD32552B0A2005168E2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | 420CFFDF2552B32D005168E2 /* FixedHeightPresentedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FixedHeightPresentedViewController.swift; sourceTree = ""; }; 42 | 420CFFFE2553BF73005168E2 /* ResizablePresentedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizablePresentedViewController.swift; sourceTree = ""; }; 43 | 42EFAC8C255AAD82000CB6BC /* ResizableControllerSampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ResizableControllerSampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 42EFAC8E255AAD82000CB6BC /* ResizableControllerSampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizableControllerSampleUITests.swift; sourceTree = ""; }; 45 | 42EFAC90255AAD82000CB6BC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | /* End PBXFileReference section */ 47 | 48 | /* Begin PBXFrameworksBuildPhase section */ 49 | 420CFFBF2552B0A0005168E2 /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | 42589E21255D752200F109F5 /* ResizableController in Frameworks */, 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | 42EFAC89255AAD82000CB6BC /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | 420CFFB92552B0A0005168E2 = { 68 | isa = PBXGroup; 69 | children = ( 70 | 420CFFC42552B0A0005168E2 /* ResizableControllerSample */, 71 | 42EFAC8D255AAD82000CB6BC /* ResizableControllerSampleUITests */, 72 | 420CFFC32552B0A0005168E2 /* Products */, 73 | AFE4132F4C4195658B17B2FA /* Pods */, 74 | ); 75 | sourceTree = ""; 76 | }; 77 | 420CFFC32552B0A0005168E2 /* Products */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 420CFFC22552B0A0005168E2 /* ResizableControllerSample.app */, 81 | 42EFAC8C255AAD82000CB6BC /* ResizableControllerSampleUITests.xctest */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 420CFFC42552B0A0005168E2 /* ResizableControllerSample */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 420CFFC52552B0A0005168E2 /* AppDelegate.swift */, 90 | 420CFFC72552B0A0005168E2 /* SceneDelegate.swift */, 91 | 420CFFC92552B0A0005168E2 /* PresentingViewController.swift */, 92 | 420CFFDF2552B32D005168E2 /* FixedHeightPresentedViewController.swift */, 93 | 420CFFFE2553BF73005168E2 /* ResizablePresentedViewController.swift */, 94 | 420CFFCB2552B0A0005168E2 /* Main.storyboard */, 95 | 420CFFCE2552B0A2005168E2 /* Assets.xcassets */, 96 | 420CFFD02552B0A2005168E2 /* LaunchScreen.storyboard */, 97 | 420CFFD32552B0A2005168E2 /* Info.plist */, 98 | ); 99 | path = ResizableControllerSample; 100 | sourceTree = ""; 101 | }; 102 | 42EFAC8D255AAD82000CB6BC /* ResizableControllerSampleUITests */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 42EFAC8E255AAD82000CB6BC /* ResizableControllerSampleUITests.swift */, 106 | 42EFAC90255AAD82000CB6BC /* Info.plist */, 107 | ); 108 | path = ResizableControllerSampleUITests; 109 | sourceTree = ""; 110 | }; 111 | AFE4132F4C4195658B17B2FA /* Pods */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | ); 115 | path = Pods; 116 | sourceTree = ""; 117 | }; 118 | /* End PBXGroup section */ 119 | 120 | /* Begin PBXNativeTarget section */ 121 | 420CFFC12552B0A0005168E2 /* ResizableControllerSample */ = { 122 | isa = PBXNativeTarget; 123 | buildConfigurationList = 420CFFD62552B0A2005168E2 /* Build configuration list for PBXNativeTarget "ResizableControllerSample" */; 124 | buildPhases = ( 125 | 420CFFBE2552B0A0005168E2 /* Sources */, 126 | 420CFFBF2552B0A0005168E2 /* Frameworks */, 127 | 420CFFC02552B0A0005168E2 /* Resources */, 128 | ); 129 | buildRules = ( 130 | ); 131 | dependencies = ( 132 | ); 133 | name = ResizableControllerSample; 134 | packageProductDependencies = ( 135 | 42589E20255D752200F109F5 /* ResizableController */, 136 | ); 137 | productName = ResizableControllerSample; 138 | productReference = 420CFFC22552B0A0005168E2 /* ResizableControllerSample.app */; 139 | productType = "com.apple.product-type.application"; 140 | }; 141 | 42EFAC8B255AAD82000CB6BC /* ResizableControllerSampleUITests */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 42EFAC95255AAD82000CB6BC /* Build configuration list for PBXNativeTarget "ResizableControllerSampleUITests" */; 144 | buildPhases = ( 145 | 42EFAC88255AAD82000CB6BC /* Sources */, 146 | 42EFAC89255AAD82000CB6BC /* Frameworks */, 147 | 42EFAC8A255AAD82000CB6BC /* Resources */, 148 | ); 149 | buildRules = ( 150 | ); 151 | dependencies = ( 152 | 42EFAC92255AAD82000CB6BC /* PBXTargetDependency */, 153 | ); 154 | name = ResizableControllerSampleUITests; 155 | productName = ResizableControllerSampleUITests; 156 | productReference = 42EFAC8C255AAD82000CB6BC /* ResizableControllerSampleUITests.xctest */; 157 | productType = "com.apple.product-type.bundle.ui-testing"; 158 | }; 159 | /* End PBXNativeTarget section */ 160 | 161 | /* Begin PBXProject section */ 162 | 420CFFBA2552B0A0005168E2 /* Project object */ = { 163 | isa = PBXProject; 164 | attributes = { 165 | LastSwiftUpdateCheck = 1200; 166 | LastUpgradeCheck = 1200; 167 | TargetAttributes = { 168 | 420CFFC12552B0A0005168E2 = { 169 | CreatedOnToolsVersion = 12.0.1; 170 | }; 171 | 42EFAC8B255AAD82000CB6BC = { 172 | CreatedOnToolsVersion = 12.0.1; 173 | TestTargetID = 420CFFC12552B0A0005168E2; 174 | }; 175 | }; 176 | }; 177 | buildConfigurationList = 420CFFBD2552B0A0005168E2 /* Build configuration list for PBXProject "ResizableControllerSample" */; 178 | compatibilityVersion = "Xcode 9.3"; 179 | developmentRegion = en; 180 | hasScannedForEncodings = 0; 181 | knownRegions = ( 182 | en, 183 | Base, 184 | ); 185 | mainGroup = 420CFFB92552B0A0005168E2; 186 | packageReferences = ( 187 | 42589E1F255D752200F109F5 /* XCRemoteSwiftPackageReference "ResizableController" */, 188 | ); 189 | productRefGroup = 420CFFC32552B0A0005168E2 /* Products */; 190 | projectDirPath = ""; 191 | projectRoot = ""; 192 | targets = ( 193 | 420CFFC12552B0A0005168E2 /* ResizableControllerSample */, 194 | 42EFAC8B255AAD82000CB6BC /* ResizableControllerSampleUITests */, 195 | ); 196 | }; 197 | /* End PBXProject section */ 198 | 199 | /* Begin PBXResourcesBuildPhase section */ 200 | 420CFFC02552B0A0005168E2 /* Resources */ = { 201 | isa = PBXResourcesBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | 420CFFD22552B0A2005168E2 /* LaunchScreen.storyboard in Resources */, 205 | 420CFFCF2552B0A2005168E2 /* Assets.xcassets in Resources */, 206 | 420CFFCD2552B0A0005168E2 /* Main.storyboard in Resources */, 207 | ); 208 | runOnlyForDeploymentPostprocessing = 0; 209 | }; 210 | 42EFAC8A255AAD82000CB6BC /* Resources */ = { 211 | isa = PBXResourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | ); 215 | runOnlyForDeploymentPostprocessing = 0; 216 | }; 217 | /* End PBXResourcesBuildPhase section */ 218 | 219 | /* Begin PBXSourcesBuildPhase section */ 220 | 420CFFBE2552B0A0005168E2 /* Sources */ = { 221 | isa = PBXSourcesBuildPhase; 222 | buildActionMask = 2147483647; 223 | files = ( 224 | 420CFFCA2552B0A0005168E2 /* PresentingViewController.swift in Sources */, 225 | 420CFFFF2553BF73005168E2 /* ResizablePresentedViewController.swift in Sources */, 226 | 420CFFE02552B32D005168E2 /* FixedHeightPresentedViewController.swift in Sources */, 227 | 420CFFC62552B0A0005168E2 /* AppDelegate.swift in Sources */, 228 | 420CFFC82552B0A0005168E2 /* SceneDelegate.swift in Sources */, 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | 42EFAC88255AAD82000CB6BC /* Sources */ = { 233 | isa = PBXSourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | 42EFAC8F255AAD82000CB6BC /* ResizableControllerSampleUITests.swift in Sources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | /* End PBXSourcesBuildPhase section */ 241 | 242 | /* Begin PBXTargetDependency section */ 243 | 42EFAC92255AAD82000CB6BC /* PBXTargetDependency */ = { 244 | isa = PBXTargetDependency; 245 | target = 420CFFC12552B0A0005168E2 /* ResizableControllerSample */; 246 | targetProxy = 42EFAC91255AAD82000CB6BC /* PBXContainerItemProxy */; 247 | }; 248 | /* End PBXTargetDependency section */ 249 | 250 | /* Begin PBXVariantGroup section */ 251 | 420CFFCB2552B0A0005168E2 /* Main.storyboard */ = { 252 | isa = PBXVariantGroup; 253 | children = ( 254 | 420CFFCC2552B0A0005168E2 /* Base */, 255 | ); 256 | name = Main.storyboard; 257 | sourceTree = ""; 258 | }; 259 | 420CFFD02552B0A2005168E2 /* LaunchScreen.storyboard */ = { 260 | isa = PBXVariantGroup; 261 | children = ( 262 | 420CFFD12552B0A2005168E2 /* Base */, 263 | ); 264 | name = LaunchScreen.storyboard; 265 | sourceTree = ""; 266 | }; 267 | /* End PBXVariantGroup section */ 268 | 269 | /* Begin XCBuildConfiguration section */ 270 | 420CFFD42552B0A2005168E2 /* Debug */ = { 271 | isa = XCBuildConfiguration; 272 | buildSettings = { 273 | ALWAYS_SEARCH_USER_PATHS = NO; 274 | CLANG_ANALYZER_NONNULL = YES; 275 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 276 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 277 | CLANG_CXX_LIBRARY = "libc++"; 278 | CLANG_ENABLE_MODULES = YES; 279 | CLANG_ENABLE_OBJC_ARC = YES; 280 | CLANG_ENABLE_OBJC_WEAK = YES; 281 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 282 | CLANG_WARN_BOOL_CONVERSION = YES; 283 | CLANG_WARN_COMMA = YES; 284 | CLANG_WARN_CONSTANT_CONVERSION = YES; 285 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 286 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 287 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 288 | CLANG_WARN_EMPTY_BODY = YES; 289 | CLANG_WARN_ENUM_CONVERSION = YES; 290 | CLANG_WARN_INFINITE_RECURSION = YES; 291 | CLANG_WARN_INT_CONVERSION = YES; 292 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 293 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 294 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 295 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 296 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 297 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 298 | CLANG_WARN_STRICT_PROTOTYPES = YES; 299 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 300 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 301 | CLANG_WARN_UNREACHABLE_CODE = YES; 302 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 303 | COPY_PHASE_STRIP = NO; 304 | DEBUG_INFORMATION_FORMAT = dwarf; 305 | ENABLE_STRICT_OBJC_MSGSEND = YES; 306 | ENABLE_TESTABILITY = YES; 307 | GCC_C_LANGUAGE_STANDARD = gnu11; 308 | GCC_DYNAMIC_NO_PIC = NO; 309 | GCC_NO_COMMON_BLOCKS = YES; 310 | GCC_OPTIMIZATION_LEVEL = 0; 311 | GCC_PREPROCESSOR_DEFINITIONS = ( 312 | "DEBUG=1", 313 | "$(inherited)", 314 | ); 315 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 316 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 317 | GCC_WARN_UNDECLARED_SELECTOR = YES; 318 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 319 | GCC_WARN_UNUSED_FUNCTION = YES; 320 | GCC_WARN_UNUSED_VARIABLE = YES; 321 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 322 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 323 | MTL_FAST_MATH = YES; 324 | ONLY_ACTIVE_ARCH = YES; 325 | SDKROOT = iphoneos; 326 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 327 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 328 | }; 329 | name = Debug; 330 | }; 331 | 420CFFD52552B0A2005168E2 /* Release */ = { 332 | isa = XCBuildConfiguration; 333 | buildSettings = { 334 | ALWAYS_SEARCH_USER_PATHS = NO; 335 | CLANG_ANALYZER_NONNULL = YES; 336 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 337 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 338 | CLANG_CXX_LIBRARY = "libc++"; 339 | CLANG_ENABLE_MODULES = YES; 340 | CLANG_ENABLE_OBJC_ARC = YES; 341 | CLANG_ENABLE_OBJC_WEAK = YES; 342 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 343 | CLANG_WARN_BOOL_CONVERSION = YES; 344 | CLANG_WARN_COMMA = YES; 345 | CLANG_WARN_CONSTANT_CONVERSION = YES; 346 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 347 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 348 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 349 | CLANG_WARN_EMPTY_BODY = YES; 350 | CLANG_WARN_ENUM_CONVERSION = YES; 351 | CLANG_WARN_INFINITE_RECURSION = YES; 352 | CLANG_WARN_INT_CONVERSION = YES; 353 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 354 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 355 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 356 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 357 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 358 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 359 | CLANG_WARN_STRICT_PROTOTYPES = YES; 360 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 361 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 362 | CLANG_WARN_UNREACHABLE_CODE = YES; 363 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 364 | COPY_PHASE_STRIP = NO; 365 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 366 | ENABLE_NS_ASSERTIONS = NO; 367 | ENABLE_STRICT_OBJC_MSGSEND = YES; 368 | GCC_C_LANGUAGE_STANDARD = gnu11; 369 | GCC_NO_COMMON_BLOCKS = YES; 370 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 371 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 372 | GCC_WARN_UNDECLARED_SELECTOR = YES; 373 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 374 | GCC_WARN_UNUSED_FUNCTION = YES; 375 | GCC_WARN_UNUSED_VARIABLE = YES; 376 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 377 | MTL_ENABLE_DEBUG_INFO = NO; 378 | MTL_FAST_MATH = YES; 379 | SDKROOT = iphoneos; 380 | SWIFT_COMPILATION_MODE = wholemodule; 381 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 382 | VALIDATE_PRODUCT = YES; 383 | }; 384 | name = Release; 385 | }; 386 | 420CFFD72552B0A2005168E2 /* Debug */ = { 387 | isa = XCBuildConfiguration; 388 | buildSettings = { 389 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 390 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 391 | CODE_SIGN_STYLE = Automatic; 392 | DEVELOPMENT_TEAM = 885LR8YVNT; 393 | INFOPLIST_FILE = ResizableControllerSample/Info.plist; 394 | LD_RUNPATH_SEARCH_PATHS = ( 395 | "$(inherited)", 396 | "@executable_path/Frameworks", 397 | ); 398 | PRODUCT_BUNDLE_IDENTIFIER = com.paytmmoney.opensource.ResizableControllerSample; 399 | PRODUCT_NAME = "$(TARGET_NAME)"; 400 | SWIFT_VERSION = 5.0; 401 | TARGETED_DEVICE_FAMILY = "1,2"; 402 | }; 403 | name = Debug; 404 | }; 405 | 420CFFD82552B0A2005168E2 /* Release */ = { 406 | isa = XCBuildConfiguration; 407 | buildSettings = { 408 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 409 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 410 | CODE_SIGN_STYLE = Automatic; 411 | DEVELOPMENT_TEAM = 885LR8YVNT; 412 | INFOPLIST_FILE = ResizableControllerSample/Info.plist; 413 | LD_RUNPATH_SEARCH_PATHS = ( 414 | "$(inherited)", 415 | "@executable_path/Frameworks", 416 | ); 417 | PRODUCT_BUNDLE_IDENTIFIER = com.paytmmoney.opensource.ResizableControllerSample; 418 | PRODUCT_NAME = "$(TARGET_NAME)"; 419 | SWIFT_VERSION = 5.0; 420 | TARGETED_DEVICE_FAMILY = "1,2"; 421 | }; 422 | name = Release; 423 | }; 424 | 42EFAC93255AAD82000CB6BC /* Debug */ = { 425 | isa = XCBuildConfiguration; 426 | buildSettings = { 427 | CODE_SIGN_STYLE = Automatic; 428 | DEVELOPMENT_TEAM = V83QP83683; 429 | INFOPLIST_FILE = ResizableControllerSampleUITests/Info.plist; 430 | LD_RUNPATH_SEARCH_PATHS = ( 431 | "$(inherited)", 432 | "@executable_path/Frameworks", 433 | "@loader_path/Frameworks", 434 | ); 435 | PRODUCT_BUNDLE_IDENTIFIER = com.paytmmoney.opensource.ResizableControllerSampleUITests; 436 | PRODUCT_NAME = "$(TARGET_NAME)"; 437 | SWIFT_VERSION = 5.0; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | TEST_TARGET_NAME = ResizableControllerSample; 440 | }; 441 | name = Debug; 442 | }; 443 | 42EFAC94255AAD82000CB6BC /* Release */ = { 444 | isa = XCBuildConfiguration; 445 | buildSettings = { 446 | CODE_SIGN_STYLE = Automatic; 447 | DEVELOPMENT_TEAM = V83QP83683; 448 | INFOPLIST_FILE = ResizableControllerSampleUITests/Info.plist; 449 | LD_RUNPATH_SEARCH_PATHS = ( 450 | "$(inherited)", 451 | "@executable_path/Frameworks", 452 | "@loader_path/Frameworks", 453 | ); 454 | PRODUCT_BUNDLE_IDENTIFIER = com.paytmmoney.opensource.ResizableControllerSampleUITests; 455 | PRODUCT_NAME = "$(TARGET_NAME)"; 456 | SWIFT_VERSION = 5.0; 457 | TARGETED_DEVICE_FAMILY = "1,2"; 458 | TEST_TARGET_NAME = ResizableControllerSample; 459 | }; 460 | name = Release; 461 | }; 462 | /* End XCBuildConfiguration section */ 463 | 464 | /* Begin XCConfigurationList section */ 465 | 420CFFBD2552B0A0005168E2 /* Build configuration list for PBXProject "ResizableControllerSample" */ = { 466 | isa = XCConfigurationList; 467 | buildConfigurations = ( 468 | 420CFFD42552B0A2005168E2 /* Debug */, 469 | 420CFFD52552B0A2005168E2 /* Release */, 470 | ); 471 | defaultConfigurationIsVisible = 0; 472 | defaultConfigurationName = Release; 473 | }; 474 | 420CFFD62552B0A2005168E2 /* Build configuration list for PBXNativeTarget "ResizableControllerSample" */ = { 475 | isa = XCConfigurationList; 476 | buildConfigurations = ( 477 | 420CFFD72552B0A2005168E2 /* Debug */, 478 | 420CFFD82552B0A2005168E2 /* Release */, 479 | ); 480 | defaultConfigurationIsVisible = 0; 481 | defaultConfigurationName = Release; 482 | }; 483 | 42EFAC95255AAD82000CB6BC /* Build configuration list for PBXNativeTarget "ResizableControllerSampleUITests" */ = { 484 | isa = XCConfigurationList; 485 | buildConfigurations = ( 486 | 42EFAC93255AAD82000CB6BC /* Debug */, 487 | 42EFAC94255AAD82000CB6BC /* Release */, 488 | ); 489 | defaultConfigurationIsVisible = 0; 490 | defaultConfigurationName = Release; 491 | }; 492 | /* End XCConfigurationList section */ 493 | 494 | /* Begin XCRemoteSwiftPackageReference section */ 495 | 42589E1F255D752200F109F5 /* XCRemoteSwiftPackageReference "ResizableController" */ = { 496 | isa = XCRemoteSwiftPackageReference; 497 | repositoryURL = "https://github.com/paytmmoney/ResizableController"; 498 | requirement = { 499 | branch = dev/SwiftPackageManager; 500 | kind = branch; 501 | }; 502 | }; 503 | /* End XCRemoteSwiftPackageReference section */ 504 | 505 | /* Begin XCSwiftPackageProductDependency section */ 506 | 42589E20255D752200F109F5 /* ResizableController */ = { 507 | isa = XCSwiftPackageProductDependency; 508 | package = 42589E1F255D752200F109F5 /* XCRemoteSwiftPackageReference "ResizableController" */; 509 | productName = ResizableController; 510 | }; 511 | /* End XCSwiftPackageProductDependency section */ 512 | }; 513 | rootObject = 420CFFBA2552B0A0005168E2 /* Project object */; 514 | } 515 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ResizableControllerSample 4 | // 5 | // Created by Arjun Baru on 04/11/20. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSample/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSample/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 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 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 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 122 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSample/FixedHeightPresentedViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FixedHeightPresentedViewController.swift 3 | // ResizableControllerSample 4 | // 5 | // Created by Arjun Baru on 04/11/20. 6 | // 7 | 8 | import UIKit 9 | import ResizableController 10 | 11 | class FixedHeightPresentedViewController: UIViewController { 12 | 13 | @IBOutlet weak var swipeLabel: UILabel! 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | } 17 | } 18 | 19 | extension FixedHeightPresentedViewController: ResizableControllerPositionHandler {} 20 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UIApplicationSupportsIndirectInputEvents 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSample/PresentingViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PresentingViewController.swift 3 | // ResizableControllerSample 4 | // 5 | // Created by Arjun Baru on 04/11/20. 6 | // 7 | 8 | import UIKit 9 | import ResizableController 10 | 11 | class PresentingViewController: UIViewController { 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | title = "Resizable Controller" 15 | } 16 | 17 | @IBAction func onTapOfCustomHeightController(_ sender: Any) { 18 | 19 | let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "ResizablePresentedViewController") as ResizablePresentedViewController 20 | self.present(viewController) 21 | } 22 | 23 | @IBAction func onTapOfFixedHeightController(_ sender: Any) { 24 | let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "FixedPresentingViewController") as FixedHeightPresentedViewController 25 | self.present(viewController) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSample/ResizablePresentedViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResizablePresentedViewController.swift 3 | // ResizableControllerSample 4 | // 5 | // Created by Arjun Baru on 05/11/20. 6 | // 7 | 8 | import UIKit 9 | import ResizableController 10 | 11 | class ResizablePresentedViewController: UIViewController { 12 | 13 | @IBOutlet weak var swipeLabel: UILabel! 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | } 17 | } 18 | 19 | extension ResizablePresentedViewController: ResizableControllerPositionHandler { 20 | var initialTopOffset: CGFloat { 21 | 500 22 | } 23 | 24 | func didMoveTopOffset(value: CGFloat) { 25 | if value == initialTopOffset { 26 | self.swipeLabel.text = "Swipe up to full size" 27 | } 28 | 29 | if value == finalTopOffset { 30 | self.swipeLabel.text = "Swipe down to half screen" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSample/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // ResizableControllerSample 4 | // 5 | // Created by Arjun Baru on 04/11/20. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSampleUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ResizableControllerSample/ResizableControllerSampleUITests/ResizableControllerSampleUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResizableControllerSampleUITests.swift 3 | // ResizableControllerSampleUITests 4 | // 5 | // Created by Arjun Baru on 10/11/20. 6 | // 7 | 8 | import XCTest 9 | 10 | class ResizableControllerSampleUITests: XCTestCase { 11 | 12 | func testResizableController() throws { 13 | let app = XCUIApplication() 14 | app.launch() 15 | 16 | 17 | app.buttons["Show Custom Height Controller"].staticTexts["Show Custom Height Controller"].tap() 18 | 19 | let element2 = app.children(matching: .window).element(boundBy: 0).children(matching: .other).element.children(matching: .other).element(boundBy: 1) 20 | 21 | // Swipe to full screen 22 | element2.swipeUp() 23 | sleep(1) 24 | 25 | // swipe to half screen 26 | element2.swipeDown() 27 | sleep(1) 28 | 29 | // back tap to dismiss 30 | app.children(matching: .window).element(boundBy: 0).children(matching: .other).element.children(matching: .other).element(boundBy: 1).tap() 31 | } 32 | 33 | func testFixedHeightController() throws { 34 | let app = XCUIApplication() 35 | app.launch() 36 | 37 | app.buttons["Show Fixed Height Controller"].staticTexts["Show Fixed Height Controller"].tap() 38 | let element2 = app.children(matching: .window).element(boundBy: 0).children(matching: .other).element.children(matching: .other).element(boundBy: 1) 39 | 40 | // test swipe up on full screen 41 | element2.swipeUp() 42 | sleep(1) 43 | 44 | // swipe down to dismiss 45 | element2.swipeDown() 46 | sleep(1) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ResizableControllerUnitTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ResizableControllerUnitTests/ResizableControllerUnitTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResizableControllerUnitTests.swift 3 | // ResizableControllerUnitTests 4 | // 5 | // Created by Arjun Baru on 10/11/20. 6 | // 7 | 8 | import XCTest 9 | import UIKit 10 | @testable import ResizableController 11 | 12 | class ResizableControllerUnitTests: XCTestCase { 13 | 14 | var presentedResizableVC : ResizableControllerPositionHandler = ResizableControllerPositionHandlerMock() 15 | var presentedFixedSizeVC : ResizableControllerPositionHandler = FixedSizeControllerPositionHandlerMock() 16 | var panHandler: ResizableControllerObserver? 17 | 18 | func testResizableControllerPositionHandlerDelegate() throws { 19 | panHandler = ResizableControllerObserver(in: presentedResizableVC.onView, duration: 0.3, delegate: presentedResizableVC) 20 | // test for custom initial top offset updated on client end 21 | XCTAssertEqual(presentedResizableVC.initialTopOffset, panHandler?.estimatedInitialTopOffset) 22 | 23 | // test for maximum top offset updated on client end 24 | XCTAssertEqual(presentedResizableVC.finalTopOffset, panHandler?.estimatedFinalTopOffset) 25 | 26 | // test for updated sliderbar colour on client end 27 | XCTAssertEqual(presentedResizableVC.sliderBackgroundColor, panHandler?.delegate?.sliderBackgroundColor) 28 | } 29 | 30 | func testFixedSizeControllerPositionHandlerDelegate() throws { 31 | panHandler = ResizableControllerObserver(in: presentedResizableVC.onView, duration: 0.3, delegate: presentedFixedSizeVC) 32 | // test for custom initial top offset for default implementation 33 | XCTAssertEqual(presentedFixedSizeVC.initialTopOffset, panHandler?.estimatedInitialTopOffset) 34 | 35 | // test for maximum top offset for default implementation 36 | XCTAssertEqual(presentedFixedSizeVC.finalTopOffset, panHandler?.estimatedFinalTopOffset) 37 | 38 | // test for updated sliderbar for default colour 39 | XCTAssertEqual(presentedFixedSizeVC.sliderBackgroundColor, panHandler?.delegate?.sliderBackgroundColor) 40 | } 41 | 42 | } 43 | 44 | /// Resizable controller with custom implementation 45 | class ResizableControllerPositionHandlerMock: UIViewController, ResizableControllerPositionHandler { 46 | var initialTopOffset: CGFloat { 47 | 500 48 | } 49 | 50 | var finalTopOffset: CGFloat { 51 | 200 52 | } 53 | 54 | var sliderBackgroundColor: UIColor { 55 | UIColor.red.withAlphaComponent(0.5) 56 | } 57 | } 58 | 59 | /// Resizable controller with Default implementation 60 | class FixedSizeControllerPositionHandlerMock: UIViewController, ResizableControllerPositionHandler { 61 | } 62 | -------------------------------------------------------------------------------- /Sources/ResizableController/ResizableAnimatedController.swift: -------------------------------------------------------------------------------- 1 | ../../ResizableController/ResizableAnimatedController.swift -------------------------------------------------------------------------------- /Sources/ResizableController/ResizableControllerObserver.swift: -------------------------------------------------------------------------------- 1 | ../../ResizableController/ResizableControllerObserver.swift -------------------------------------------------------------------------------- /Sources/ResizableController/ResizableTransitioningController.swift: -------------------------------------------------------------------------------- 1 | ../../ResizableController/ResizableTransitioningController.swift -------------------------------------------------------------------------------- /Sources/ResizableController/UIView+Extension.swift: -------------------------------------------------------------------------------- 1 | ../../ResizableController/UIView+Extension.swift --------------------------------------------------------------------------------