├── .gitignore ├── .swift-version ├── Example ├── .gitignore ├── Example.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Example.xcworkspace │ └── contents.xcworkspacedata ├── Example │ ├── AppDelegate.swift │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── ExampleViewController.swift │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ └── Info.plist ├── Podfile └── Podfile.lock ├── LICENSE ├── README.md ├── Swiftly.podspec ├── Swiftly.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── Swiftly.xcscheme ├── Swiftly ├── Info.plist ├── Swiftly.h └── Swiftly.swift └── SwiftlyTests ├── Info.plist └── SwiftlyTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | *build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | 20 | #CocoaPods 21 | Pods 22 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /Example/.gitignore: -------------------------------------------------------------------------------- 1 | Pods 2 | 3 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2DA9AA831B3A0CD80084835F /* ExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA9AA821B3A0CD80084835F /* ExampleViewController.swift */; }; 11 | A9381B7EE2C132722A26D63A /* Pods_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B38BF979BC6E98E5044D90D /* Pods_Example.framework */; }; 12 | D8093AC51AB6551800282415 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8093AC41AB6551800282415 /* AppDelegate.swift */; }; 13 | D8093ACC1AB6551800282415 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D8093ACB1AB6551800282415 /* Images.xcassets */; }; 14 | D8093ACF1AB6551800282415 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8093ACD1AB6551800282415 /* LaunchScreen.xib */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXFileReference section */ 18 | 1B38BF979BC6E98E5044D90D /* Pods_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 19 | 2DA9AA821B3A0CD80084835F /* ExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleViewController.swift; sourceTree = ""; }; 20 | BEA3A014976F265BB0DE9865 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | D8093ABF1AB6551800282415 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | D8093AC31AB6551800282415 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 23 | D8093AC41AB6551800282415 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 24 | D8093ACB1AB6551800282415 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 25 | D8093ACE1AB6551800282415 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 26 | E0C7E0942F4D02C6AC5F9959 /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig"; sourceTree = ""; }; 27 | FF2ED946E2EBF558737F0729 /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = ""; }; 28 | /* End PBXFileReference section */ 29 | 30 | /* Begin PBXFrameworksBuildPhase section */ 31 | D8093ABC1AB6551800282415 /* Frameworks */ = { 32 | isa = PBXFrameworksBuildPhase; 33 | buildActionMask = 2147483647; 34 | files = ( 35 | A9381B7EE2C132722A26D63A /* Pods_Example.framework in Frameworks */, 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 58FB0DF7D21BFE8F993E02C4 /* Pods */ = { 43 | isa = PBXGroup; 44 | children = ( 45 | E0C7E0942F4D02C6AC5F9959 /* Pods-Example.debug.xcconfig */, 46 | FF2ED946E2EBF558737F0729 /* Pods-Example.release.xcconfig */, 47 | ); 48 | name = Pods; 49 | sourceTree = ""; 50 | }; 51 | B923C92507DC8B4308CB0E1C /* Frameworks */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | BEA3A014976F265BB0DE9865 /* Pods.framework */, 55 | 1B38BF979BC6E98E5044D90D /* Pods_Example.framework */, 56 | ); 57 | name = Frameworks; 58 | sourceTree = ""; 59 | }; 60 | D8093AB41AB6543A00282415 = { 61 | isa = PBXGroup; 62 | children = ( 63 | D8093AC11AB6551800282415 /* Example */, 64 | D8093AC01AB6551800282415 /* Products */, 65 | B923C92507DC8B4308CB0E1C /* Frameworks */, 66 | 58FB0DF7D21BFE8F993E02C4 /* Pods */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | D8093AC01AB6551800282415 /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | D8093ABF1AB6551800282415 /* Example.app */, 74 | ); 75 | name = Products; 76 | sourceTree = ""; 77 | }; 78 | D8093AC11AB6551800282415 /* Example */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | D8093AC41AB6551800282415 /* AppDelegate.swift */, 82 | 2DA9AA821B3A0CD80084835F /* ExampleViewController.swift */, 83 | D8093ACB1AB6551800282415 /* Images.xcassets */, 84 | D8093ACD1AB6551800282415 /* LaunchScreen.xib */, 85 | D8093AC21AB6551800282415 /* Supporting Files */, 86 | ); 87 | path = Example; 88 | sourceTree = ""; 89 | }; 90 | D8093AC21AB6551800282415 /* Supporting Files */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | D8093AC31AB6551800282415 /* Info.plist */, 94 | ); 95 | name = "Supporting Files"; 96 | sourceTree = ""; 97 | }; 98 | /* End PBXGroup section */ 99 | 100 | /* Begin PBXNativeTarget section */ 101 | D8093ABE1AB6551800282415 /* Example */ = { 102 | isa = PBXNativeTarget; 103 | buildConfigurationList = D8093AE01AB6551900282415 /* Build configuration list for PBXNativeTarget "Example" */; 104 | buildPhases = ( 105 | 3989CC769E780B195AC67ADB /* [CP] Check Pods Manifest.lock */, 106 | D8093ABB1AB6551800282415 /* Sources */, 107 | D8093ABC1AB6551800282415 /* Frameworks */, 108 | D8093ABD1AB6551800282415 /* Resources */, 109 | 6F77EE8946431C920EFB9009 /* [CP] Embed Pods Frameworks */, 110 | A9BD68359B1EE8AB2B6A2A0F /* [CP] Copy Pods Resources */, 111 | ); 112 | buildRules = ( 113 | ); 114 | dependencies = ( 115 | ); 116 | name = Example; 117 | productName = Example; 118 | productReference = D8093ABF1AB6551800282415 /* Example.app */; 119 | productType = "com.apple.product-type.application"; 120 | }; 121 | /* End PBXNativeTarget section */ 122 | 123 | /* Begin PBXProject section */ 124 | D8093AB51AB6543A00282415 /* Project object */ = { 125 | isa = PBXProject; 126 | attributes = { 127 | LastSwiftUpdateCheck = 0710; 128 | LastUpgradeCheck = 0630; 129 | TargetAttributes = { 130 | D8093ABE1AB6551800282415 = { 131 | CreatedOnToolsVersion = 6.3; 132 | DevelopmentTeam = B2W6572NYZ; 133 | LastSwiftMigration = 0800; 134 | }; 135 | }; 136 | }; 137 | buildConfigurationList = D8093AB81AB6543A00282415 /* Build configuration list for PBXProject "Example" */; 138 | compatibilityVersion = "Xcode 3.2"; 139 | developmentRegion = English; 140 | hasScannedForEncodings = 0; 141 | knownRegions = ( 142 | en, 143 | Base, 144 | ); 145 | mainGroup = D8093AB41AB6543A00282415; 146 | productRefGroup = D8093AC01AB6551800282415 /* Products */; 147 | projectDirPath = ""; 148 | projectRoot = ""; 149 | targets = ( 150 | D8093ABE1AB6551800282415 /* Example */, 151 | ); 152 | }; 153 | /* End PBXProject section */ 154 | 155 | /* Begin PBXResourcesBuildPhase section */ 156 | D8093ABD1AB6551800282415 /* Resources */ = { 157 | isa = PBXResourcesBuildPhase; 158 | buildActionMask = 2147483647; 159 | files = ( 160 | D8093ACF1AB6551800282415 /* LaunchScreen.xib in Resources */, 161 | D8093ACC1AB6551800282415 /* Images.xcassets in Resources */, 162 | ); 163 | runOnlyForDeploymentPostprocessing = 0; 164 | }; 165 | /* End PBXResourcesBuildPhase section */ 166 | 167 | /* Begin PBXShellScriptBuildPhase section */ 168 | 3989CC769E780B195AC67ADB /* [CP] Check Pods Manifest.lock */ = { 169 | isa = PBXShellScriptBuildPhase; 170 | buildActionMask = 2147483647; 171 | files = ( 172 | ); 173 | inputPaths = ( 174 | ); 175 | name = "[CP] Check Pods Manifest.lock"; 176 | outputPaths = ( 177 | ); 178 | runOnlyForDeploymentPostprocessing = 0; 179 | shellPath = /bin/sh; 180 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 181 | showEnvVarsInLog = 0; 182 | }; 183 | 6F77EE8946431C920EFB9009 /* [CP] Embed Pods Frameworks */ = { 184 | isa = PBXShellScriptBuildPhase; 185 | buildActionMask = 2147483647; 186 | files = ( 187 | ); 188 | inputPaths = ( 189 | ); 190 | name = "[CP] Embed Pods Frameworks"; 191 | outputPaths = ( 192 | ); 193 | runOnlyForDeploymentPostprocessing = 0; 194 | shellPath = /bin/sh; 195 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example/Pods-Example-frameworks.sh\"\n"; 196 | showEnvVarsInLog = 0; 197 | }; 198 | A9BD68359B1EE8AB2B6A2A0F /* [CP] Copy Pods Resources */ = { 199 | isa = PBXShellScriptBuildPhase; 200 | buildActionMask = 2147483647; 201 | files = ( 202 | ); 203 | inputPaths = ( 204 | ); 205 | name = "[CP] Copy Pods Resources"; 206 | outputPaths = ( 207 | ); 208 | runOnlyForDeploymentPostprocessing = 0; 209 | shellPath = /bin/sh; 210 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example/Pods-Example-resources.sh\"\n"; 211 | showEnvVarsInLog = 0; 212 | }; 213 | /* End PBXShellScriptBuildPhase section */ 214 | 215 | /* Begin PBXSourcesBuildPhase section */ 216 | D8093ABB1AB6551800282415 /* Sources */ = { 217 | isa = PBXSourcesBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | D8093AC51AB6551800282415 /* AppDelegate.swift in Sources */, 221 | 2DA9AA831B3A0CD80084835F /* ExampleViewController.swift in Sources */, 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | /* End PBXSourcesBuildPhase section */ 226 | 227 | /* Begin PBXVariantGroup section */ 228 | D8093ACD1AB6551800282415 /* LaunchScreen.xib */ = { 229 | isa = PBXVariantGroup; 230 | children = ( 231 | D8093ACE1AB6551800282415 /* Base */, 232 | ); 233 | name = LaunchScreen.xib; 234 | sourceTree = ""; 235 | }; 236 | /* End PBXVariantGroup section */ 237 | 238 | /* Begin XCBuildConfiguration section */ 239 | D8093AB91AB6543A00282415 /* Debug */ = { 240 | isa = XCBuildConfiguration; 241 | buildSettings = { 242 | SWIFT_VERSION = 3.0; 243 | }; 244 | name = Debug; 245 | }; 246 | D8093ABA1AB6543A00282415 /* Release */ = { 247 | isa = XCBuildConfiguration; 248 | buildSettings = { 249 | SWIFT_VERSION = 3.0; 250 | }; 251 | name = Release; 252 | }; 253 | D8093ADC1AB6551900282415 /* Debug */ = { 254 | isa = XCBuildConfiguration; 255 | baseConfigurationReference = E0C7E0942F4D02C6AC5F9959 /* Pods-Example.debug.xcconfig */; 256 | buildSettings = { 257 | ALWAYS_SEARCH_USER_PATHS = NO; 258 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 259 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 260 | CLANG_CXX_LIBRARY = "libc++"; 261 | CLANG_ENABLE_MODULES = YES; 262 | CLANG_ENABLE_OBJC_ARC = YES; 263 | CLANG_WARN_BOOL_CONVERSION = YES; 264 | CLANG_WARN_CONSTANT_CONVERSION = YES; 265 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 266 | CLANG_WARN_EMPTY_BODY = YES; 267 | CLANG_WARN_ENUM_CONVERSION = YES; 268 | CLANG_WARN_INT_CONVERSION = YES; 269 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 270 | CLANG_WARN_UNREACHABLE_CODE = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 273 | COPY_PHASE_STRIP = NO; 274 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 275 | DEVELOPMENT_TEAM = B2W6572NYZ; 276 | ENABLE_STRICT_OBJC_MSGSEND = YES; 277 | GCC_C_LANGUAGE_STANDARD = gnu99; 278 | GCC_DYNAMIC_NO_PIC = NO; 279 | GCC_NO_COMMON_BLOCKS = YES; 280 | GCC_OPTIMIZATION_LEVEL = 0; 281 | GCC_PREPROCESSOR_DEFINITIONS = ( 282 | "DEBUG=1", 283 | "$(inherited)", 284 | ); 285 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 286 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 287 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 288 | GCC_WARN_UNDECLARED_SELECTOR = YES; 289 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 290 | GCC_WARN_UNUSED_FUNCTION = YES; 291 | GCC_WARN_UNUSED_VARIABLE = YES; 292 | INFOPLIST_FILE = Example/Info.plist; 293 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 294 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 295 | MTL_ENABLE_DEBUG_INFO = YES; 296 | ONLY_ACTIVE_ARCH = YES; 297 | PRODUCT_NAME = "$(TARGET_NAME)"; 298 | SDKROOT = iphoneos; 299 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 300 | SWIFT_VERSION = 3.0; 301 | TARGETED_DEVICE_FAMILY = "1,2"; 302 | }; 303 | name = Debug; 304 | }; 305 | D8093ADD1AB6551900282415 /* Release */ = { 306 | isa = XCBuildConfiguration; 307 | baseConfigurationReference = FF2ED946E2EBF558737F0729 /* Pods-Example.release.xcconfig */; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 312 | CLANG_CXX_LIBRARY = "libc++"; 313 | CLANG_ENABLE_MODULES = YES; 314 | CLANG_ENABLE_OBJC_ARC = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_CONSTANT_CONVERSION = YES; 317 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 318 | CLANG_WARN_EMPTY_BODY = YES; 319 | CLANG_WARN_ENUM_CONVERSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 322 | CLANG_WARN_UNREACHABLE_CODE = YES; 323 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 324 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 325 | COPY_PHASE_STRIP = NO; 326 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 327 | DEVELOPMENT_TEAM = B2W6572NYZ; 328 | ENABLE_NS_ASSERTIONS = NO; 329 | ENABLE_STRICT_OBJC_MSGSEND = YES; 330 | GCC_C_LANGUAGE_STANDARD = gnu99; 331 | GCC_NO_COMMON_BLOCKS = YES; 332 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 333 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 334 | GCC_WARN_UNDECLARED_SELECTOR = YES; 335 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 336 | GCC_WARN_UNUSED_FUNCTION = YES; 337 | GCC_WARN_UNUSED_VARIABLE = YES; 338 | INFOPLIST_FILE = Example/Info.plist; 339 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 340 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 341 | MTL_ENABLE_DEBUG_INFO = NO; 342 | PRODUCT_NAME = "$(TARGET_NAME)"; 343 | SDKROOT = iphoneos; 344 | SWIFT_VERSION = 3.0; 345 | TARGETED_DEVICE_FAMILY = "1,2"; 346 | VALIDATE_PRODUCT = YES; 347 | }; 348 | name = Release; 349 | }; 350 | /* End XCBuildConfiguration section */ 351 | 352 | /* Begin XCConfigurationList section */ 353 | D8093AB81AB6543A00282415 /* Build configuration list for PBXProject "Example" */ = { 354 | isa = XCConfigurationList; 355 | buildConfigurations = ( 356 | D8093AB91AB6543A00282415 /* Debug */, 357 | D8093ABA1AB6543A00282415 /* Release */, 358 | ); 359 | defaultConfigurationIsVisible = 0; 360 | defaultConfigurationName = Release; 361 | }; 362 | D8093AE01AB6551900282415 /* Build configuration list for PBXNativeTarget "Example" */ = { 363 | isa = XCConfigurationList; 364 | buildConfigurations = ( 365 | D8093ADC1AB6551900282415 /* Debug */, 366 | D8093ADD1AB6551900282415 /* Release */, 367 | ); 368 | defaultConfigurationIsVisible = 0; 369 | defaultConfigurationName = Release; 370 | }; 371 | /* End XCConfigurationList section */ 372 | }; 373 | rootObject = D8093AB51AB6543A00282415 /* Project object */; 374 | } 375 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by Nora Trapp on 3/15/15. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | 19 | window = UIWindow(frame: UIScreen.main.bounds) 20 | 21 | window?.rootViewController = ExampleViewController() 22 | window?.makeKeyAndVisible() 23 | 24 | return true 25 | } 26 | 27 | } 28 | 29 | -------------------------------------------------------------------------------- /Example/Example/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/Example/ExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleViewController.swift 3 | // Example 4 | // 5 | // Created by Nora Trapp on 6/23/15. 6 | // 7 | // 8 | 9 | import UIKit 10 | import Swiftly 11 | 12 | class ExampleViewController: UIViewController { 13 | 14 | var twoColumnConstraints = [NSLayoutConstraint]() 15 | var threeColumnConstraints = [NSLayoutConstraint]() 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | // Create three views 21 | let view1 = UIView() 22 | view.addSubview(view1) 23 | view1.backgroundColor = .blue 24 | let view2 = UIView() 25 | view.addSubview(view2) 26 | view2.backgroundColor = .white 27 | let view3 = UIView() 28 | view.addSubview(view3) 29 | view3.backgroundColor = .purple 30 | 31 | // Create 3 column constraints, disabled for now 32 | threeColumnConstraints += view1.applyLayout(.vertical, .left + 5, .width / 3 - 7.5) 33 | threeColumnConstraints += view2.applyLayout(.vertical, .left == .right(view1) + 7.5, .width / 3 - 7.5) 34 | threeColumnConstraints += view3.applyLayout(.vertical, .left == .right(view2) + 7.5, .right - 5) 35 | 36 | NSLayoutConstraint.deactivate(threeColumnConstraints) 37 | 38 | // Layout two views in two vertical columns. Save the constraints for future modification. 39 | twoColumnConstraints += view1.applyLayout(.vertical, .left + 5, .width / 2 - 7.5) 40 | twoColumnConstraints += view2.applyLayout(.vertical, .left == .right(view1) + 7.5, .right - 5) 41 | 42 | // Third column off the right hand side 43 | twoColumnConstraints += view3.applyLayout(.vertical, .right) 44 | 45 | // Animate in the third column after 2 seconds 46 | let delayTime = DispatchTime.now() + Double(Int64(2 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) 47 | DispatchQueue.main.asyncAfter(deadline: delayTime) { [unowned self] in 48 | UIView.animate(withDuration: 0.5, animations: { 49 | NSLayoutConstraint.deactivate(self.twoColumnConstraints) 50 | NSLayoutConstraint.activate(self.threeColumnConstraints) 51 | self.view.layoutIfNeeded() 52 | }) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Example/Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.trappdesign.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationLandscapeLeft 34 | UIInterfaceOrientationLandscapeRight 35 | UIInterfaceOrientationPortrait 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | use_frameworks! 3 | 4 | target :Example do 5 | pod 'Swiftly', :path => '../' 6 | end 7 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Swiftly (0.1.0) 3 | 4 | DEPENDENCIES: 5 | - Swiftly (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | Swiftly: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | Swiftly: 92568c3f59bf031019cf83fb07b17d758f0bb137 13 | 14 | PODFILE CHECKSUM: d3728b59ebd101b754e070197a6df02e58c7a8a5 15 | 16 | COCOAPODS: 1.1.0.rc.2 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Nora Trapp 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swiftly 2 | 3 | [![Version](https://img.shields.io/cocoapods/v/Swiftly.svg?style=flat)](http://cocoadocs.org/docsets/Swiftly) 4 | [![License](https://img.shields.io/cocoapods/l/Swiftly.svg?style=flat)](http://cocoadocs.org/docsets/Swiftly) 5 | [![Platform](https://img.shields.io/cocoapods/p/Swiftly.svg?style=flat)](http://cocoadocs.org/docsets/Swiftly) 6 | 7 | Swiftly generate Auto Layout constraints. 8 | 9 | ## Usage 10 | 11 | To run the example project, simply run `pod try swiftly`. Alternatively, you can clone the repo and run the project in the example directory. 12 | 13 | All `UIView`s and `UILayoutGuide`s respond to the `applyLayout` method which takes a variadic list of `Swiftly` objects. Convenience initializers are available which pair with all of Apple's `NSLayoutAttribute` types. Common combinatorial layout types `flush`, `flushToMargins`, `vertical`, `horizontal`, `center`, and `size` are also available. 14 | 15 | ```swift 16 | view.applyLayout(.centerX, .vertical, .width * 0.5) 17 | ``` 18 | 19 | ## Installation 20 | 21 | ### Cocoapods 22 | 23 | Swiftly is available through [CocoaPods](https://cocoapods.org). To install 24 | it, simply add the following line to your Podfile: 25 | 26 | ```ruby 27 | pod "Swiftly" 28 | ``` 29 | 30 | ### Carthage 31 | 32 | Swiftly is available through [Carthage](Swiftly/Swiftly.swift). To install 33 | it, simply add the following line to your Cartfile: 34 | 35 | ```ogdl 36 | github "Imperiopolis/Swiftly" ~> 2.0 37 | ``` 38 | 39 | ### Swift Version 40 | 41 | Swiftly 2.0 and later require Swift 4.2. For older versions of Swift, please use the Swiftly 1.0 build. 42 | 43 | ## Custom Operators 44 | 45 | Operators can be used on `Swiftly` objects to produce modified layouts. The `==`, `<=`, `>=`, `+`, `-`, `*`, `~=`, and `/` operators are available. 46 | 47 | ```swift 48 | view.applyLayout(.centerX, .top + 20, .width * 0.5, .height == 200) 49 | ``` 50 | 51 | ## Setting Priority 52 | 53 | The priority of `Swiftly` objects may be configured. 54 | 55 | ```swift 56 | view.applyLayout(.centerY ~= UILayoutPriorityRequired) 57 | ``` 58 | 59 | ## View Relationships 60 | 61 | By default, layout types reference the views `superview`. To create a constraint relative to a sibling view pass that view as a paramter. 62 | 63 | ```swift 64 | view1.applyLayout(.left == .right(view2) + 5, .size(view2)) 65 | ``` 66 | 67 | ## Constraint Manipulation 68 | 69 | The `applyLayout` method returns an array of the generated `LayoutConstraint` objects, which can be used to later to easily disable or modify the generated constraints. 70 | 71 | ```swift 72 | let constraints = view.applyLayout(.size == 5, .center) 73 | NSLayoutConstraint.deactivate(constraints) 74 | 75 | ... 76 | 77 | NSLayoutConstraint.activate(constraints) 78 | 79 | ``` 80 | 81 | __Note:__ Any view which has `applyLayout` called on it will automatically set `translatesAutoresizingMaskIntoConstraints` to `false` and have the generated constraints added to its superview. 82 | 83 | ## Documentation 84 | 85 | Read the documentation [here](http://cocoadocs.org/docsets/Swiftly). 86 | 87 | ## About Swiftly 88 | 89 | Swiftly was created by [@Imperiopolis](https://twitter.com/Imperiopolis) and was intended as a lightweight version of [Cartography](https://github.com/robb/cartography) by [Robb Böhnke](https://github.com/robb). 90 | 91 | Swiftly is released under the MIT license. See LICENSE for details. 92 | -------------------------------------------------------------------------------- /Swiftly.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Swiftly" 3 | s.version = "2.0.0" 4 | s.summary = "Swiftly generate Auto Layout constraints." 5 | s.description = <<-DESC 6 | Swiftly generate Auto Layout constraints and interact with them with all of Apple's built in functions. 7 | DESC 8 | s.homepage = "https://github.com/imperiopolis/Swiftly" 9 | s.license = 'MIT' 10 | s.author = { "Imperiopolis" => "me@trappdesign.net" } 11 | s.source = { :git => "https://github.com/imperiopolis/Swiftly.git", :tag => s.version.to_s } 12 | s.social_media_url = 'https://twitter.com/imperiopolis' 13 | 14 | s.platform = :ios, '10.0' 15 | s.requires_arc = true 16 | 17 | s.source_files = 'Swiftly/*.swift' 18 | end 19 | -------------------------------------------------------------------------------- /Swiftly.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1E23B0251D8A39A800D2632D /* Swiftly.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E23B0231D8A39A800D2632D /* Swiftly.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 1EBD90501D8A39D400BC83ED /* Swiftly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EBD904F1D8A39D400BC83ED /* Swiftly.swift */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXFileReference section */ 15 | 1E23B0201D8A39A800D2632D /* Swiftly.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Swiftly.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 16 | 1E23B0231D8A39A800D2632D /* Swiftly.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Swiftly.h; sourceTree = ""; }; 17 | 1E23B0241D8A39A800D2632D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 18 | 1EBD904F1D8A39D400BC83ED /* Swiftly.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Swiftly.swift; sourceTree = ""; }; 19 | /* End PBXFileReference section */ 20 | 21 | /* Begin PBXFrameworksBuildPhase section */ 22 | 1E23B01C1D8A39A800D2632D /* Frameworks */ = { 23 | isa = PBXFrameworksBuildPhase; 24 | buildActionMask = 2147483647; 25 | files = ( 26 | ); 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXFrameworksBuildPhase section */ 30 | 31 | /* Begin PBXGroup section */ 32 | 1E23B0161D8A39A800D2632D = { 33 | isa = PBXGroup; 34 | children = ( 35 | 1E23B0221D8A39A800D2632D /* Swiftly */, 36 | 1E23B0211D8A39A800D2632D /* Products */, 37 | ); 38 | sourceTree = ""; 39 | }; 40 | 1E23B0211D8A39A800D2632D /* Products */ = { 41 | isa = PBXGroup; 42 | children = ( 43 | 1E23B0201D8A39A800D2632D /* Swiftly.framework */, 44 | ); 45 | name = Products; 46 | sourceTree = ""; 47 | }; 48 | 1E23B0221D8A39A800D2632D /* Swiftly */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | 1E23B0231D8A39A800D2632D /* Swiftly.h */, 52 | 1EBD904F1D8A39D400BC83ED /* Swiftly.swift */, 53 | 1E23B0241D8A39A800D2632D /* Info.plist */, 54 | ); 55 | path = Swiftly; 56 | sourceTree = ""; 57 | }; 58 | /* End PBXGroup section */ 59 | 60 | /* Begin PBXHeadersBuildPhase section */ 61 | 1E23B01D1D8A39A800D2632D /* Headers */ = { 62 | isa = PBXHeadersBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | 1E23B0251D8A39A800D2632D /* Swiftly.h in Headers */, 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXHeadersBuildPhase section */ 70 | 71 | /* Begin PBXNativeTarget section */ 72 | 1E23B01F1D8A39A800D2632D /* Swiftly */ = { 73 | isa = PBXNativeTarget; 74 | buildConfigurationList = 1E23B0281D8A39A800D2632D /* Build configuration list for PBXNativeTarget "Swiftly" */; 75 | buildPhases = ( 76 | 1E23B01B1D8A39A800D2632D /* Sources */, 77 | 1E23B01C1D8A39A800D2632D /* Frameworks */, 78 | 1E23B01D1D8A39A800D2632D /* Headers */, 79 | 1E23B01E1D8A39A800D2632D /* Resources */, 80 | ); 81 | buildRules = ( 82 | ); 83 | dependencies = ( 84 | ); 85 | name = Swiftly; 86 | productName = Swiftly; 87 | productReference = 1E23B0201D8A39A800D2632D /* Swiftly.framework */; 88 | productType = "com.apple.product-type.framework"; 89 | }; 90 | /* End PBXNativeTarget section */ 91 | 92 | /* Begin PBXProject section */ 93 | 1E23B0171D8A39A800D2632D /* Project object */ = { 94 | isa = PBXProject; 95 | attributes = { 96 | LastUpgradeCheck = 0800; 97 | ORGANIZATIONNAME = Swiftly; 98 | TargetAttributes = { 99 | 1E23B01F1D8A39A800D2632D = { 100 | CreatedOnToolsVersion = 8.0; 101 | LastSwiftMigration = 0800; 102 | ProvisioningStyle = Automatic; 103 | }; 104 | }; 105 | }; 106 | buildConfigurationList = 1E23B01A1D8A39A800D2632D /* Build configuration list for PBXProject "Swiftly" */; 107 | compatibilityVersion = "Xcode 3.2"; 108 | developmentRegion = English; 109 | hasScannedForEncodings = 0; 110 | knownRegions = ( 111 | en, 112 | ); 113 | mainGroup = 1E23B0161D8A39A800D2632D; 114 | productRefGroup = 1E23B0211D8A39A800D2632D /* Products */; 115 | projectDirPath = ""; 116 | projectRoot = ""; 117 | targets = ( 118 | 1E23B01F1D8A39A800D2632D /* Swiftly */, 119 | ); 120 | }; 121 | /* End PBXProject section */ 122 | 123 | /* Begin PBXResourcesBuildPhase section */ 124 | 1E23B01E1D8A39A800D2632D /* Resources */ = { 125 | isa = PBXResourcesBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | ); 129 | runOnlyForDeploymentPostprocessing = 0; 130 | }; 131 | /* End PBXResourcesBuildPhase section */ 132 | 133 | /* Begin PBXSourcesBuildPhase section */ 134 | 1E23B01B1D8A39A800D2632D /* Sources */ = { 135 | isa = PBXSourcesBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | 1EBD90501D8A39D400BC83ED /* Swiftly.swift in Sources */, 139 | ); 140 | runOnlyForDeploymentPostprocessing = 0; 141 | }; 142 | /* End PBXSourcesBuildPhase section */ 143 | 144 | /* Begin XCBuildConfiguration section */ 145 | 1E23B0261D8A39A800D2632D /* Debug */ = { 146 | isa = XCBuildConfiguration; 147 | buildSettings = { 148 | ALWAYS_SEARCH_USER_PATHS = NO; 149 | CLANG_ANALYZER_NONNULL = YES; 150 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 151 | CLANG_CXX_LIBRARY = "libc++"; 152 | CLANG_ENABLE_MODULES = YES; 153 | CLANG_ENABLE_OBJC_ARC = YES; 154 | CLANG_WARN_BOOL_CONVERSION = YES; 155 | CLANG_WARN_CONSTANT_CONVERSION = YES; 156 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 157 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 158 | CLANG_WARN_EMPTY_BODY = YES; 159 | CLANG_WARN_ENUM_CONVERSION = YES; 160 | CLANG_WARN_INFINITE_RECURSION = YES; 161 | CLANG_WARN_INT_CONVERSION = YES; 162 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 163 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 164 | CLANG_WARN_UNREACHABLE_CODE = YES; 165 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 166 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 167 | COPY_PHASE_STRIP = NO; 168 | CURRENT_PROJECT_VERSION = 1; 169 | DEBUG_INFORMATION_FORMAT = dwarf; 170 | ENABLE_STRICT_OBJC_MSGSEND = YES; 171 | ENABLE_TESTABILITY = YES; 172 | GCC_C_LANGUAGE_STANDARD = gnu99; 173 | GCC_DYNAMIC_NO_PIC = NO; 174 | GCC_NO_COMMON_BLOCKS = YES; 175 | GCC_OPTIMIZATION_LEVEL = 0; 176 | GCC_PREPROCESSOR_DEFINITIONS = ( 177 | "DEBUG=1", 178 | "$(inherited)", 179 | ); 180 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 181 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 182 | GCC_WARN_UNDECLARED_SELECTOR = YES; 183 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 184 | GCC_WARN_UNUSED_FUNCTION = YES; 185 | GCC_WARN_UNUSED_VARIABLE = YES; 186 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 187 | MTL_ENABLE_DEBUG_INFO = YES; 188 | ONLY_ACTIVE_ARCH = YES; 189 | SDKROOT = iphoneos; 190 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 191 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 192 | TARGETED_DEVICE_FAMILY = "1,2"; 193 | VERSIONING_SYSTEM = "apple-generic"; 194 | VERSION_INFO_PREFIX = ""; 195 | }; 196 | name = Debug; 197 | }; 198 | 1E23B0271D8A39A800D2632D /* Release */ = { 199 | isa = XCBuildConfiguration; 200 | buildSettings = { 201 | ALWAYS_SEARCH_USER_PATHS = NO; 202 | CLANG_ANALYZER_NONNULL = YES; 203 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 204 | CLANG_CXX_LIBRARY = "libc++"; 205 | CLANG_ENABLE_MODULES = YES; 206 | CLANG_ENABLE_OBJC_ARC = YES; 207 | CLANG_WARN_BOOL_CONVERSION = YES; 208 | CLANG_WARN_CONSTANT_CONVERSION = YES; 209 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 210 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 211 | CLANG_WARN_EMPTY_BODY = YES; 212 | CLANG_WARN_ENUM_CONVERSION = YES; 213 | CLANG_WARN_INFINITE_RECURSION = YES; 214 | CLANG_WARN_INT_CONVERSION = YES; 215 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 216 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 217 | CLANG_WARN_UNREACHABLE_CODE = YES; 218 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 219 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 220 | COPY_PHASE_STRIP = NO; 221 | CURRENT_PROJECT_VERSION = 1; 222 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 223 | ENABLE_NS_ASSERTIONS = NO; 224 | ENABLE_STRICT_OBJC_MSGSEND = YES; 225 | GCC_C_LANGUAGE_STANDARD = gnu99; 226 | GCC_NO_COMMON_BLOCKS = YES; 227 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 228 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 229 | GCC_WARN_UNDECLARED_SELECTOR = YES; 230 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 231 | GCC_WARN_UNUSED_FUNCTION = YES; 232 | GCC_WARN_UNUSED_VARIABLE = YES; 233 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 234 | MTL_ENABLE_DEBUG_INFO = NO; 235 | SDKROOT = iphoneos; 236 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 237 | TARGETED_DEVICE_FAMILY = "1,2"; 238 | VALIDATE_PRODUCT = YES; 239 | VERSIONING_SYSTEM = "apple-generic"; 240 | VERSION_INFO_PREFIX = ""; 241 | }; 242 | name = Release; 243 | }; 244 | 1E23B0291D8A39A800D2632D /* Debug */ = { 245 | isa = XCBuildConfiguration; 246 | buildSettings = { 247 | CLANG_ENABLE_MODULES = YES; 248 | CODE_SIGN_IDENTITY = ""; 249 | DEFINES_MODULE = YES; 250 | DYLIB_COMPATIBILITY_VERSION = 1; 251 | DYLIB_CURRENT_VERSION = 1; 252 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 253 | INFOPLIST_FILE = Swiftly/Info.plist; 254 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 255 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 256 | PRODUCT_BUNDLE_IDENTIFIER = com.trappdesign.Swiftly; 257 | PRODUCT_NAME = "$(TARGET_NAME)"; 258 | SKIP_INSTALL = YES; 259 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 260 | SWIFT_VERSION = 4.2; 261 | }; 262 | name = Debug; 263 | }; 264 | 1E23B02A1D8A39A800D2632D /* Release */ = { 265 | isa = XCBuildConfiguration; 266 | buildSettings = { 267 | CLANG_ENABLE_MODULES = YES; 268 | CODE_SIGN_IDENTITY = ""; 269 | DEFINES_MODULE = YES; 270 | DYLIB_COMPATIBILITY_VERSION = 1; 271 | DYLIB_CURRENT_VERSION = 1; 272 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 273 | INFOPLIST_FILE = Swiftly/Info.plist; 274 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 275 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 276 | PRODUCT_BUNDLE_IDENTIFIER = com.trappdesign.Swiftly; 277 | PRODUCT_NAME = "$(TARGET_NAME)"; 278 | SKIP_INSTALL = YES; 279 | SWIFT_VERSION = 4.2; 280 | }; 281 | name = Release; 282 | }; 283 | /* End XCBuildConfiguration section */ 284 | 285 | /* Begin XCConfigurationList section */ 286 | 1E23B01A1D8A39A800D2632D /* Build configuration list for PBXProject "Swiftly" */ = { 287 | isa = XCConfigurationList; 288 | buildConfigurations = ( 289 | 1E23B0261D8A39A800D2632D /* Debug */, 290 | 1E23B0271D8A39A800D2632D /* Release */, 291 | ); 292 | defaultConfigurationIsVisible = 0; 293 | defaultConfigurationName = Release; 294 | }; 295 | 1E23B0281D8A39A800D2632D /* Build configuration list for PBXNativeTarget "Swiftly" */ = { 296 | isa = XCConfigurationList; 297 | buildConfigurations = ( 298 | 1E23B0291D8A39A800D2632D /* Debug */, 299 | 1E23B02A1D8A39A800D2632D /* Release */, 300 | ); 301 | defaultConfigurationIsVisible = 0; 302 | defaultConfigurationName = Release; 303 | }; 304 | /* End XCConfigurationList section */ 305 | }; 306 | rootObject = 1E23B0171D8A39A800D2632D /* Project object */; 307 | } 308 | -------------------------------------------------------------------------------- /Swiftly.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Swiftly.xcodeproj/xcshareddata/xcschemes/Swiftly.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Swiftly/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Swiftly/Swiftly.h: -------------------------------------------------------------------------------- 1 | // 2 | // Swiftly.h 3 | // Swiftly 4 | // 5 | // Created by Nora Trapp on 6/23/15. 6 | // Copyright (c) 2015 Trapp Design. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Swiftly. 12 | FOUNDATION_EXPORT double SwiftlyVersionNumber; 13 | 14 | //! Project version string for Swiftly. 15 | FOUNDATION_EXPORT const unsigned char SwiftlyVersionString[]; 16 | -------------------------------------------------------------------------------- /Swiftly/Swiftly.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Swiftly.swift 3 | // Swiftly 4 | // 5 | // Created by Nora Trapp on 6/23/15. 6 | // Copyright (c) 2015 Trapp Design. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol Swiftlyable { 12 | 13 | var superview: UIView? { get } 14 | 15 | } 16 | 17 | extension UIView: Swiftlyable {} 18 | 19 | @available(iOS, introduced: 9.0) 20 | extension UILayoutGuide: Swiftlyable { 21 | 22 | public var superview: UIView? { 23 | return owningView 24 | } 25 | 26 | } 27 | 28 | public extension Swiftlyable { 29 | 30 | /** 31 | Create constraints from an array of Swiftly objects. 32 | 33 | - parameter layoutArray: The layout(s) to apply. 34 | 35 | - returns: An array of constraints that represent the applied layout. This can be used to dynamically enable / disable a given layout. 36 | */ 37 | internal func createLayout(layoutArray: [Swiftly]) -> [NSLayoutConstraint] { 38 | guard let superview = superview else { 39 | fatalError("You must assign a superview before applying a layout") 40 | } 41 | 42 | if let view = self as? UIView { 43 | view.translatesAutoresizingMaskIntoConstraints = false 44 | } 45 | 46 | let constraints = layoutArray.flatMap { l -> [NSLayoutConstraint] in 47 | let attributes: [NSLayoutConstraint.Attribute] 48 | if let attrs = l.attributes { 49 | attributes = attrs 50 | } else if let attr = l.attribute { 51 | attributes = [attr] 52 | } else { 53 | fatalError("You must define an attribute.") 54 | } 55 | 56 | let otherAttributes: [NSLayoutConstraint.Attribute] 57 | if let otherAttrs = l.otherAttributes { 58 | otherAttributes = otherAttrs 59 | } else if let otherAttr = l.otherAttribute { 60 | otherAttributes = [otherAttr] 61 | } else if let attr = l.attribute { 62 | otherAttributes = [attr] 63 | } else if let attrs = l.attributes { 64 | otherAttributes = attrs 65 | } else { 66 | otherAttributes = [] 67 | } 68 | 69 | return zip(attributes, otherAttributes).map { attr, otherAttr in 70 | // toItem should be nil when setting a fixed size 71 | let toItem = otherAttr == .notAnAttribute ? nil : (l.toItem ?? superview) 72 | 73 | let constraint = NSLayoutConstraint( 74 | item: self, 75 | attribute: attr, 76 | relatedBy: l.relatedBy ?? .equal, 77 | toItem: toItem, 78 | attribute: otherAttr, 79 | multiplier: l.multiplier, 80 | constant: l.constant) 81 | 82 | if let priority = l.priority { 83 | constraint.priority = priority 84 | } 85 | 86 | return constraint 87 | } 88 | } 89 | 90 | return constraints 91 | } 92 | 93 | /** 94 | Apply a variadic list of Swiftly objects to a `Swiftlyable`. This is appended to any existing constraints. 95 | 96 | - parameter layout: The layout(s) to apply. 97 | 98 | - returns: An array of constraints that represent the applied layout. This can be used to dynamically enable / disable a given layout. 99 | */ 100 | @discardableResult 101 | func applyLayout(_ layout: Swiftly...) -> [NSLayoutConstraint] { 102 | let constraints = createLayout(layoutArray: layout) 103 | NSLayoutConstraint.activate(constraints) 104 | return constraints 105 | } 106 | 107 | /** 108 | Apply a variadic list of Swiftly objects to a `Swiftlyable`. The constraints are not immediately applied. 109 | 110 | - parameter layout: The layout(s) to apply. 111 | 112 | - returns: An array of constraints that represent the applied layout. This can be used to dynamically enable / disable a given layout. 113 | */ 114 | func createLayout(_ layout: Swiftly...) -> [NSLayoutConstraint] { 115 | return createLayout(layoutArray: layout) 116 | } 117 | 118 | } 119 | 120 | public extension Array where Element : Swiftlyable { 121 | 122 | /** 123 | Apply an array of Swiftly objects to an array of `Swiftlyable`s. This is appended to any existing constraints. 124 | 125 | - parameter layoutArray: The layout(s) to apply. 126 | 127 | - returns: An array of constraints that represent the applied layout. This can be used to dynamically enable / disable a given layout. 128 | */ 129 | internal func createLayout(layoutArray: [Swiftly]) -> [NSLayoutConstraint] { 130 | return flatMap { return $0.createLayout(layoutArray: layoutArray) } 131 | } 132 | 133 | /** 134 | Apply a variadic list of Swiftly objects to an array of views. This is appended to any existing constraints. 135 | 136 | - parameter layout: The layout(s) to apply. 137 | 138 | - returns: An array of constraints that represent the applied layout. This can be used to dynamically enable / disable a given layout. 139 | */ 140 | @discardableResult 141 | func applyLayout(_ layout: Swiftly...) -> [NSLayoutConstraint] { 142 | let constraints = createLayout(layoutArray: layout) 143 | NSLayoutConstraint.activate(constraints) 144 | return constraints 145 | } 146 | 147 | /** 148 | Apply a variadic list of Swiftly objects to an array of views. The constraints are not immediately applied. 149 | 150 | - parameter layout: The layout(s) to apply. 151 | 152 | - returns: An array of constraints that represent the applied layout. This can be used to dynamically enable / disable a given layout. 153 | */ 154 | func createLayout(_ layout: Swiftly...) -> [NSLayoutConstraint] { 155 | return createLayout(layoutArray: layout) 156 | } 157 | 158 | } 159 | 160 | public extension Array where Element : UIView { 161 | 162 | /** 163 | Apply an array of Swiftly objects to an array of views. The constraints are not applied to the first view in the array (since it has no previous item). This is appended to any existing constraints. 164 | 165 | - parameter callback: A closure used to define the constraints. A previousView argument is passed to allow for distributing views. 166 | 167 | - returns: An array of constraints that represent the applied layout. This can be used to dynamically enable / disable a given layout. 168 | */ 169 | @discardableResult 170 | func applyLayoutWithPreviousView(_ callback: (_ previousView: UIView) -> [Swiftly]) -> [NSLayoutConstraint] { 171 | let constraints = createLayoutWithPreviousView(callback) 172 | NSLayoutConstraint.activate(constraints) 173 | return constraints 174 | } 175 | 176 | /** 177 | Apply an array of Swiftly objects to an array of views. The constraints are not applied to the first view in the array (since it has no previous item). The constraints are not applied immediately. 178 | 179 | - parameter callback: A closure used to define the constraints. A previousView argument is passed to allow for distributing views. 180 | 181 | - returns: An array of constraints that represent the applied layout. This can be used to dynamically enable / disable a given layout. 182 | */ 183 | func createLayoutWithPreviousView(_ callback: (_ previousView: UIView) -> [Swiftly]) -> [NSLayoutConstraint] { 184 | var constraints = [NSLayoutConstraint]() 185 | 186 | var previousView: UIView? 187 | for view in self { 188 | if let previousView = previousView { 189 | let swiftly = callback(previousView) 190 | constraints += (view as Swiftlyable).createLayout(layoutArray: swiftly) 191 | } 192 | 193 | previousView = view 194 | } 195 | 196 | return constraints 197 | } 198 | 199 | } 200 | 201 | /** 202 | * A struct representing a set of constraint attributes. Initializers are available to create all common constraint types. 203 | */ 204 | public struct Swiftly { 205 | /** 206 | A combined layout representing all sides of a `Swiftlyable`'s alignment rectangle. 207 | 208 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 209 | 210 | - returns: A Swiftly object representing the desired layout. 211 | */ 212 | public static func flush(_ item: Swiftlyable) -> Swiftly { 213 | return Swiftly(attributes: [.top, .left, .bottom, .right], toItem: item) 214 | } 215 | 216 | /// A combined layout representing all sides of a `Swiftlyable`'s alignment rectangle. 217 | public static var flush: Swiftly { 218 | return Swiftly(attributes: [.top, .left, .bottom, .right]) 219 | } 220 | 221 | /** 222 | A combined layout representing all sides of a `Swiftlyable`'s margin alignment rectangle. 223 | 224 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 225 | 226 | - returns: A Swiftly object representing the desired layout. 227 | */ 228 | public static func flushToMargins(_ item: Swiftlyable) -> Swiftly { 229 | return Swiftly(attributes: [.top, .left, .bottom, .right], toItem: item, otherAttributes: [.topMargin, .leftMargin, .bottomMargin, .rightMargin]) 230 | } 231 | 232 | /// A combined layout representing all sides of a `Swiftlyable`'s margin alignment rectangle. 233 | public static var flushToMargins: Swiftly { 234 | return Swiftly(attributes: [.top, .left, .bottom, .right], otherAttributes: [.topMargin, .leftMargin, .bottomMargin, .rightMargin]) 235 | } 236 | 237 | /** 238 | A combined layout representing the left and right sides of a `Swiftlyable`'s alignment rectangle. 239 | 240 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 241 | 242 | - returns: A Swiftly object representing the desired layout. 243 | */ 244 | public static func horizontal(_ item: Swiftlyable) -> Swiftly { 245 | return Swiftly(attributes: [.left, .right], toItem: item) 246 | } 247 | 248 | /// A combined layout representing the left and right sides of a `Swiftlyable`'s alignment rectangle. 249 | public static var horizontal: Swiftly { 250 | return Swiftly(attributes: [.left, .right]) 251 | } 252 | 253 | /** 254 | A combined layout representing the left and right sides of a `Swiftlyable`'s margin alignment rectangle. 255 | 256 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 257 | 258 | - returns: A Swiftly object representing the desired layout. 259 | */ 260 | public static func horizontalMargins(_ item: Swiftlyable) -> Swiftly { 261 | return Swiftly(attributes: [.left, .right], toItem: item, otherAttributes: [.leftMargin, .rightMargin]) 262 | } 263 | 264 | /// A combined layout representing the left and right sides of a `Swiftlyable`'s margin alignment rectangle. 265 | public static var horizontalMargins: Swiftly { 266 | return Swiftly(attributes: [.left, .right], otherAttributes: [.leftMargin, .rightMargin]) 267 | } 268 | 269 | /** 270 | A combined layout representing the top and bottom sides of a `Swiftlyable`'s alignment rectangle. 271 | 272 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 273 | 274 | - returns: A Swiftly object representing the desired layout. 275 | */ 276 | public static func vertical(_ item: Swiftlyable) -> Swiftly { 277 | return Swiftly(attributes: [.top, .bottom], toItem: item) 278 | } 279 | 280 | /// A combined layout representing the top and bottom sides of a `Swiftlyable`'s alignment rectangle. 281 | public static var vertical: Swiftly { 282 | return Swiftly(attributes: [.top, .bottom]) 283 | } 284 | 285 | /** 286 | A combined layout representing the top and bottom sides of a `Swiftlyable`'s margin alignment rectangle. 287 | 288 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 289 | 290 | - returns: A Swiftly object representing the desired layout. 291 | */ 292 | public static func verticalMargins(_ item: Swiftlyable) -> Swiftly { 293 | return Swiftly(attributes: [.top, .bottom], toItem: item, otherAttributes: [.topMargin, .bottomMargin]) 294 | } 295 | 296 | /// A combined layout representing the top and bottom sides of a `Swiftlyable`'s margin alignment rectangle. 297 | public static var verticalMargins: Swiftly { 298 | return Swiftly(attributes: [.top, .bottom], otherAttributes: [.topMargin, .bottomMargin]) 299 | } 300 | 301 | /** 302 | A combined layout representing the center along the x-axis and y-axis of a `Swiftlyable`'s alignment rectangle. 303 | 304 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 305 | 306 | - returns: A Swiftly object representing the desired layout. 307 | */ 308 | public static func center(_ item: Swiftlyable) -> Swiftly { 309 | return Swiftly(attributes: [.centerX, .centerY], toItem: item) 310 | } 311 | 312 | /// A combined layout representing the center along the x-axis and y-axis of a `Swiftlyable`'s alignment rectangle. 313 | public static var center: Swiftly { 314 | return Swiftly(attributes: [.centerX, .centerY]) 315 | } 316 | 317 | /** 318 | A combined layout representing the height and width of a `Swiftlyable`'s alignment rectangle. 319 | 320 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 321 | 322 | - returns: A Swiftly object representing the desired layout. 323 | */ 324 | public static func size(_ item: Swiftlyable) -> Swiftly { 325 | return Swiftly(attributes: [.height, .width], toItem: item) 326 | } 327 | 328 | /// A combined layout representing the height and width of a `Swiftlyable`'s alignment rectangle. 329 | public static var size: Swiftly { 330 | return Swiftly(attributes: [.height, .width]) 331 | } 332 | 333 | /** 334 | A layout representing the left side of a `Swiftlyable`'s alignment rectangle. 335 | 336 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 337 | 338 | - returns: A Swiftly object representing the desired layout. 339 | */ 340 | public static func left(_ item: Swiftlyable) -> Swiftly { 341 | return Swiftly(.left, toItem: item) 342 | } 343 | 344 | /// A layout representing the left side of a `Swiftlyable`'s alignment rectangle. 345 | public static var left: Swiftly { 346 | return Swiftly(.left) 347 | } 348 | 349 | /** 350 | A layout representing the right side of a `Swiftlyable`'s alignment rectangle. 351 | 352 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 353 | 354 | - returns: A Swiftly object representing the desired layout. 355 | */ 356 | public static func right(_ item: Swiftlyable) -> Swiftly { 357 | return Swiftly(.right, toItem: item) 358 | } 359 | 360 | /// A layout representing the right side of a `Swiftlyable`'s alignment rectangle. 361 | public static var right: Swiftly { 362 | return Swiftly(.right) 363 | } 364 | 365 | /** 366 | A layout representing the top side of a `Swiftlyable`'s alignment rectangle. 367 | 368 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 369 | 370 | - returns: A Swiftly object representing the desired layout. 371 | */ 372 | public static func top(_ item: Swiftlyable) -> Swiftly { 373 | return Swiftly(.top, toItem: item) 374 | } 375 | 376 | /// A layout representing the top side of a `Swiftlyable`'s alignment rectangle. 377 | public static var top: Swiftly { 378 | return Swiftly(.top) 379 | } 380 | 381 | /** 382 | A layout representing the bottom side of a `Swiftlyable`'s alignment rectangle. 383 | 384 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 385 | 386 | - returns: A Swiftly object representing the desired layout. 387 | */ 388 | public static func bottom(_ item: Swiftlyable) -> Swiftly { 389 | return Swiftly(.bottom, toItem: item) 390 | } 391 | 392 | /// A layout representing the bottom side of a `Swiftlyable`'s alignment rectangle. 393 | public static var bottom: Swiftly { 394 | return Swiftly(.bottom) 395 | } 396 | 397 | /** 398 | A layout representing the leading edge of a `Swiftlyable`'s alignment rectangle. 399 | 400 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 401 | 402 | - returns: A Swiftly object representing the desired layout. 403 | */ 404 | public static func leading(_ item: Swiftlyable) -> Swiftly { 405 | return Swiftly(.leading, toItem: item) 406 | } 407 | 408 | /// A layout representing the leading edge of a `Swiftlyable`'s alignment rectangle. 409 | public static var leading: Swiftly { 410 | return Swiftly(.leading) 411 | } 412 | 413 | /** 414 | A layout representing the trailing edge of a `Swiftlyable`'s alignment rectangle. 415 | 416 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 417 | 418 | - returns: A Swiftly object representing the desired layout. 419 | */ 420 | public static func trailing(_ item: Swiftlyable) -> Swiftly { 421 | return Swiftly(.trailing, toItem: item) 422 | } 423 | 424 | /// A layout representing the trailing edge of a `Swiftlyable`'s alignment rectangle. 425 | public static var trailing: Swiftly { 426 | return Swiftly(.trailing) 427 | } 428 | 429 | /** 430 | A layout representing the height of a `Swiftlyable`'s alignment rectangle. 431 | 432 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 433 | 434 | - returns: A Swiftly object representing the desired layout. 435 | */ 436 | public static func height(_ item: Swiftlyable) -> Swiftly { 437 | return Swiftly(.height, toItem: item) 438 | } 439 | 440 | /// A layout representing the height of a `Swiftlyable`'s alignment rectangle. 441 | public static var height: Swiftly { 442 | return Swiftly(.height) 443 | } 444 | 445 | /** 446 | A layout representing the width of a `Swiftlyable`'s alignment rectangle. 447 | 448 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 449 | 450 | - returns: A Swiftly object representing the desired layout. 451 | */ 452 | public static func width(_ item: Swiftlyable) -> Swiftly { 453 | return Swiftly(.width, toItem: item) 454 | } 455 | 456 | /// A layout representing the width of a `Swiftlyable`'s alignment rectangle. 457 | public static var width: Swiftly { 458 | return Swiftly(.width) 459 | } 460 | 461 | /** 462 | A layout representing the center along the x-axis of a `Swiftlyable`'s alignment rectangle. 463 | 464 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 465 | 466 | - returns: A Swiftly object representing the desired layout. 467 | */ 468 | public static func centerX(_ item: Swiftlyable) -> Swiftly { 469 | return Swiftly(.centerX, toItem: item) 470 | } 471 | 472 | /// A layout representing the center along the x-axis of a `Swiftlyable`'s alignment rectangle. 473 | public static var centerX: Swiftly { 474 | return Swiftly(.centerX) 475 | } 476 | 477 | /** 478 | A layout representing the center along the y-axis of a `Swiftlyable`'s alignment rectangle. 479 | 480 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 481 | 482 | - returns: A Swiftly object representing the desired layout. 483 | */ 484 | public static func centerY(_ item: Swiftlyable) -> Swiftly { 485 | return Swiftly(.centerY, toItem: item) 486 | } 487 | 488 | /// A layout representing the center along the y-axis of a `Swiftlyable`'s alignment rectangle. 489 | public static var centerY: Swiftly { 490 | return Swiftly(.centerY) 491 | } 492 | 493 | /** 494 | A layout representing the baseline of a `Swiftlyable`. 495 | 496 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 497 | 498 | - returns: A Swiftly object representing the desired layout. 499 | */ 500 | public static func baseline(_ item: Swiftlyable) -> Swiftly { 501 | return Swiftly(.lastBaseline, toItem: item) 502 | } 503 | 504 | /// A layout representing the baseline of a `Swiftlyable`. 505 | public static var baseline: Swiftly { 506 | return Swiftly(.lastBaseline) 507 | } 508 | 509 | /** 510 | A layout representing the top most baseline of a `Swiftlyable`. 511 | 512 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 513 | 514 | - returns: A Swiftly object representing the desired layout. 515 | */ 516 | @available(iOS, introduced: 8.0) 517 | public static func firstBaseline(_ item: Swiftlyable) -> Swiftly { 518 | return Swiftly(.firstBaseline, toItem: item) 519 | } 520 | 521 | /// A layout representing the top most baseline of a `Swiftlyable`. 522 | @available(iOS, introduced: 8.0) 523 | public static var firstBaseline: Swiftly { 524 | return Swiftly(.firstBaseline) 525 | } 526 | 527 | /** 528 | A layout representing the left margin of a `Swiftlyable`. 529 | 530 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 531 | 532 | - returns: A Swiftly object representing the desired layout. 533 | */ 534 | @available(iOS, introduced: 8.0) 535 | public static func leftMargin(_ item: Swiftlyable) -> Swiftly { 536 | return Swiftly(.leftMargin, toItem: item) 537 | } 538 | 539 | /// A layout representing the left margin of a `Swiftlyable`. 540 | @available(iOS, introduced: 8.0) 541 | public static var leftMargin: Swiftly { 542 | return Swiftly(.leftMargin) 543 | } 544 | 545 | /** 546 | A layout representing the right margin of a `Swiftlyable`. 547 | 548 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 549 | 550 | - returns: A Swiftly object representing the desired layout. 551 | */ 552 | @available(iOS, introduced: 8.0) 553 | public static func rightMargin(_ item: Swiftlyable) -> Swiftly { 554 | return Swiftly(.rightMargin, toItem: item) 555 | } 556 | 557 | /// A layout representing the right margin of a `Swiftlyable`. 558 | @available(iOS, introduced: 8.0) 559 | public static var rightMargin: Swiftly { 560 | return Swiftly(.rightMargin) 561 | } 562 | 563 | /** 564 | A layout representing the top margin of a `Swiftlyable`. 565 | 566 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 567 | 568 | - returns: A Swiftly object representing the desired layout. 569 | */ 570 | @available(iOS, introduced: 8.0) 571 | public static func topMargin(_ item: Swiftlyable) -> Swiftly { 572 | return Swiftly(.topMargin, toItem: item) 573 | } 574 | 575 | /// A layout representing the top margin of a `Swiftlyable`. 576 | @available(iOS, introduced: 8.0) 577 | public static var topMargin: Swiftly { 578 | return Swiftly(.topMargin) 579 | } 580 | 581 | /** 582 | A layout representing the bottom margin of a `Swiftlyable`. 583 | 584 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 585 | 586 | - returns: A Swiftly object representing the desired layout. 587 | */ 588 | @available(iOS, introduced: 8.0) 589 | public static func bottomMargin(_ item: Swiftlyable) -> Swiftly { 590 | return Swiftly(.bottomMargin, toItem: item) 591 | } 592 | 593 | /// A layout representing the bottom margin of a `Swiftlyable`. 594 | @available(iOS, introduced: 8.0) 595 | public static var bottomMargin: Swiftly { 596 | return Swiftly(.bottomMargin) 597 | } 598 | 599 | /** 600 | A layout representing the leading margin of a `Swiftlyable`. 601 | 602 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 603 | 604 | - returns: A Swiftly object representing the desired layout. 605 | */ 606 | @available(iOS, introduced: 8.0) 607 | public static func leadingMargin(_ item: Swiftlyable) -> Swiftly { 608 | return Swiftly(.leadingMargin, toItem: item) 609 | } 610 | 611 | /// A layout representing the leading margin of a `Swiftlyable`. 612 | @available(iOS, introduced: 8.0) 613 | public static var leadingMargin: Swiftly { 614 | return Swiftly(.leadingMargin) 615 | } 616 | 617 | /** 618 | A layout representing the trailing margin of a `Swiftlyable`. 619 | 620 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 621 | 622 | - returns: A Swiftly object representing the desired layout. 623 | */ 624 | @available(iOS, introduced: 8.0) 625 | public static func trailingMargin(_ item: Swiftlyable) -> Swiftly { 626 | return Swiftly(.trailingMargin, toItem: item) 627 | } 628 | 629 | /// A layout representing the trailing margin of a `Swiftlyable`. 630 | @available(iOS, introduced: 8.0) 631 | public static var trailingMargin: Swiftly { 632 | return Swiftly(.trailingMargin) 633 | } 634 | 635 | /** 636 | A layout representing the center along the x-axis between the left and right margins of a `Swiftlyable`. 637 | 638 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 639 | 640 | - returns: A Swiftly object representing the desired layout. 641 | */ 642 | @available(iOS, introduced: 8.0) 643 | public static func centerXWithinMargins(_ item: Swiftlyable) -> Swiftly { 644 | return Swiftly(.centerXWithinMargins, toItem: item) 645 | } 646 | 647 | /// A layout representing the center along the x-axis between the left and right margins of a `Swiftlyable`. 648 | @available(iOS, introduced: 8.0) 649 | public static var centerXWithinMargins: Swiftly { 650 | return Swiftly(.centerXWithinMargins) 651 | } 652 | 653 | /** 654 | A layout representing the center along the y-axis between the top and bottom margins of a `Swiftlyable`. 655 | 656 | - parameter item: The item that the property is representing. When nil, the layout is relative to the superview. 657 | 658 | - returns: A Swiftly object representing the desired layout. 659 | */ 660 | @available(iOS, introduced: 8.0) 661 | public static func centerYWithinMargins(_ item: Swiftlyable) -> Swiftly { 662 | return Swiftly(.centerYWithinMargins, toItem: item) 663 | } 664 | 665 | /// A layout representing the center along the y-axis between the top and bottom margins of a `Swiftlyable`. 666 | @available(iOS, introduced: 8.0) 667 | public static var centerYWithinMargins: Swiftly { 668 | return Swiftly(.centerYWithinMargins) 669 | } 670 | 671 | let attribute: NSLayoutConstraint.Attribute? 672 | let attributes: [NSLayoutConstraint.Attribute]? 673 | var relatedBy: NSLayoutConstraint.Relation? 674 | var otherAttribute: NSLayoutConstraint.Attribute? 675 | var otherAttributes: [NSLayoutConstraint.Attribute]? 676 | var multiplier: CGFloat 677 | var constant: CGFloat 678 | var toItem: Swiftlyable? 679 | var priority: UILayoutPriority? 680 | 681 | init(_ a: NSLayoutConstraint.Attribute? = nil, attributes atts: [NSLayoutConstraint.Attribute]? = nil, relatedBy r: NSLayoutConstraint.Relation? = .equal, toItem ti: Swiftlyable? = nil, otherAttribute oa: NSLayoutConstraint.Attribute? = nil, otherAttributes otherAtts: [NSLayoutConstraint.Attribute]? = nil, multiplier m: CGFloat = 1, constant c: CGFloat = 0) { 682 | attribute = a 683 | attributes = atts 684 | relatedBy = r 685 | toItem = ti 686 | otherAttribute = oa 687 | otherAttributes = otherAtts 688 | multiplier = m 689 | constant = c 690 | } 691 | } 692 | 693 | /** 694 | Assign a property of a `Swiftlyable` equal to that property on another `Swiftlyable`. Useful for things such as settings the top of a view equal to the top of another view. 695 | 696 | - parameter left: Layout property to assign 697 | - parameter right: View to equal 698 | 699 | - returns: A Swiftly object representing the desired constraint 700 | */ 701 | public func ==(left: Swiftly, right: Swiftlyable) -> Swiftly { 702 | var result = left 703 | result.toItem = right 704 | return result 705 | } 706 | 707 | /** 708 | Assign a property of a `Swiftlyable` equal to a constant. Useful for things such as settings the top of a view equal to the top of another view. 709 | 710 | - parameter left: Layout property to assign 711 | - parameter right: Constant to assign 712 | 713 | - returns: A Swiftly object representing the desired constraint 714 | */ 715 | public func ==(left: Swiftly, right: CGFloat) -> Swiftly { 716 | var result = left 717 | result.constant = right 718 | 719 | if left.attribute != nil { 720 | result.otherAttribute = .notAnAttribute 721 | } else if let attrsCount = left.attributes?.count { 722 | result.otherAttributes = [NSLayoutConstraint.Attribute](repeating: .notAnAttribute, count: attrsCount) 723 | } 724 | 725 | return result 726 | } 727 | 728 | /** 729 | Assign a layout property equal to another property. Useful for things such as setting the bottom of a view to the top of another. 730 | 731 | - parameter left: Layout property to assign 732 | - parameter right: Layout property to equal 733 | 734 | - returns: A Swiftly object representing the desired constraint 735 | */ 736 | public func ==(left: Swiftly, right: Swiftly) -> Swiftly { 737 | var result = left 738 | result.toItem = right.toItem 739 | 740 | if let attrs = right.attributes { 741 | result.otherAttributes = attrs 742 | } else { 743 | result.otherAttribute = right.attribute 744 | } 745 | 746 | result.relatedBy = .equal 747 | if right.constant != 0 { 748 | result.constant = right.constant 749 | } 750 | if right.multiplier != 0 { 751 | result.multiplier = right.multiplier 752 | } 753 | return result 754 | } 755 | 756 | /** 757 | Assign a layout property greater than or equal to another property. Useful for things such as creating a view that should have a width greater than or equal to a spacer view. 758 | 759 | - parameter left: Layout property to assign 760 | - parameter right: Layout property to be greater than or equal to 761 | 762 | - returns: A Swiftly object representing the desired constraint 763 | */ 764 | public func >=(left: Swiftly, right: Swiftly) -> Swiftly { 765 | var result = left 766 | result.toItem = right.toItem 767 | 768 | if let attrs = right.attributes { 769 | result.otherAttributes = attrs 770 | } else { 771 | result.otherAttribute = right.attribute 772 | } 773 | 774 | result.relatedBy = .greaterThanOrEqual 775 | if right.constant != 0 { 776 | result.constant = right.constant 777 | } 778 | if right.multiplier != 0 { 779 | result.multiplier = right.multiplier 780 | } 781 | return result 782 | } 783 | 784 | /** 785 | Assign a layout property greater than or equal to a constant. 786 | 787 | - parameter left: Layout property to assign 788 | - parameter right: Constant to be greater than or equal to 789 | 790 | - returns: A Swiftly object representing the desired constraint 791 | */ 792 | public func >=(left: Swiftly, right: CGFloat) -> Swiftly { 793 | var result = left 794 | result.constant = right 795 | 796 | if left.attribute != nil { 797 | result.otherAttribute = .notAnAttribute 798 | } else if let attrsCount = left.attributes?.count { 799 | result.otherAttributes = [NSLayoutConstraint.Attribute](repeating: .notAnAttribute, count: attrsCount) 800 | } 801 | 802 | result.relatedBy = .greaterThanOrEqual 803 | result.multiplier = 1 804 | return result 805 | } 806 | 807 | /** 808 | Assign a layout property less than or equal to another property. Useful for things such as creating a spacer view that should have a width less than or equal to a view. 809 | 810 | - parameter left: Layout property to assign 811 | - parameter right: Layout property to be less than or equal to 812 | 813 | - returns: A Swiftly object representing the desired constraint 814 | */ 815 | public func <=(left: Swiftly, right: Swiftly) -> Swiftly { 816 | var result = left 817 | result.toItem = right.toItem 818 | 819 | if let attrs = right.attributes { 820 | result.otherAttributes = attrs 821 | } else { 822 | result.otherAttribute = right.attribute 823 | } 824 | 825 | result.relatedBy = .lessThanOrEqual 826 | if right.constant != 0 { 827 | result.constant = right.constant 828 | } 829 | if right.multiplier != 0 { 830 | result.multiplier = right.multiplier 831 | } 832 | return result 833 | } 834 | 835 | /** 836 | Assign a layout property less than or equal to a constant. 837 | 838 | - parameter left: Layout property to assign 839 | - parameter right: The constant to be less than or equal to 840 | 841 | - returns: A Swiftly object representing the desired constraint 842 | */ 843 | public func <=(left: Swiftly, right: CGFloat) -> Swiftly { 844 | var result = left 845 | result.constant = right 846 | 847 | if left.attribute != nil { 848 | result.otherAttribute = .notAnAttribute 849 | } else if let attrsCount = left.attributes?.count { 850 | result.otherAttributes = [NSLayoutConstraint.Attribute](repeating: .notAnAttribute, count: attrsCount) 851 | } 852 | 853 | result.relatedBy = .lessThanOrEqual 854 | result.multiplier = 1 855 | return result 856 | } 857 | 858 | /** 859 | Assign the constant of a property. Useful for things such as pinning a view to it's superview with a margin. 860 | 861 | - parameter left: Layout property to assign 862 | - parameter right: Constant to apply 863 | 864 | - returns: A Swiftly object representing the desired constraint 865 | */ 866 | public func +(left: Swiftly, right: CGFloat) -> Swiftly { 867 | var result = left 868 | result.constant = right 869 | return result 870 | } 871 | 872 | /** 873 | Assign a negative constant to a property. 874 | 875 | - parameter left: Layout property to assign 876 | - parameter right: Constant value to apply 877 | 878 | - returns: A Swiftly object representing the desired constraint 879 | */ 880 | public func -(left: Swiftly, right: CGFloat) -> Swiftly { 881 | var result = left 882 | result.constant = -(right) 883 | return result 884 | } 885 | 886 | /** 887 | Assign the multiplier of a property. 888 | 889 | - parameter left: Layout property to assign 890 | - parameter right: Multiplier value 891 | 892 | - returns: A Swiftly object representing the desired constraint 893 | */ 894 | public func *(left: Swiftly, right: CGFloat) -> Swiftly { 895 | var result = left 896 | result.multiplier = right 897 | return result 898 | } 899 | 900 | /** 901 | Assign the multiplier of a property. 902 | 903 | - parameter left: Layout property to assign 904 | - parameter right: Inverse multiplier value 905 | 906 | - returns: A Swiftly object representing the desired constraint 907 | */ 908 | public func /(left: Swiftly, right: CGFloat) -> Swiftly { 909 | var result = left 910 | result.multiplier = 1 / right 911 | return result 912 | } 913 | 914 | /** 915 | Assign the priority of a property. 916 | 917 | - parameter priority: Layout property to assign 918 | - parameter swiftly: The priority 919 | 920 | - returns: A Swiftly object representing the desired constraint 921 | */ 922 | public func ~=(left: Swiftly, right: UILayoutPriority) -> Swiftly { 923 | var s = left 924 | s.priority = right 925 | return s 926 | } 927 | -------------------------------------------------------------------------------- /SwiftlyTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.trappdesign.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftlyTests/SwiftlyTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftlyTests.swift 3 | // SwiftlyTests 4 | // 5 | // Created by Nora Trapp on 6/23/15. 6 | // Copyright (c) 2015 Trapp Design. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | import Swiftly 12 | 13 | class SwiftlyTests: XCTestCase { 14 | 15 | var containerView: UIView! 16 | let view = UIView() 17 | let offset: CGFloat = 5 18 | let negativeOffset: CGFloat = -5 19 | let span: CGFloat = 100 20 | let percentage: CGFloat = 0.5 21 | 22 | override func setUp() { 23 | super.setUp() 24 | containerView = UIView(frame: CGRect(x: 0, y: 0, width: span, height: span)) 25 | containerView.addSubview(view) 26 | } 27 | 28 | override func tearDown() { 29 | // Put teardown code here. This method is called after the invocation of each test method in the class. 30 | super.tearDown() 31 | } 32 | 33 | func testOriginYPositiveOffset() { 34 | view.applyLayout(.Top() + offset) 35 | view.setNeedsLayout() 36 | view.layoutIfNeeded() 37 | XCTAssert(view.frame.origin.y == offset) 38 | } 39 | 40 | func testOriginYNegativeOffset() { 41 | view.applyLayout(.Top() - offset) 42 | view.setNeedsLayout() 43 | view.layoutIfNeeded() 44 | XCTAssert(view.frame.origin.y == negativeOffset) 45 | } 46 | 47 | func testOriginXPositiveOffset() { 48 | view.applyLayout(.Left() + offset) 49 | view.setNeedsLayout() 50 | view.layoutIfNeeded() 51 | XCTAssert(view.frame.origin.x == offset) 52 | } 53 | 54 | func testOriginXNegativeOffset() { 55 | view.applyLayout(.Left() - offset) 56 | view.setNeedsLayout() 57 | view.layoutIfNeeded() 58 | XCTAssert(view.frame.origin.x == negativeOffset) 59 | } 60 | 61 | func testOriginMultiplication() { 62 | view.applyLayout(.Width() * 2) 63 | view.setNeedsLayout() 64 | view.layoutIfNeeded() 65 | XCTAssert(view.frame.size.width == span * 2) 66 | } 67 | 68 | func testFlush() { 69 | view.applyLayout(.Flush()) 70 | view.setNeedsLayout() 71 | view.layoutIfNeeded() 72 | XCTAssert(view.frame == containerView.frame) 73 | } 74 | 75 | func testFlushToMargins() { 76 | containerView.layoutMargins = UIEdgeInsets(top: offset, left: offset, bottom: offset, right: offset) 77 | view.applyLayout(.FlushToMargins()) 78 | view.setNeedsLayout() 79 | view.layoutIfNeeded() 80 | let expectedSpan = span - (offset * 2) 81 | XCTAssert(view.frame == CGRect(x: offset, y: offset, width: expectedSpan, height: expectedSpan)) 82 | } 83 | 84 | } 85 | --------------------------------------------------------------------------------