├── .gitignore ├── .gitmodules ├── .swiftlint.yml ├── Cartfile ├── README.md ├── TransformableView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── TransformableView.xcscheme ├── TransformableView ├── BorderType.swift ├── DragableView.swift ├── Info.plist ├── ResizableView.swift ├── RotatableView.swift ├── TransformableView.h ├── TransformableView.swift └── UIView.swift ├── TransformableViewDemo ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── View.swift └── ViewController.swift ├── TransformableViewTests └── Info.plist └── demo.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/xcode 2 | 3 | ### Xcode ### 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcscmblueprint 27 | *.generated.swift 28 | 29 | .DS_Store 30 | 31 | /Pods 32 | 33 | /Carthage 34 | Cartfile.resolved 35 | 36 | ./build 37 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Carthage/Checkouts/RxSwift"] 2 | path = Carthage/Checkouts/RxSwift 3 | url = https://github.com/ReactiveX/RxSwift.git 4 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - todo 3 | 4 | excluded: # paths to ignore during linting. Takes precedence over `included`. 5 | - Carthage 6 | - Pods 7 | opt_in_rules: 8 | - empty_count 9 | - file_header 10 | - explicit_init 11 | - closure_spacing 12 | - overridden_super_call 13 | - redundant_nil_coalescing 14 | - private_outlet 15 | - nimble_operator 16 | - attributes 17 | - operator_usage_whitespace 18 | - closure_end_indentation 19 | - first_where 20 | - object_literal 21 | - number_separator 22 | - prohibited_super_call 23 | - fatal_error_message 24 | - vertical_parameter_alignment_on_call 25 | - let_var_whitespace 26 | identifier_name: 27 | excluded: 28 | - id 29 | - x 30 | - y 31 | line_length: 196 32 | number_separator: 33 | minimum_length: 5 34 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "RxSwiftCommunity/RxGesture" ~> 2.0 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TransformableView 2 | 3 | Draggable, resizable and rotatable UIView implemented using protocols in Swift. Uses RxCocoa, RxGesture and RxSwift to avoid protocol extension restrictions. 4 | 5 | ## Usage 6 | 7 | `TransformableView` is a composition of `DraggableView`, `ResizableView` and `RotatableView`. Implement desired protocol as needed. 8 | 9 | ```Swift 10 | class View: UIView, TransformableView {} 11 | 12 | class NonRotatableView: UIView, DraggableView, ResizableView {} 13 | ``` 14 | 15 | Protocols require `var disposeBag: DisposeBag { get }` and include optional `func didUpdate(frame: CGRect)` and `func didUpdate(rotation: CGFloat)` methods. 16 | 17 | Finnaly, add gesture handling 18 | 19 | ```Swift 20 | view.addGesturesHandling() 21 | ``` 22 | 23 | ## Example 24 | 25 | Demo is included in the project. 26 | 27 | ![Demo](https://github.com/Rep2/TransformableView/blob/master/demo.gif) 28 | -------------------------------------------------------------------------------- /TransformableView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F615F5831FDEDD03007762A3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F615F5821FDEDD03007762A3 /* AppDelegate.swift */; }; 11 | F615F5851FDEDD03007762A3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F615F5841FDEDD03007762A3 /* ViewController.swift */; }; 12 | F615F5881FDEDD03007762A3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F615F5861FDEDD03007762A3 /* Main.storyboard */; }; 13 | F615F58A1FDEDD03007762A3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F615F5891FDEDD03007762A3 /* Assets.xcassets */; }; 14 | F615F58D1FDEDD03007762A3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F615F58B1FDEDD03007762A3 /* LaunchScreen.storyboard */; }; 15 | F615F5931FDEDFE9007762A3 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = F615F5921FDEDFE9007762A3 /* View.swift */; }; 16 | F617AA57201AAC7600985E34 /* BorderType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F617AA56201AAC7600985E34 /* BorderType.swift */; }; 17 | F617AA59201AB81F00985E34 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F617AA58201AB81F00985E34 /* UIView.swift */; }; 18 | F682F2F2203DB106001DDD7C /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F69B4E9B1FDEFD4B00210797 /* RxCocoa.framework */; }; 19 | F682F2F3203DB106001DDD7C /* RxGesture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F69B4E921FDEFD1300210797 /* RxGesture.framework */; }; 20 | F682F2F4203DB106001DDD7C /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F69B4E901FDEFD1100210797 /* RxSwift.framework */; }; 21 | F69B4E911FDEFD1100210797 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F69B4E901FDEFD1100210797 /* RxSwift.framework */; }; 22 | F69B4E931FDEFD1300210797 /* RxGesture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F69B4E921FDEFD1300210797 /* RxGesture.framework */; }; 23 | F69B4E9C1FDEFD4B00210797 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F69B4E9B1FDEFD4B00210797 /* RxCocoa.framework */; }; 24 | F6DCAC5C1FDCC54700234666 /* TransformableView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6DCAC521FDCC54700234666 /* TransformableView.framework */; }; 25 | F6DCAC631FDCC54700234666 /* TransformableView.h in Headers */ = {isa = PBXBuildFile; fileRef = F6DCAC551FDCC54700234666 /* TransformableView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 26 | F6DCAC701FDCC5D300234666 /* RotatableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6DCAC6C1FDCC5D300234666 /* RotatableView.swift */; }; 27 | F6DCAC711FDCC5D300234666 /* DragableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6DCAC6D1FDCC5D300234666 /* DragableView.swift */; }; 28 | F6DCAC721FDCC5D300234666 /* TransformableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6DCAC6E1FDCC5D300234666 /* TransformableView.swift */; }; 29 | F6DCAC731FDCC5D300234666 /* ResizableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6DCAC6F1FDCC5D300234666 /* ResizableView.swift */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXContainerItemProxy section */ 33 | F6DCAC5D1FDCC54700234666 /* PBXContainerItemProxy */ = { 34 | isa = PBXContainerItemProxy; 35 | containerPortal = F6DCAC491FDCC54700234666 /* Project object */; 36 | proxyType = 1; 37 | remoteGlobalIDString = F6DCAC511FDCC54700234666; 38 | remoteInfo = TransformableView; 39 | }; 40 | /* End PBXContainerItemProxy section */ 41 | 42 | /* Begin PBXFileReference section */ 43 | F615F5801FDEDD03007762A3 /* TransformableViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TransformableViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | F615F5821FDEDD03007762A3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 45 | F615F5841FDEDD03007762A3 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 46 | F615F5871FDEDD03007762A3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | F615F5891FDEDD03007762A3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | F615F58C1FDEDD03007762A3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | F615F58E1FDEDD03007762A3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | F615F5921FDEDFE9007762A3 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; 51 | F617AA56201AAC7600985E34 /* BorderType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BorderType.swift; sourceTree = ""; }; 52 | F617AA58201AB81F00985E34 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; 53 | F69B4E901FDEFD1100210797 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = ""; }; 54 | F69B4E921FDEFD1300210797 /* RxGesture.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxGesture.framework; path = Carthage/Build/iOS/RxGesture.framework; sourceTree = ""; }; 55 | F69B4E941FDEFD2000210797 /* RxGesture.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxGesture.framework; path = Carthage/Build/iOS/RxGesture.framework; sourceTree = ""; }; 56 | F69B4E981FDEFD2600210797 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = ""; }; 57 | F69B4E9B1FDEFD4B00210797 /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = ""; }; 58 | F69B4E9D1FDEFD6500210797 /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = ""; }; 59 | F6DCAC521FDCC54700234666 /* TransformableView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TransformableView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | F6DCAC551FDCC54700234666 /* TransformableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TransformableView.h; sourceTree = ""; }; 61 | F6DCAC561FDCC54700234666 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62 | F6DCAC5B1FDCC54700234666 /* TransformableViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TransformableViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | F6DCAC621FDCC54700234666 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | F6DCAC6C1FDCC5D300234666 /* RotatableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RotatableView.swift; sourceTree = ""; }; 65 | F6DCAC6D1FDCC5D300234666 /* DragableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragableView.swift; sourceTree = ""; }; 66 | F6DCAC6E1FDCC5D300234666 /* TransformableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransformableView.swift; sourceTree = ""; }; 67 | F6DCAC6F1FDCC5D300234666 /* ResizableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResizableView.swift; sourceTree = ""; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | F615F57D1FDEDD03007762A3 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | F682F2F2203DB106001DDD7C /* RxCocoa.framework in Frameworks */, 76 | F682F2F3203DB106001DDD7C /* RxGesture.framework in Frameworks */, 77 | F682F2F4203DB106001DDD7C /* RxSwift.framework in Frameworks */, 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | F6DCAC4E1FDCC54700234666 /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 2147483647; 84 | files = ( 85 | F69B4E911FDEFD1100210797 /* RxSwift.framework in Frameworks */, 86 | F69B4E931FDEFD1300210797 /* RxGesture.framework in Frameworks */, 87 | F69B4E9C1FDEFD4B00210797 /* RxCocoa.framework in Frameworks */, 88 | ); 89 | runOnlyForDeploymentPostprocessing = 0; 90 | }; 91 | F6DCAC581FDCC54700234666 /* Frameworks */ = { 92 | isa = PBXFrameworksBuildPhase; 93 | buildActionMask = 2147483647; 94 | files = ( 95 | F6DCAC5C1FDCC54700234666 /* TransformableView.framework in Frameworks */, 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXFrameworksBuildPhase section */ 100 | 101 | /* Begin PBXGroup section */ 102 | F615F5811FDEDD03007762A3 /* TransformableViewDemo */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | F615F5821FDEDD03007762A3 /* AppDelegate.swift */, 106 | F615F5841FDEDD03007762A3 /* ViewController.swift */, 107 | F615F5861FDEDD03007762A3 /* Main.storyboard */, 108 | F615F5891FDEDD03007762A3 /* Assets.xcassets */, 109 | F615F58B1FDEDD03007762A3 /* LaunchScreen.storyboard */, 110 | F615F58E1FDEDD03007762A3 /* Info.plist */, 111 | F615F5921FDEDFE9007762A3 /* View.swift */, 112 | ); 113 | path = TransformableViewDemo; 114 | sourceTree = ""; 115 | }; 116 | F69B4E8F1FDEFD1100210797 /* Frameworks */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | F69B4E9B1FDEFD4B00210797 /* RxCocoa.framework */, 120 | F69B4E921FDEFD1300210797 /* RxGesture.framework */, 121 | F69B4E901FDEFD1100210797 /* RxSwift.framework */, 122 | ); 123 | name = Frameworks; 124 | sourceTree = ""; 125 | }; 126 | F6DCAC481FDCC54700234666 = { 127 | isa = PBXGroup; 128 | children = ( 129 | F69B4E9D1FDEFD6500210797 /* RxCocoa.framework */, 130 | F69B4E981FDEFD2600210797 /* RxSwift.framework */, 131 | F69B4E941FDEFD2000210797 /* RxGesture.framework */, 132 | F6DCAC541FDCC54700234666 /* TransformableView */, 133 | F6DCAC5F1FDCC54700234666 /* TransformableViewTests */, 134 | F615F5811FDEDD03007762A3 /* TransformableViewDemo */, 135 | F6DCAC531FDCC54700234666 /* Products */, 136 | F69B4E8F1FDEFD1100210797 /* Frameworks */, 137 | ); 138 | sourceTree = ""; 139 | }; 140 | F6DCAC531FDCC54700234666 /* Products */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | F6DCAC521FDCC54700234666 /* TransformableView.framework */, 144 | F6DCAC5B1FDCC54700234666 /* TransformableViewTests.xctest */, 145 | F615F5801FDEDD03007762A3 /* TransformableViewDemo.app */, 146 | ); 147 | name = Products; 148 | sourceTree = ""; 149 | }; 150 | F6DCAC541FDCC54700234666 /* TransformableView */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | F6DCAC6D1FDCC5D300234666 /* DragableView.swift */, 154 | F6DCAC6F1FDCC5D300234666 /* ResizableView.swift */, 155 | F6DCAC6C1FDCC5D300234666 /* RotatableView.swift */, 156 | F6DCAC6E1FDCC5D300234666 /* TransformableView.swift */, 157 | F6DCAC551FDCC54700234666 /* TransformableView.h */, 158 | F6DCAC561FDCC54700234666 /* Info.plist */, 159 | F617AA56201AAC7600985E34 /* BorderType.swift */, 160 | F617AA58201AB81F00985E34 /* UIView.swift */, 161 | ); 162 | path = TransformableView; 163 | sourceTree = ""; 164 | }; 165 | F6DCAC5F1FDCC54700234666 /* TransformableViewTests */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | F6DCAC621FDCC54700234666 /* Info.plist */, 169 | ); 170 | path = TransformableViewTests; 171 | sourceTree = ""; 172 | }; 173 | /* End PBXGroup section */ 174 | 175 | /* Begin PBXHeadersBuildPhase section */ 176 | F6DCAC4F1FDCC54700234666 /* Headers */ = { 177 | isa = PBXHeadersBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | F6DCAC631FDCC54700234666 /* TransformableView.h in Headers */, 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | /* End PBXHeadersBuildPhase section */ 185 | 186 | /* Begin PBXNativeTarget section */ 187 | F615F57F1FDEDD03007762A3 /* TransformableViewDemo */ = { 188 | isa = PBXNativeTarget; 189 | buildConfigurationList = F615F5911FDEDD03007762A3 /* Build configuration list for PBXNativeTarget "TransformableViewDemo" */; 190 | buildPhases = ( 191 | F615F57C1FDEDD03007762A3 /* Sources */, 192 | F615F57D1FDEDD03007762A3 /* Frameworks */, 193 | F615F57E1FDEDD03007762A3 /* Resources */, 194 | F682F2F5203DB10B001DDD7C /* Carthage */, 195 | ); 196 | buildRules = ( 197 | ); 198 | dependencies = ( 199 | ); 200 | name = TransformableViewDemo; 201 | productName = TransformableViewDemo; 202 | productReference = F615F5801FDEDD03007762A3 /* TransformableViewDemo.app */; 203 | productType = "com.apple.product-type.application"; 204 | }; 205 | F6DCAC511FDCC54700234666 /* TransformableView */ = { 206 | isa = PBXNativeTarget; 207 | buildConfigurationList = F6DCAC661FDCC54700234666 /* Build configuration list for PBXNativeTarget "TransformableView" */; 208 | buildPhases = ( 209 | F6DCAC4D1FDCC54700234666 /* Sources */, 210 | F6DCAC4E1FDCC54700234666 /* Frameworks */, 211 | F6DCAC4F1FDCC54700234666 /* Headers */, 212 | F6DCAC501FDCC54700234666 /* Resources */, 213 | F657A7B921809C7700FCA602 /* Swiftlint */, 214 | ); 215 | buildRules = ( 216 | ); 217 | dependencies = ( 218 | ); 219 | name = TransformableView; 220 | productName = TransformableView; 221 | productReference = F6DCAC521FDCC54700234666 /* TransformableView.framework */; 222 | productType = "com.apple.product-type.framework"; 223 | }; 224 | F6DCAC5A1FDCC54700234666 /* TransformableViewTests */ = { 225 | isa = PBXNativeTarget; 226 | buildConfigurationList = F6DCAC691FDCC54700234666 /* Build configuration list for PBXNativeTarget "TransformableViewTests" */; 227 | buildPhases = ( 228 | F6DCAC571FDCC54700234666 /* Sources */, 229 | F6DCAC581FDCC54700234666 /* Frameworks */, 230 | F6DCAC591FDCC54700234666 /* Resources */, 231 | ); 232 | buildRules = ( 233 | ); 234 | dependencies = ( 235 | F6DCAC5E1FDCC54700234666 /* PBXTargetDependency */, 236 | ); 237 | name = TransformableViewTests; 238 | productName = TransformableViewTests; 239 | productReference = F6DCAC5B1FDCC54700234666 /* TransformableViewTests.xctest */; 240 | productType = "com.apple.product-type.bundle.unit-test"; 241 | }; 242 | /* End PBXNativeTarget section */ 243 | 244 | /* Begin PBXProject section */ 245 | F6DCAC491FDCC54700234666 /* Project object */ = { 246 | isa = PBXProject; 247 | attributes = { 248 | LastSwiftUpdateCheck = 0920; 249 | LastUpgradeCheck = 1000; 250 | ORGANIZATIONNAME = Rep; 251 | TargetAttributes = { 252 | F615F57F1FDEDD03007762A3 = { 253 | CreatedOnToolsVersion = 9.2; 254 | ProvisioningStyle = Automatic; 255 | }; 256 | F6DCAC511FDCC54700234666 = { 257 | CreatedOnToolsVersion = 9.2; 258 | LastSwiftMigration = ""; 259 | ProvisioningStyle = Automatic; 260 | }; 261 | F6DCAC5A1FDCC54700234666 = { 262 | CreatedOnToolsVersion = 9.2; 263 | ProvisioningStyle = Automatic; 264 | }; 265 | }; 266 | }; 267 | buildConfigurationList = F6DCAC4C1FDCC54700234666 /* Build configuration list for PBXProject "TransformableView" */; 268 | compatibilityVersion = "Xcode 8.0"; 269 | developmentRegion = en; 270 | hasScannedForEncodings = 0; 271 | knownRegions = ( 272 | en, 273 | Base, 274 | ); 275 | mainGroup = F6DCAC481FDCC54700234666; 276 | productRefGroup = F6DCAC531FDCC54700234666 /* Products */; 277 | projectDirPath = ""; 278 | projectRoot = ""; 279 | targets = ( 280 | F6DCAC511FDCC54700234666 /* TransformableView */, 281 | F6DCAC5A1FDCC54700234666 /* TransformableViewTests */, 282 | F615F57F1FDEDD03007762A3 /* TransformableViewDemo */, 283 | ); 284 | }; 285 | /* End PBXProject section */ 286 | 287 | /* Begin PBXResourcesBuildPhase section */ 288 | F615F57E1FDEDD03007762A3 /* Resources */ = { 289 | isa = PBXResourcesBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | F615F58D1FDEDD03007762A3 /* LaunchScreen.storyboard in Resources */, 293 | F615F58A1FDEDD03007762A3 /* Assets.xcassets in Resources */, 294 | F615F5881FDEDD03007762A3 /* Main.storyboard in Resources */, 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | F6DCAC501FDCC54700234666 /* Resources */ = { 299 | isa = PBXResourcesBuildPhase; 300 | buildActionMask = 2147483647; 301 | files = ( 302 | ); 303 | runOnlyForDeploymentPostprocessing = 0; 304 | }; 305 | F6DCAC591FDCC54700234666 /* Resources */ = { 306 | isa = PBXResourcesBuildPhase; 307 | buildActionMask = 2147483647; 308 | files = ( 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | }; 312 | /* End PBXResourcesBuildPhase section */ 313 | 314 | /* Begin PBXShellScriptBuildPhase section */ 315 | F657A7B921809C7700FCA602 /* Swiftlint */ = { 316 | isa = PBXShellScriptBuildPhase; 317 | buildActionMask = 2147483647; 318 | files = ( 319 | ); 320 | inputFileListPaths = ( 321 | ); 322 | inputPaths = ( 323 | ); 324 | name = Swiftlint; 325 | outputFileListPaths = ( 326 | ); 327 | outputPaths = ( 328 | ); 329 | runOnlyForDeploymentPostprocessing = 0; 330 | shellPath = /bin/sh; 331 | shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; 332 | }; 333 | F682F2F5203DB10B001DDD7C /* Carthage */ = { 334 | isa = PBXShellScriptBuildPhase; 335 | buildActionMask = 2147483647; 336 | files = ( 337 | ); 338 | inputPaths = ( 339 | "$(SRCROOT)/Carthage/Build/iOS/RxCocoa.framework", 340 | "$(SRCROOT)/Carthage/Build/iOS/RxGesture.framework", 341 | "$(SRCROOT)/Carthage/Build/iOS/RxSwift.framework", 342 | ); 343 | name = Carthage; 344 | outputPaths = ( 345 | ); 346 | runOnlyForDeploymentPostprocessing = 0; 347 | shellPath = /bin/sh; 348 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 349 | }; 350 | /* End PBXShellScriptBuildPhase section */ 351 | 352 | /* Begin PBXSourcesBuildPhase section */ 353 | F615F57C1FDEDD03007762A3 /* Sources */ = { 354 | isa = PBXSourcesBuildPhase; 355 | buildActionMask = 2147483647; 356 | files = ( 357 | F615F5851FDEDD03007762A3 /* ViewController.swift in Sources */, 358 | F615F5831FDEDD03007762A3 /* AppDelegate.swift in Sources */, 359 | F615F5931FDEDFE9007762A3 /* View.swift in Sources */, 360 | ); 361 | runOnlyForDeploymentPostprocessing = 0; 362 | }; 363 | F6DCAC4D1FDCC54700234666 /* Sources */ = { 364 | isa = PBXSourcesBuildPhase; 365 | buildActionMask = 2147483647; 366 | files = ( 367 | F6DCAC721FDCC5D300234666 /* TransformableView.swift in Sources */, 368 | F617AA57201AAC7600985E34 /* BorderType.swift in Sources */, 369 | F6DCAC711FDCC5D300234666 /* DragableView.swift in Sources */, 370 | F6DCAC731FDCC5D300234666 /* ResizableView.swift in Sources */, 371 | F617AA59201AB81F00985E34 /* UIView.swift in Sources */, 372 | F6DCAC701FDCC5D300234666 /* RotatableView.swift in Sources */, 373 | ); 374 | runOnlyForDeploymentPostprocessing = 0; 375 | }; 376 | F6DCAC571FDCC54700234666 /* Sources */ = { 377 | isa = PBXSourcesBuildPhase; 378 | buildActionMask = 2147483647; 379 | files = ( 380 | ); 381 | runOnlyForDeploymentPostprocessing = 0; 382 | }; 383 | /* End PBXSourcesBuildPhase section */ 384 | 385 | /* Begin PBXTargetDependency section */ 386 | F6DCAC5E1FDCC54700234666 /* PBXTargetDependency */ = { 387 | isa = PBXTargetDependency; 388 | target = F6DCAC511FDCC54700234666 /* TransformableView */; 389 | targetProxy = F6DCAC5D1FDCC54700234666 /* PBXContainerItemProxy */; 390 | }; 391 | /* End PBXTargetDependency section */ 392 | 393 | /* Begin PBXVariantGroup section */ 394 | F615F5861FDEDD03007762A3 /* Main.storyboard */ = { 395 | isa = PBXVariantGroup; 396 | children = ( 397 | F615F5871FDEDD03007762A3 /* Base */, 398 | ); 399 | name = Main.storyboard; 400 | sourceTree = ""; 401 | }; 402 | F615F58B1FDEDD03007762A3 /* LaunchScreen.storyboard */ = { 403 | isa = PBXVariantGroup; 404 | children = ( 405 | F615F58C1FDEDD03007762A3 /* Base */, 406 | ); 407 | name = LaunchScreen.storyboard; 408 | sourceTree = ""; 409 | }; 410 | /* End PBXVariantGroup section */ 411 | 412 | /* Begin XCBuildConfiguration section */ 413 | F615F58F1FDEDD03007762A3 /* Debug */ = { 414 | isa = XCBuildConfiguration; 415 | buildSettings = { 416 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 417 | CODE_SIGN_STYLE = Automatic; 418 | DEVELOPMENT_TEAM = MDX989Y6D8; 419 | FRAMEWORK_SEARCH_PATHS = ( 420 | "$(inherited)", 421 | "$(PROJECT_DIR)/Carthage/Build/iOS", 422 | ); 423 | INFOPLIST_FILE = TransformableViewDemo/Info.plist; 424 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 425 | PRODUCT_BUNDLE_IDENTIFIER = com.rep.TransformableViewDemo; 426 | PRODUCT_NAME = "$(TARGET_NAME)"; 427 | SWIFT_VERSION = 4.0; 428 | TARGETED_DEVICE_FAMILY = "1,2"; 429 | }; 430 | name = Debug; 431 | }; 432 | F615F5901FDEDD03007762A3 /* Release */ = { 433 | isa = XCBuildConfiguration; 434 | buildSettings = { 435 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 436 | CODE_SIGN_STYLE = Automatic; 437 | DEVELOPMENT_TEAM = MDX989Y6D8; 438 | FRAMEWORK_SEARCH_PATHS = ( 439 | "$(inherited)", 440 | "$(PROJECT_DIR)/Carthage/Build/iOS", 441 | ); 442 | INFOPLIST_FILE = TransformableViewDemo/Info.plist; 443 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 444 | PRODUCT_BUNDLE_IDENTIFIER = com.rep.TransformableViewDemo; 445 | PRODUCT_NAME = "$(TARGET_NAME)"; 446 | SWIFT_VERSION = 4.0; 447 | TARGETED_DEVICE_FAMILY = "1,2"; 448 | }; 449 | name = Release; 450 | }; 451 | F6DCAC641FDCC54700234666 /* Debug */ = { 452 | isa = XCBuildConfiguration; 453 | buildSettings = { 454 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 455 | ALWAYS_SEARCH_USER_PATHS = NO; 456 | CLANG_ANALYZER_NONNULL = YES; 457 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 458 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 459 | CLANG_CXX_LIBRARY = "libc++"; 460 | CLANG_ENABLE_MODULES = YES; 461 | CLANG_ENABLE_OBJC_ARC = YES; 462 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 463 | CLANG_WARN_BOOL_CONVERSION = YES; 464 | CLANG_WARN_COMMA = YES; 465 | CLANG_WARN_CONSTANT_CONVERSION = YES; 466 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 467 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 468 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 469 | CLANG_WARN_EMPTY_BODY = YES; 470 | CLANG_WARN_ENUM_CONVERSION = YES; 471 | CLANG_WARN_INFINITE_RECURSION = YES; 472 | CLANG_WARN_INT_CONVERSION = YES; 473 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 474 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 475 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 476 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 477 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 478 | CLANG_WARN_STRICT_PROTOTYPES = YES; 479 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 480 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 481 | CLANG_WARN_UNREACHABLE_CODE = YES; 482 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 483 | CODE_SIGN_IDENTITY = "iPhone Developer"; 484 | COPY_PHASE_STRIP = NO; 485 | CURRENT_PROJECT_VERSION = 1; 486 | DEBUG_INFORMATION_FORMAT = dwarf; 487 | ENABLE_STRICT_OBJC_MSGSEND = YES; 488 | ENABLE_TESTABILITY = YES; 489 | GCC_C_LANGUAGE_STANDARD = gnu11; 490 | GCC_DYNAMIC_NO_PIC = NO; 491 | GCC_NO_COMMON_BLOCKS = YES; 492 | GCC_OPTIMIZATION_LEVEL = 0; 493 | GCC_PREPROCESSOR_DEFINITIONS = ( 494 | "DEBUG=1", 495 | "$(inherited)", 496 | ); 497 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 498 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 499 | GCC_WARN_UNDECLARED_SELECTOR = YES; 500 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 501 | GCC_WARN_UNUSED_FUNCTION = YES; 502 | GCC_WARN_UNUSED_VARIABLE = YES; 503 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 504 | MTL_ENABLE_DEBUG_INFO = YES; 505 | ONLY_ACTIVE_ARCH = YES; 506 | SDKROOT = iphoneos; 507 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 508 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 509 | VERSIONING_SYSTEM = "apple-generic"; 510 | VERSION_INFO_PREFIX = ""; 511 | }; 512 | name = Debug; 513 | }; 514 | F6DCAC651FDCC54700234666 /* Release */ = { 515 | isa = XCBuildConfiguration; 516 | buildSettings = { 517 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 518 | ALWAYS_SEARCH_USER_PATHS = NO; 519 | CLANG_ANALYZER_NONNULL = YES; 520 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 521 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 522 | CLANG_CXX_LIBRARY = "libc++"; 523 | CLANG_ENABLE_MODULES = YES; 524 | CLANG_ENABLE_OBJC_ARC = YES; 525 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 526 | CLANG_WARN_BOOL_CONVERSION = YES; 527 | CLANG_WARN_COMMA = YES; 528 | CLANG_WARN_CONSTANT_CONVERSION = YES; 529 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 530 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 531 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 532 | CLANG_WARN_EMPTY_BODY = YES; 533 | CLANG_WARN_ENUM_CONVERSION = YES; 534 | CLANG_WARN_INFINITE_RECURSION = YES; 535 | CLANG_WARN_INT_CONVERSION = YES; 536 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 537 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 538 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 539 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 540 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 541 | CLANG_WARN_STRICT_PROTOTYPES = YES; 542 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 543 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 544 | CLANG_WARN_UNREACHABLE_CODE = YES; 545 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 546 | CODE_SIGN_IDENTITY = "iPhone Developer"; 547 | COPY_PHASE_STRIP = NO; 548 | CURRENT_PROJECT_VERSION = 1; 549 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 550 | ENABLE_NS_ASSERTIONS = NO; 551 | ENABLE_STRICT_OBJC_MSGSEND = YES; 552 | GCC_C_LANGUAGE_STANDARD = gnu11; 553 | GCC_NO_COMMON_BLOCKS = YES; 554 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 555 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 556 | GCC_WARN_UNDECLARED_SELECTOR = YES; 557 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 558 | GCC_WARN_UNUSED_FUNCTION = YES; 559 | GCC_WARN_UNUSED_VARIABLE = YES; 560 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 561 | MTL_ENABLE_DEBUG_INFO = NO; 562 | SDKROOT = iphoneos; 563 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 564 | VALIDATE_PRODUCT = YES; 565 | VERSIONING_SYSTEM = "apple-generic"; 566 | VERSION_INFO_PREFIX = ""; 567 | }; 568 | name = Release; 569 | }; 570 | F6DCAC671FDCC54700234666 /* Debug */ = { 571 | isa = XCBuildConfiguration; 572 | buildSettings = { 573 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 574 | CLANG_ENABLE_MODULES = YES; 575 | CODE_SIGN_IDENTITY = ""; 576 | CODE_SIGN_STYLE = Automatic; 577 | DEFINES_MODULE = YES; 578 | DEVELOPMENT_TEAM = ""; 579 | DYLIB_COMPATIBILITY_VERSION = 1; 580 | DYLIB_CURRENT_VERSION = 1; 581 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 582 | FRAMEWORK_SEARCH_PATHS = ( 583 | "$(inherited)", 584 | "$(PROJECT_DIR)/Carthage/Build/iOS", 585 | ); 586 | INFOPLIST_FILE = TransformableView/Info.plist; 587 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 588 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 589 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 590 | PRODUCT_BUNDLE_IDENTIFIER = com.rep.TransformableView; 591 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 592 | PROVISIONING_PROFILE_SPECIFIER = ""; 593 | SKIP_INSTALL = YES; 594 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 595 | SWIFT_VERSION = 4.2; 596 | TARGETED_DEVICE_FAMILY = "1,2"; 597 | }; 598 | name = Debug; 599 | }; 600 | F6DCAC681FDCC54700234666 /* Release */ = { 601 | isa = XCBuildConfiguration; 602 | buildSettings = { 603 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 604 | CLANG_ENABLE_MODULES = YES; 605 | CODE_SIGN_IDENTITY = ""; 606 | CODE_SIGN_STYLE = Automatic; 607 | DEFINES_MODULE = YES; 608 | DEVELOPMENT_TEAM = ""; 609 | DYLIB_COMPATIBILITY_VERSION = 1; 610 | DYLIB_CURRENT_VERSION = 1; 611 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 612 | FRAMEWORK_SEARCH_PATHS = ( 613 | "$(inherited)", 614 | "$(PROJECT_DIR)/Carthage/Build/iOS", 615 | ); 616 | INFOPLIST_FILE = TransformableView/Info.plist; 617 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 618 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 619 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 620 | PRODUCT_BUNDLE_IDENTIFIER = com.rep.TransformableView; 621 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 622 | PROVISIONING_PROFILE_SPECIFIER = ""; 623 | SKIP_INSTALL = YES; 624 | SWIFT_VERSION = 4.2; 625 | TARGETED_DEVICE_FAMILY = "1,2"; 626 | }; 627 | name = Release; 628 | }; 629 | F6DCAC6A1FDCC54700234666 /* Debug */ = { 630 | isa = XCBuildConfiguration; 631 | buildSettings = { 632 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 633 | CODE_SIGN_STYLE = Automatic; 634 | DEVELOPMENT_TEAM = MDX989Y6D8; 635 | INFOPLIST_FILE = TransformableViewTests/Info.plist; 636 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 637 | PRODUCT_BUNDLE_IDENTIFIER = com.rep.TransformableViewTests; 638 | PRODUCT_NAME = "$(TARGET_NAME)"; 639 | SWIFT_VERSION = 4.0; 640 | TARGETED_DEVICE_FAMILY = "1,2"; 641 | }; 642 | name = Debug; 643 | }; 644 | F6DCAC6B1FDCC54700234666 /* Release */ = { 645 | isa = XCBuildConfiguration; 646 | buildSettings = { 647 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 648 | CODE_SIGN_STYLE = Automatic; 649 | DEVELOPMENT_TEAM = MDX989Y6D8; 650 | INFOPLIST_FILE = TransformableViewTests/Info.plist; 651 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 652 | PRODUCT_BUNDLE_IDENTIFIER = com.rep.TransformableViewTests; 653 | PRODUCT_NAME = "$(TARGET_NAME)"; 654 | SWIFT_VERSION = 4.0; 655 | TARGETED_DEVICE_FAMILY = "1,2"; 656 | }; 657 | name = Release; 658 | }; 659 | /* End XCBuildConfiguration section */ 660 | 661 | /* Begin XCConfigurationList section */ 662 | F615F5911FDEDD03007762A3 /* Build configuration list for PBXNativeTarget "TransformableViewDemo" */ = { 663 | isa = XCConfigurationList; 664 | buildConfigurations = ( 665 | F615F58F1FDEDD03007762A3 /* Debug */, 666 | F615F5901FDEDD03007762A3 /* Release */, 667 | ); 668 | defaultConfigurationIsVisible = 0; 669 | defaultConfigurationName = Release; 670 | }; 671 | F6DCAC4C1FDCC54700234666 /* Build configuration list for PBXProject "TransformableView" */ = { 672 | isa = XCConfigurationList; 673 | buildConfigurations = ( 674 | F6DCAC641FDCC54700234666 /* Debug */, 675 | F6DCAC651FDCC54700234666 /* Release */, 676 | ); 677 | defaultConfigurationIsVisible = 0; 678 | defaultConfigurationName = Release; 679 | }; 680 | F6DCAC661FDCC54700234666 /* Build configuration list for PBXNativeTarget "TransformableView" */ = { 681 | isa = XCConfigurationList; 682 | buildConfigurations = ( 683 | F6DCAC671FDCC54700234666 /* Debug */, 684 | F6DCAC681FDCC54700234666 /* Release */, 685 | ); 686 | defaultConfigurationIsVisible = 0; 687 | defaultConfigurationName = Release; 688 | }; 689 | F6DCAC691FDCC54700234666 /* Build configuration list for PBXNativeTarget "TransformableViewTests" */ = { 690 | isa = XCConfigurationList; 691 | buildConfigurations = ( 692 | F6DCAC6A1FDCC54700234666 /* Debug */, 693 | F6DCAC6B1FDCC54700234666 /* Release */, 694 | ); 695 | defaultConfigurationIsVisible = 0; 696 | defaultConfigurationName = Release; 697 | }; 698 | /* End XCConfigurationList section */ 699 | }; 700 | rootObject = F6DCAC491FDCC54700234666 /* Project object */; 701 | } 702 | -------------------------------------------------------------------------------- /TransformableView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TransformableView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TransformableView.xcodeproj/xcshareddata/xcschemes/TransformableView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /TransformableView/BorderType.swift: -------------------------------------------------------------------------------- 1 | public enum BorderType { 2 | case none 3 | case border(width: CGFloat, color: UIColor) 4 | 5 | var borderColor: UIColor? { 6 | switch self { 7 | case .none: 8 | return nil 9 | case .border(_, let color): 10 | return color 11 | } 12 | } 13 | 14 | var borderWidth: CGFloat { 15 | switch self { 16 | case .none: 17 | return 0 18 | case .border(let width, _): 19 | return width 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TransformableView/DragableView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import RxGesture 3 | import RxSwift 4 | 5 | public protocol DraggableView { 6 | var disposeBag: DisposeBag { get } 7 | var borderType: BorderType { get } 8 | 9 | var minX: CGFloat? { get } 10 | var maxX: CGFloat? { get } 11 | var minY: CGFloat? { get } 12 | var maxY: CGFloat? { get } 13 | 14 | func addDragGestrueHandling() 15 | 16 | func willStartDragging() 17 | func didUpdate(frame: CGRect) 18 | } 19 | 20 | extension DraggableView where Self: UIView { 21 | public func addDragGestrueHandling() { 22 | layer.borderColor = borderType.borderColor?.cgColor 23 | 24 | rx 25 | .panGesture() 26 | .subscribe( 27 | onNext: { [weak self] panGestureRecognizer in 28 | guard let strongSelf = self else { return } 29 | 30 | switch panGestureRecognizer.state { 31 | case .began: 32 | strongSelf.willStartDragging() 33 | 34 | strongSelf.layer.borderWidth = strongSelf.borderType.borderWidth 35 | case .changed: 36 | var translation = panGestureRecognizer.translation(in: strongSelf.superview) 37 | 38 | if let minX = strongSelf.minX, strongSelf.frame.origin.x + translation.x < minX { 39 | translation.x = minX - strongSelf.frame.origin.x 40 | } else if let maxX = strongSelf.maxX, strongSelf.frame.origin.x + translation.x > maxX { 41 | translation.x = maxX - strongSelf.frame.origin.x 42 | } 43 | 44 | if let minY = strongSelf.minY, strongSelf.frame.origin.y + translation.y < minY { 45 | translation.y = minY - strongSelf.frame.origin.y 46 | } else if let maxY = strongSelf.maxY, strongSelf.frame.origin.y + translation.y > maxY { 47 | translation.y = maxY - strongSelf.frame.origin.y 48 | } 49 | 50 | strongSelf.transform = strongSelf 51 | .transform 52 | .concatenating(CGAffineTransform(translationX: translation.x, y: translation.y)) 53 | 54 | panGestureRecognizer.setTranslation(.zero, in: strongSelf.superview) 55 | case .ended: 56 | strongSelf.didUpdate(frame: strongSelf.frame) 57 | strongSelf.layer.borderWidth = 0 58 | default: 59 | break 60 | } 61 | } 62 | ).disposed(by: disposeBag) 63 | } 64 | 65 | func willStartDragging() {} 66 | func didUpdate(frame: CGRect) {} 67 | } 68 | -------------------------------------------------------------------------------- /TransformableView/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /TransformableView/ResizableView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import RxGesture 3 | import RxSwift 4 | 5 | public protocol ResizableView { 6 | var disposeBag: DisposeBag { get } 7 | var scale: CGFloat { get set } 8 | var borderType: BorderType { get } 9 | 10 | var minScale: CGFloat? { get } 11 | var maxScale: CGFloat? { get } 12 | 13 | func addScaleGestrueHandling() 14 | 15 | func updateDidBegin() 16 | func didUpdate(frame: CGRect) 17 | } 18 | 19 | extension ResizableView where Self: UIView { 20 | public func addScaleGestrueHandling() { 21 | layer.borderColor = borderType.borderColor?.cgColor 22 | 23 | rx 24 | .pinchGesture() 25 | .subscribe( 26 | onNext: { [weak self] pinchGestureRecognizer in 27 | guard var strongSelf = self else { return } 28 | 29 | switch pinchGestureRecognizer.state { 30 | case .began: 31 | strongSelf.layer.borderWidth = strongSelf.borderType.borderWidth 32 | 33 | strongSelf.updateDidBegin() 34 | case .changed: 35 | var scale = pinchGestureRecognizer.scale / strongSelf.scale 36 | 37 | let newScale = scale * strongSelf.verticalScale 38 | 39 | if let minScale = strongSelf.minScale, newScale < minScale { 40 | scale = minScale / strongSelf.verticalScale 41 | } else if let maxScale = strongSelf.maxScale, newScale > maxScale { 42 | scale = maxScale / strongSelf.verticalScale 43 | } 44 | 45 | strongSelf.transform = strongSelf 46 | .transform 47 | .scaledBy(x: scale, y: scale) 48 | 49 | strongSelf.scale = pinchGestureRecognizer.scale 50 | case .ended: 51 | strongSelf.scale = 1 52 | strongSelf.layer.borderWidth = 0 53 | 54 | strongSelf.didUpdate(frame: strongSelf.frame) 55 | default: 56 | break 57 | } 58 | } 59 | ).disposed(by: disposeBag) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /TransformableView/RotatableView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import RxGesture 3 | import RxSwift 4 | 5 | public protocol RotatableView { 6 | var disposeBag: DisposeBag { get } 7 | var borderType: BorderType { get } 8 | 9 | func addRotateGestrueHandling() 10 | func didUpdate(rotation: CGFloat) 11 | } 12 | 13 | extension RotatableView where Self: UIView { 14 | public func addRotateGestrueHandling() { 15 | layer.borderColor = borderType.borderColor?.cgColor 16 | 17 | rx 18 | .rotationGesture() 19 | .subscribe( 20 | onNext: { [weak self] rotationGestureRecognizer in 21 | guard let strongSelf = self else { return } 22 | 23 | switch rotationGestureRecognizer.state { 24 | case .began: 25 | strongSelf.layer.borderWidth = strongSelf.borderType.borderWidth 26 | case .changed: 27 | let x = strongSelf.layer.bounds.minX 28 | let y = strongSelf.layer.bounds.minY 29 | 30 | strongSelf.transform = strongSelf 31 | .transform 32 | .translatedBy(x: x, y: y) 33 | .rotated(by: rotationGestureRecognizer.rotation) 34 | .translatedBy(x: -x, y: -y) 35 | 36 | rotationGestureRecognizer.rotation = 0 37 | case .ended: 38 | strongSelf.layer.borderWidth = 0 39 | 40 | strongSelf.didUpdate( 41 | rotation: atan2(strongSelf.transform.b, strongSelf.transform.a) 42 | ) 43 | default: 44 | break 45 | } 46 | } 47 | ).disposed(by: disposeBag) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /TransformableView/TransformableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TransformableView.h 3 | // TransformableView 4 | // 5 | // Created by Ivan Rep on 10/12/2017. 6 | // Copyright © 2017 Rep. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for TransformableView. 12 | FOUNDATION_EXPORT double TransformableViewVersionNumber; 13 | 14 | //! Project version string for TransformableView. 15 | FOUNDATION_EXPORT const unsigned char TransformableViewVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /TransformableView/TransformableView.swift: -------------------------------------------------------------------------------- 1 | public protocol TransformableView: DraggableView, ResizableView, RotatableView { 2 | func addGesturesHandling() 3 | } 4 | 5 | extension TransformableView { 6 | public func addGesturesHandling() { 7 | addDragGestrueHandling() 8 | addScaleGestrueHandling() 9 | addRotateGestrueHandling() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /TransformableView/UIView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIView { 4 | var horizontalScale: CGFloat { 5 | return pow( 6 | pow(transform.a, 2) + pow(transform.c, 2), 7 | 0.5 8 | ) 9 | } 10 | 11 | var verticalScale: CGFloat { 12 | return pow( 13 | pow(transform.b, 2) + pow(transform.d, 2), 14 | 0.5 15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /TransformableViewDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | 6 | var window: UIWindow? = UIWindow() 7 | 8 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 9 | window?.rootViewController = ViewController(nibName: nil, bundle: nil) 10 | window?.makeKeyAndVisible() 11 | 12 | return true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /TransformableViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /TransformableViewDemo/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 | -------------------------------------------------------------------------------- /TransformableViewDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /TransformableViewDemo/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /TransformableViewDemo/View.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import TransformableView 3 | import RxSwift 4 | 5 | class View: UIView, TransformableView { 6 | var minX: CGFloat? = 10 7 | var maxX: CGFloat? = 200 8 | var minY: CGFloat? = 10 9 | var maxY: CGFloat? = 400 10 | 11 | var minScale: CGFloat? = 0.5 12 | var maxScale: CGFloat? 13 | 14 | let disposeBag = DisposeBag() 15 | 16 | var scale: CGFloat = 1 17 | var borderType = BorderType.border(width: 10, color: .black) 18 | 19 | override init(frame: CGRect) { 20 | super.init(frame: frame) 21 | 22 | addGesturesHandling() 23 | } 24 | 25 | required init?(coder aDecoder: NSCoder) { 26 | super.init(coder: aDecoder) 27 | 28 | addGesturesHandling() 29 | } 30 | 31 | func didUpdate(frame: CGRect) { 32 | } 33 | 34 | func didUpdate(rotation: CGFloat) { 35 | } 36 | 37 | func updateDidBegin() { 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /TransformableViewDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class ViewController: UIViewController { 4 | lazy var transformableView: View = { 5 | let view = View(frame: CGRect(x: 100, y: 100, width: 100, height: 100)) 6 | 7 | view.backgroundColor = .blue 8 | view.isUserInteractionEnabled = true 9 | 10 | return view 11 | }() 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | view.backgroundColor = .white 17 | 18 | view.addSubview(transformableView) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /TransformableViewTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rep2/TransformableView/e6695f087ecc4ca746095ec3a845e23805b9f63c/demo.gif --------------------------------------------------------------------------------