From 3034a29c3e6847b595ad27d31f1685bbecc0d16b Mon Sep 17 00:00:00 2001 From: Deepak Mantena Date: Fri, 9 Aug 2019 20:51:45 -0700 Subject: [PATCH] Initial release of Cully. --- .gitignore | 28 + .../Cully Example.xcodeproj/project.pbxproj | 345 ++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcschemes/Cully Example.xcscheme | 91 ++++ Cully Example/Cully Example/AppDelegate.swift | 25 + .../AppIcon.appiconset/Contents.json | 98 ++++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 + Cully Example/Cully Example/Info.plist | 41 ++ Cully Example/Cully Example/View.swift | 149 ++++++ .../Cully Example/ViewController.swift | 40 ++ Cully.xcodeproj/project.pbxproj | 494 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Cully.xcscheme | 99 ++++ Cully.xcworkspace/contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + Cully/Resources/Cully.h | 17 + Cully/Resources/Info.plist | 22 + Cully/Source/Collection+Cully.swift | 22 + Cully/Source/UIView+Cully.swift | 362 +++++++++++++ CullyTests/CullyTests.swift | 14 + CullyTests/Info.plist | 22 + LICENSE | 21 + README.md | 105 ++++ 26 files changed, 2074 insertions(+) create mode 100644 .gitignore create mode 100644 Cully Example/Cully Example.xcodeproj/project.pbxproj create mode 100644 Cully Example/Cully Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Cully Example/Cully Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Cully Example/Cully Example.xcodeproj/xcshareddata/xcschemes/Cully Example.xcscheme create mode 100644 Cully Example/Cully Example/AppDelegate.swift create mode 100644 Cully Example/Cully Example/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Cully Example/Cully Example/Assets.xcassets/Contents.json create mode 100644 Cully Example/Cully Example/Base.lproj/LaunchScreen.storyboard create mode 100644 Cully Example/Cully Example/Info.plist create mode 100644 Cully Example/Cully Example/View.swift create mode 100644 Cully Example/Cully Example/ViewController.swift create mode 100644 Cully.xcodeproj/project.pbxproj create mode 100644 Cully.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Cully.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Cully.xcodeproj/xcshareddata/xcschemes/Cully.xcscheme create mode 100644 Cully.xcworkspace/contents.xcworkspacedata create mode 100644 Cully.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Cully/Resources/Cully.h create mode 100644 Cully/Resources/Info.plist create mode 100644 Cully/Source/Collection+Cully.swift create mode 100644 Cully/Source/UIView+Cully.swift create mode 100644 CullyTests/CullyTests.swift create mode 100644 CullyTests/Info.plist create mode 100644 LICENSE create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..21c3f01 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# Xcode + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xcuserstate + +.DS_Store +xcuserdata +*.xcuserdatad +*.xccheckout +*.mode* +*.pbxuser +.build \ No newline at end of file diff --git a/Cully Example/Cully Example.xcodeproj/project.pbxproj b/Cully Example/Cully Example.xcodeproj/project.pbxproj new file mode 100644 index 0000000..88d3b38 --- /dev/null +++ b/Cully Example/Cully Example.xcodeproj/project.pbxproj @@ -0,0 +1,345 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 0008D13322F6E29E006D1408 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0008D13222F6E29E006D1408 /* AppDelegate.swift */; }; + 0008D13522F6E29E006D1408 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0008D13422F6E29E006D1408 /* ViewController.swift */; }; + 0008D13A22F6E2A3006D1408 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0008D13922F6E2A3006D1408 /* Assets.xcassets */; }; + 0008D13D22F6E2A3006D1408 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0008D13B22F6E2A3006D1408 /* LaunchScreen.storyboard */; }; + 0008D15122F6E526006D1408 /* Cully.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0008D15022F6E526006D1408 /* Cully.framework */; }; + 00CC770522F7DEA900C24BC8 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CC770422F7DEA900C24BC8 /* View.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0008D12F22F6E29E006D1408 /* Cully Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Cully Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 0008D13222F6E29E006D1408 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 0008D13422F6E29E006D1408 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 0008D13922F6E2A3006D1408 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 0008D13C22F6E2A3006D1408 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 0008D13E22F6E2A3006D1408 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0008D15022F6E526006D1408 /* Cully.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Cully.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 00CC770422F7DEA900C24BC8 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0008D12C22F6E29E006D1408 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0008D15122F6E526006D1408 /* Cully.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0008D12622F6E29E006D1408 = { + isa = PBXGroup; + children = ( + 0008D13122F6E29E006D1408 /* Cully Example */, + 0008D13022F6E29E006D1408 /* Products */, + 0008D14F22F6E526006D1408 /* Frameworks */, + ); + sourceTree = ""; + }; + 0008D13022F6E29E006D1408 /* Products */ = { + isa = PBXGroup; + children = ( + 0008D12F22F6E29E006D1408 /* Cully Example.app */, + ); + name = Products; + sourceTree = ""; + }; + 0008D13122F6E29E006D1408 /* Cully Example */ = { + isa = PBXGroup; + children = ( + 0008D13222F6E29E006D1408 /* AppDelegate.swift */, + 0008D13422F6E29E006D1408 /* ViewController.swift */, + 0008D13922F6E2A3006D1408 /* Assets.xcassets */, + 0008D13B22F6E2A3006D1408 /* LaunchScreen.storyboard */, + 0008D13E22F6E2A3006D1408 /* Info.plist */, + 00CC770422F7DEA900C24BC8 /* View.swift */, + ); + path = "Cully Example"; + sourceTree = ""; + }; + 0008D14F22F6E526006D1408 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0008D15022F6E526006D1408 /* Cully.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 0008D12E22F6E29E006D1408 /* Cully Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0008D14122F6E2A3006D1408 /* Build configuration list for PBXNativeTarget "Cully Example" */; + buildPhases = ( + 0008D12B22F6E29E006D1408 /* Sources */, + 0008D12C22F6E29E006D1408 /* Frameworks */, + 0008D12D22F6E29E006D1408 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Cully Example"; + productName = "Cully Example"; + productReference = 0008D12F22F6E29E006D1408 /* Cully Example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0008D12722F6E29E006D1408 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1030; + LastUpgradeCheck = 1030; + ORGANIZATIONNAME = "Stay Kids"; + TargetAttributes = { + 0008D12E22F6E29E006D1408 = { + CreatedOnToolsVersion = 10.3; + }; + }; + }; + buildConfigurationList = 0008D12A22F6E29E006D1408 /* Build configuration list for PBXProject "Cully Example" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 0008D12622F6E29E006D1408; + productRefGroup = 0008D13022F6E29E006D1408 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0008D12E22F6E29E006D1408 /* Cully Example */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0008D12D22F6E29E006D1408 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0008D13D22F6E2A3006D1408 /* LaunchScreen.storyboard in Resources */, + 0008D13A22F6E2A3006D1408 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 0008D12B22F6E29E006D1408 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0008D13522F6E29E006D1408 /* ViewController.swift in Sources */, + 0008D13322F6E29E006D1408 /* AppDelegate.swift in Sources */, + 00CC770522F7DEA900C24BC8 /* View.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 0008D13B22F6E2A3006D1408 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 0008D13C22F6E2A3006D1408 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 0008D13F22F6E2A3006D1408 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 0008D14022F6E2A3006D1408 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 0008D14222F6E2A3006D1408 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Cully Example/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.staykids.Cully-Example"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 0008D14322F6E2A3006D1408 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Cully Example/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.staykids.Cully-Example"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0008D12A22F6E29E006D1408 /* Build configuration list for PBXProject "Cully Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0008D13F22F6E2A3006D1408 /* Debug */, + 0008D14022F6E2A3006D1408 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0008D14122F6E2A3006D1408 /* Build configuration list for PBXNativeTarget "Cully Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0008D14222F6E2A3006D1408 /* Debug */, + 0008D14322F6E2A3006D1408 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0008D12722F6E29E006D1408 /* Project object */; +} diff --git a/Cully Example/Cully Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Cully Example/Cully Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..c90a809 --- /dev/null +++ b/Cully Example/Cully Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Cully Example/Cully Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Cully Example/Cully Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Cully Example/Cully Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Cully Example/Cully Example.xcodeproj/xcshareddata/xcschemes/Cully Example.xcscheme b/Cully Example/Cully Example.xcodeproj/xcshareddata/xcschemes/Cully Example.xcscheme new file mode 100644 index 0000000..e6b9803 --- /dev/null +++ b/Cully Example/Cully Example.xcodeproj/xcshareddata/xcschemes/Cully Example.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cully Example/Cully Example/AppDelegate.swift b/Cully Example/Cully Example/AppDelegate.swift new file mode 100644 index 0000000..7066431 --- /dev/null +++ b/Cully Example/Cully Example/AppDelegate.swift @@ -0,0 +1,25 @@ +// +// AppDelegate.swift +// Cully Example +// +// Created by Deepak Mantena +// Copyright © 2019 Stay Kids. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + let window = UIWindow(frame: UIScreen.main.bounds) + window.rootViewController = ViewController(nibName: nil, bundle: nil) + window.makeKeyAndVisible() + self.window = window + return true + } + +} + diff --git a/Cully Example/Cully Example/Assets.xcassets/AppIcon.appiconset/Contents.json b/Cully Example/Cully Example/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d8db8d6 --- /dev/null +++ b/Cully Example/Cully Example/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Cully Example/Cully Example/Assets.xcassets/Contents.json b/Cully Example/Cully Example/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Cully Example/Cully Example/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Cully Example/Cully Example/Base.lproj/LaunchScreen.storyboard b/Cully Example/Cully Example/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..bfa3612 --- /dev/null +++ b/Cully Example/Cully Example/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cully Example/Cully Example/Info.plist b/Cully Example/Cully Example/Info.plist new file mode 100644 index 0000000..355baa3 --- /dev/null +++ b/Cully Example/Cully Example/Info.plist @@ -0,0 +1,41 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Cully Example/Cully Example/View.swift b/Cully Example/Cully Example/View.swift new file mode 100644 index 0000000..d6815c7 --- /dev/null +++ b/Cully Example/Cully Example/View.swift @@ -0,0 +1,149 @@ +// +// View.swift +// Cully Example +// +// Created by Deepak Mantena +// Copyright © 2019 Stay Kids. All rights reserved. +// + +import UIKit +import Cully + +final class View: UIView { + + // MARK: - Nested Types + + enum Example: Int, CaseIterable { + case one = 0 + case two = 1 + + var description: String { + switch self { + case .one: return "Example One" + case .two: return "Example Two" + } + } + } + + // MARK: - UI Elements + + let segmentedControl: UISegmentedControl = { + let segmentedControl = UISegmentedControl(items: Example.allCases.map{$0.description}) + segmentedControl.selectedSegmentIndex = Example.one.rawValue + return segmentedControl + }() + + lazy var viewOne: UIView = { + let view = UIView.withBackgroundColor(UIColor.green.withAlphaComponent(0.25)) + + let whiteBox = UIView.withBackgroundColor(.white) + let redBox = UIView.withBackgroundColor(.red) + let yellowBox = UIView.withBackgroundColor(.yellow) + let blueBox = UIView.withBackgroundColor(.blue) + let orangeBox = UIView.withBackgroundColor(.orange) + + view.addSubview(whiteBox) + whiteBox.addSubview(redBox) + whiteBox.addSubview(yellowBox) + whiteBox.addSubview(blueBox) + whiteBox.addSubview(orangeBox) + + whiteBox.constrain(width: .equal, .width, of: view) + whiteBox.constrain(height: .equal, .height, of: view, multiplier: 0.5) + whiteBox.constrain(centerY: .equal, .centerY, of: view) + + redBox.constrain(fill: whiteBox, insets: UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 100)) + + yellowBox.constrain(top: .lessThanOrEqual, .bottom, of: redBox) + yellowBox.constrain(bottom: .equal, .bottom, of: whiteBox) + yellowBox.constrain(left: .equal, .left, of: whiteBox) + yellowBox.constrain(right: .lessThanOrEqual, .left, of: redBox) + + blueBox.constrain(width: 105, height: 95) + blueBox.constrain(centerX: .equal, .centerX, of: redBox, multiplier: 1.5) + blueBox.constrain(centerY: .equal, .centerY, of: redBox, multiplier: 0.65) + + orangeBox.constrain(pinTopTo: .top, of: whiteBox, leftTo: .right, of: blueBox, bottomTo: .top, of: blueBox, rightTo: .right, of: whiteBox) + + return view + }() + + let viewTwo: UIView = { + let view = UIView.withBackgroundColor(UIColor.purple.withAlphaComponent(0.25)) + + let segmentations = 20 + var lastRectangle: UIView? + + for segment in 0...segmentations { + let hValue = CGFloat.random(in: 0...1) + let sValue = CGFloat.random(in: 0...1) + let bValue = CGFloat.random(in: 0.5...1) + + let color = UIColor(hue: hValue, saturation: sValue, brightness: bValue, alpha: 1.0) + let rectangle = UIView.withBackgroundColor(color) + + view.addSubview(rectangle) + rectangle.constrain(leftTo: .left, of: view) + rectangle.constrain(rightTo: .right, of: view) + rectangle.constrain(attribute: .height, .equal, .height, of: view, multiplier: 1/CGFloat(segmentations)) + + if let lastRectange = lastRectangle { + rectangle.constrain(top: .equal, .bottom, of: lastRectangle) + } else { + rectangle.constrain(top: .equal, .top, of: view) + } + + lastRectangle = rectangle + } + + return view + }() + + // MARK: - Lifecycle + + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + // MARK: - Setup + + func setup() { + backgroundColor = .white + + addSubview(segmentedControl) + segmentedControl.constrain(top: .equal, .top, of: safeAreaLayoutGuide) + segmentedControl.constrain(centerX: .equal, .centerX, of: self) + + insertSubview(viewOne, belowSubview: segmentedControl) + insertSubview(viewTwo, belowSubview: segmentedControl) + + viewOne.constrain(pinTopTo: .bottom, of: segmentedControl, leftTo: .left, of: self, bottomTo: .bottom, of: self, rightTo: .right, of: self, insets: UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)) + viewTwo.constrain(fill: viewOne) + + update(visible: .one) + } + + // MARK: - Update + + func update(visible example: Example) { + viewOne.isHidden = example != .one + viewTwo.isHidden = example != .two + } + +} + +fileprivate extension UIView { + + static func withBackgroundColor(_ color: UIColor) -> UIView { + let view = UIView() + view.backgroundColor = color + return view + } + +} diff --git a/Cully Example/Cully Example/ViewController.swift b/Cully Example/Cully Example/ViewController.swift new file mode 100644 index 0000000..67a254b --- /dev/null +++ b/Cully Example/Cully Example/ViewController.swift @@ -0,0 +1,40 @@ +// +// ViewController.swift +// Cully Example +// +// Created by Deepak Mantena +// Copyright © 2019 Stay Kids. All rights reserved. +// + +import UIKit +import Cully + +class ViewController: UIViewController { + + // MARK: - Properties + + lazy var exampleView = (view as? View) + + // MARK: - Lifecycle + + override func loadView() { + self.view = View(frame: UIScreen.main.bounds) + } + + override func viewDidLoad() { + super.viewDidLoad() + exampleView?.segmentedControl.addTarget(self, action: #selector(userDidUpdate(_:)), for: .valueChanged) + } + + // MARK: - Actions + + @objc func userDidUpdate(_ segmentedControl: UISegmentedControl) { + guard let selectedExample = View.Example(rawValue: segmentedControl.selectedSegmentIndex) else { + fatalError() + } + + exampleView?.update(visible: selectedExample) + } + +} + diff --git a/Cully.xcodeproj/project.pbxproj b/Cully.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7b4c5ab --- /dev/null +++ b/Cully.xcodeproj/project.pbxproj @@ -0,0 +1,494 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 0008D11022F6E05E006D1408 /* Cully.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0008D10622F6E05E006D1408 /* Cully.framework */; }; + 0008D11522F6E05E006D1408 /* CullyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0008D11422F6E05E006D1408 /* CullyTests.swift */; }; + 0008D11722F6E05E006D1408 /* Cully.h in Headers */ = {isa = PBXBuildFile; fileRef = 0008D10922F6E05E006D1408 /* Cully.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0008D12422F6E1AB006D1408 /* UIView+Cully.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0008D12322F6E1AB006D1408 /* UIView+Cully.swift */; }; + 0008D12522F6E1AB006D1408 /* UIView+Cully.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0008D12322F6E1AB006D1408 /* UIView+Cully.swift */; }; + 003609B522F7B9E00080D870 /* Collection+Cully.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003609B422F7B9E00080D870 /* Collection+Cully.swift */; }; + 003609B622F7B9E00080D870 /* Collection+Cully.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003609B422F7B9E00080D870 /* Collection+Cully.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 0008D11122F6E05E006D1408 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0008D0FD22F6E05E006D1408 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0008D10522F6E05E006D1408; + remoteInfo = Cully; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0008D10622F6E05E006D1408 /* Cully.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cully.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0008D10922F6E05E006D1408 /* Cully.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Cully.h; sourceTree = ""; }; + 0008D10A22F6E05E006D1408 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0008D10F22F6E05E006D1408 /* CullyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CullyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 0008D11422F6E05E006D1408 /* CullyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CullyTests.swift; sourceTree = ""; }; + 0008D11622F6E05E006D1408 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0008D12322F6E1AB006D1408 /* UIView+Cully.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Cully.swift"; sourceTree = ""; }; + 003609B422F7B9E00080D870 /* Collection+Cully.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+Cully.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0008D10322F6E05E006D1408 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0008D10C22F6E05E006D1408 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0008D11022F6E05E006D1408 /* Cully.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0008D0FC22F6E05E006D1408 = { + isa = PBXGroup; + children = ( + 0008D10822F6E05E006D1408 /* Cully */, + 0008D11322F6E05E006D1408 /* CullyTests */, + 0008D10722F6E05E006D1408 /* Products */, + ); + sourceTree = ""; + }; + 0008D10722F6E05E006D1408 /* Products */ = { + isa = PBXGroup; + children = ( + 0008D10622F6E05E006D1408 /* Cully.framework */, + 0008D10F22F6E05E006D1408 /* CullyTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 0008D10822F6E05E006D1408 /* Cully */ = { + isa = PBXGroup; + children = ( + 0008D12222F6E197006D1408 /* Source */, + 0008D12122F6E15A006D1408 /* Resources */, + ); + path = Cully; + sourceTree = ""; + }; + 0008D11322F6E05E006D1408 /* CullyTests */ = { + isa = PBXGroup; + children = ( + 0008D11422F6E05E006D1408 /* CullyTests.swift */, + 0008D11622F6E05E006D1408 /* Info.plist */, + ); + path = CullyTests; + sourceTree = ""; + }; + 0008D12122F6E15A006D1408 /* Resources */ = { + isa = PBXGroup; + children = ( + 0008D10922F6E05E006D1408 /* Cully.h */, + 0008D10A22F6E05E006D1408 /* Info.plist */, + ); + path = Resources; + sourceTree = ""; + }; + 0008D12222F6E197006D1408 /* Source */ = { + isa = PBXGroup; + children = ( + 0008D12322F6E1AB006D1408 /* UIView+Cully.swift */, + 003609B422F7B9E00080D870 /* Collection+Cully.swift */, + ); + path = Source; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 0008D10122F6E05E006D1408 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0008D11722F6E05E006D1408 /* Cully.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 0008D10522F6E05E006D1408 /* Cully */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0008D11A22F6E05E006D1408 /* Build configuration list for PBXNativeTarget "Cully" */; + buildPhases = ( + 0008D10122F6E05E006D1408 /* Headers */, + 0008D10222F6E05E006D1408 /* Sources */, + 0008D10322F6E05E006D1408 /* Frameworks */, + 0008D10422F6E05E006D1408 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Cully; + productName = Cully; + productReference = 0008D10622F6E05E006D1408 /* Cully.framework */; + productType = "com.apple.product-type.framework"; + }; + 0008D10E22F6E05E006D1408 /* CullyTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0008D11D22F6E05E006D1408 /* Build configuration list for PBXNativeTarget "CullyTests" */; + buildPhases = ( + 0008D10B22F6E05E006D1408 /* Sources */, + 0008D10C22F6E05E006D1408 /* Frameworks */, + 0008D10D22F6E05E006D1408 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 0008D11222F6E05E006D1408 /* PBXTargetDependency */, + ); + name = CullyTests; + productName = CullyTests; + productReference = 0008D10F22F6E05E006D1408 /* CullyTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0008D0FD22F6E05E006D1408 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1030; + LastUpgradeCheck = 1030; + ORGANIZATIONNAME = "Stay Kids, LLC"; + TargetAttributes = { + 0008D10522F6E05E006D1408 = { + CreatedOnToolsVersion = 10.3; + LastSwiftMigration = 1030; + }; + 0008D10E22F6E05E006D1408 = { + CreatedOnToolsVersion = 10.3; + }; + }; + }; + buildConfigurationList = 0008D10022F6E05E006D1408 /* Build configuration list for PBXProject "Cully" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 0008D0FC22F6E05E006D1408; + productRefGroup = 0008D10722F6E05E006D1408 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0008D10522F6E05E006D1408 /* Cully */, + 0008D10E22F6E05E006D1408 /* CullyTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0008D10422F6E05E006D1408 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0008D10D22F6E05E006D1408 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 0008D10222F6E05E006D1408 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0008D12422F6E1AB006D1408 /* UIView+Cully.swift in Sources */, + 003609B522F7B9E00080D870 /* Collection+Cully.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0008D10B22F6E05E006D1408 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 003609B622F7B9E00080D870 /* Collection+Cully.swift in Sources */, + 0008D12522F6E1AB006D1408 /* UIView+Cully.swift in Sources */, + 0008D11522F6E05E006D1408 /* CullyTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 0008D11222F6E05E006D1408 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0008D10522F6E05E006D1408 /* Cully */; + targetProxy = 0008D11122F6E05E006D1408 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 0008D11822F6E05E006D1408 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 0008D11922F6E05E006D1408 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 0008D11B22F6E05E006D1408 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/Cully/Resources/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.staykids.Cully; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 0008D11C22F6E05E006D1408 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/Cully/Resources/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.staykids.Cully; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 0008D11E22F6E05E006D1408 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = CullyTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.staykids.CullyTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 0008D11F22F6E05E006D1408 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = CullyTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.staykids.CullyTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0008D10022F6E05E006D1408 /* Build configuration list for PBXProject "Cully" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0008D11822F6E05E006D1408 /* Debug */, + 0008D11922F6E05E006D1408 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0008D11A22F6E05E006D1408 /* Build configuration list for PBXNativeTarget "Cully" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0008D11B22F6E05E006D1408 /* Debug */, + 0008D11C22F6E05E006D1408 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0008D11D22F6E05E006D1408 /* Build configuration list for PBXNativeTarget "CullyTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0008D11E22F6E05E006D1408 /* Debug */, + 0008D11F22F6E05E006D1408 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0008D0FD22F6E05E006D1408 /* Project object */; +} diff --git a/Cully.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Cully.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..e043dd3 --- /dev/null +++ b/Cully.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Cully.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Cully.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Cully.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Cully.xcodeproj/xcshareddata/xcschemes/Cully.xcscheme b/Cully.xcodeproj/xcshareddata/xcschemes/Cully.xcscheme new file mode 100644 index 0000000..cee7fc0 --- /dev/null +++ b/Cully.xcodeproj/xcshareddata/xcschemes/Cully.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cully.xcworkspace/contents.xcworkspacedata b/Cully.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..49c1a69 --- /dev/null +++ b/Cully.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Cully.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Cully.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Cully.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Cully/Resources/Cully.h b/Cully/Resources/Cully.h new file mode 100644 index 0000000..7c3bdbe --- /dev/null +++ b/Cully/Resources/Cully.h @@ -0,0 +1,17 @@ +// +// Cully.h +// Cully +// +// Created by Deepak Mantena +// Copyright © 2019 Stay Kids. All rights reserved. +// + +#import + +//! Project version number for Cully. +FOUNDATION_EXPORT double CullyVersionNumber; + +//! Project version string for Cully. +FOUNDATION_EXPORT const unsigned char CullyVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import diff --git a/Cully/Resources/Info.plist b/Cully/Resources/Info.plist new file mode 100644 index 0000000..e1fe4cf --- /dev/null +++ b/Cully/Resources/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/Cully/Source/Collection+Cully.swift b/Cully/Source/Collection+Cully.swift new file mode 100644 index 0000000..90f6809 --- /dev/null +++ b/Cully/Source/Collection+Cully.swift @@ -0,0 +1,22 @@ +// +// Collection+Cully.swift +// Cully +// +// Created by Deepak Mantena +// Copyright © 2019 Stay Kids. All rights reserved. +// + +import UIKit + +extension Collection where Element: NSLayoutConstraint { + + public typealias KeyedConstraints = [NSLayoutConstraint.Attribute: NSLayoutConstraint] + + /// An `NSLayoutConstraint.Attribute` keyed dictionary of `NSLayoutConstraint`s with Cully + public var keyed: KeyedConstraints { + return reduce(into: KeyedConstraints(), { (attributedConstraints, constraint) in + attributedConstraints[constraint.firstAttribute] = constraint + }) + } + +} diff --git a/Cully/Source/UIView+Cully.swift b/Cully/Source/UIView+Cully.swift new file mode 100644 index 0000000..f808ebc --- /dev/null +++ b/Cully/Source/UIView+Cully.swift @@ -0,0 +1,362 @@ +// +// UIView+Cully.swift +// Cully +// +// Created by Deepak Mantena +// Copyright © 2019 Stay Kids. All rights reserved. +// + +import UIKit + +extension UIView { + + // MARK: - Generic Constraints + + /// Constrain attribute with Cully + @discardableResult + public func constrain(_ attribute1: NSLayoutConstraint.Attribute, _ relation: NSLayoutConstraint.Relation, _ attribute2: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + translatesAutoresizingMaskIntoConstraints = false + let constraint = NSLayoutConstraint(item: self, attribute: attribute1, relatedBy: relation, toItem: item, attribute: attribute2, multiplier: multiplier, constant: constant) + constraint.isActive = isActive + return constraint + } + + /// Constrain attribute with Cully + @discardableResult + public func constrain(attribute attribute1: NSLayoutConstraint.Attribute, _ relation: NSLayoutConstraint.Relation, _ attribute2: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + translatesAutoresizingMaskIntoConstraints = false + let constraint = NSLayoutConstraint(item: self, attribute: attribute1, relatedBy: relation, toItem: item, attribute: attribute2, multiplier: multiplier, constant: constant) + constraint.isActive = isActive + return constraint + } + + // MARK: - Single Constraints + + /// Constrain left attribute with Cully + @discardableResult + public func constrain(left relation: NSLayoutConstraint.Relation = .equal, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.left, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain left attribute using `equal` relation with Cully + @discardableResult + public func constrain(leftTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.left, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain right attribute with Cully + @discardableResult + public func constrain(right relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.right, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain right attribute using `equal` relation with Cully + @discardableResult + public func constrain(rightTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.right, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain top attribute with Cully + @discardableResult + public func constrain(top relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.top, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain top attribute using `equal` relation with Cully + @discardableResult + public func constrain(topTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.top, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain bottom attribute with Cully + @discardableResult + public func constrain(bottom relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.bottom, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain bottom attribute using `equal` relation with Cully + @discardableResult + public func constrain(bottomTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.bottom, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain leading attribute with Cully + @discardableResult + public func constrain(leading relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.leading, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain leading attribute using `equal` relation with Cully + @discardableResult + public func constrain(leadingTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.leading, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain trailing attribute with Cully + @discardableResult + public func constrain(trailing relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.trailing, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain trailing attribute using `equal` relation with Cully + @discardableResult + public func constrain(trailingTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.trailing, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain width attribute with Cully + @discardableResult + public func constrain(width relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute? = nil, of item: Any? = nil, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.width, relation, attribute ?? .notAnAttribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain width to constant with Cully + @discardableResult + public func constrain(width constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(width: .equal, constant: constant, isActive: isActive) + } + + /// Constrain height attribute with Cully + @discardableResult + public func constrain(height relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute? = nil, of item: Any? = nil, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.height, relation, attribute ?? .notAnAttribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain height to constant with Cully + @discardableResult + public func constrain(height constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(height: .equal, constant: constant, isActive: isActive) + } + + /// Constrain centerX attribute with Cully + @discardableResult + public func constrain(centerX relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.centerX, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain centerX attribute using `equal` relation with Cully + @discardableResult + public func constrain(centerXTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.centerX, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain centerY attribute with Cully + @discardableResult + public func constrain(centerY relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.centerY, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain centerY attribute using `equal` relation with Cully + @discardableResult + public func constrain(centerYTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.centerY, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain lastBaseline attribute with Cully + @discardableResult + public func constrain(lastBaseline relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.lastBaseline, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain lastBaseline attribute using `equal` relation with Cully + @discardableResult + public func constrain(lastBaselineTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.lastBaseline, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain firstBaseline attribute with Cully + @discardableResult + public func constrain(firstBaseline relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.firstBaseline, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain firstBaseline attribute using `equal` relation with Cully + @discardableResult + public func constrain(firstBaselineTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.firstBaseline, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain leftMargin attribute with Cully + @discardableResult + public func constrain(leftMargin relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.leftMargin, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain leftMargin attribute using `equal` relation with Cully + @discardableResult + public func constrain(leftMarginTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.leftMargin, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain rightMargin attribute with Cully + @discardableResult + public func constrain(rightMargin relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.rightMargin, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain rightMargin attribute using `equal` relation with Cully + @discardableResult + public func constrain(rightMarginTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.rightMargin, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain topMargin attribute with Cully + @discardableResult + public func constrain(topMargin relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.topMargin, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain topMargin attribute using `equal` relation with Cully + @discardableResult + public func constrain(topMarginTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.topMargin, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain bottomMargin attribute with Cully + @discardableResult + public func constrain(bottomMargin relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.bottomMargin, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain bottomMargin attribute using `equal` relation with Cully + @discardableResult + public func constrain(bottomMarginTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.bottomMargin, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain leadingMargin attribute with Cully + @discardableResult + public func constrain(leadingMargin relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.leadingMargin, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain leadingMargin attribute using `equal` relation with Cully + @discardableResult + public func constrain(leadingMarginTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.leadingMargin, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain trailingMargin attribute with Cully + @discardableResult + public func constrain(trailingMargin relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.trailingMargin, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain trailingMargin attribute using `equal` relation with Cully + @discardableResult + public func constrain(trailingMarginTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.trailingMargin, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain centerXWithinMargins attribute with Cully + @discardableResult + public func constrain(centerXWithinMargins relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.centerXWithinMargins, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain centerXWithinMargins attribute using `equal` relation with Cully + @discardableResult + public func constrain(centerXWithinMarginsTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.centerXWithinMargins, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain centerYWithinMargins attribute with Cully + @discardableResult + public func constrain(centerYWithinMargins relation: NSLayoutConstraint.Relation, _ attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.centerYWithinMargins, relation, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + /// Constrain centerYWithinMargins attribute using `equal` relation with Cully + @discardableResult + public func constrain(centerYWithinMarginsTo attribute: NSLayoutConstraint.Attribute, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> NSLayoutConstraint { + return constrain(.centerYWithinMargins, .equal, attribute, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + } + + // MARK: - Group Constraints + + /// Constrain width and height attributes with Cully + @discardableResult + public func constrain(width: CGFloat, height: CGFloat, _ relation: NSLayoutConstraint.Relation = .equal, multiplier: CGFloat = 1.0, isActive: Bool = true) -> [NSLayoutConstraint] { + return [ + constrain(width: relation, multiplier: multiplier, constant: width, isActive: isActive), + constrain(height: relation, multiplier: multiplier, constant: height, isActive: isActive) + ] + } + + /// Constrain centerX and centerY attributes with Cully + @discardableResult + public func constrain(center relation: NSLayoutConstraint.Relation, of item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> [NSLayoutConstraint] { + return [ + constrain(centerX: relation, .centerX, of: item, multiplier: multiplier, constant: constant, isActive: isActive), + constrain(centerY: relation, .centerY, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + ] + } + + /// Constrain centerX and centerY attributes with Cully + @discardableResult + public func constrain(centerIn item: Any?, multiplier: CGFloat = 1, constant: CGFloat = 0, isActive: Bool = true) -> [NSLayoutConstraint] { + return [ + constrain(centerX: .equal, .centerX, of: item, multiplier: multiplier, constant: constant, isActive: isActive), + constrain(centerY: .equal, .centerY, of: item, multiplier: multiplier, constant: constant, isActive: isActive) + ] + } + + /// Constrain top, bottom, left, and right attributes filling item with Cully + @discardableResult + public func constrain(fill item: Any?, multiplier: CGFloat = 1, insets: UIEdgeInsets = .zero, isActive: Bool = true) -> [NSLayoutConstraint] { + return [ + constrain(top: .equal, .top, of: item, multiplier: multiplier, constant: insets.top, isActive: isActive), + constrain(bottom: .equal, .bottom, of: item, multiplier: multiplier, constant: -insets.bottom, isActive: isActive), + constrain(left: .equal, .left, of: item, multiplier: multiplier, constant: insets.left, isActive: isActive), + constrain(right: .equal, .right, of: item, multiplier: multiplier, constant: -insets.right, isActive: isActive), + ] + } + + + /// Constrain top, bottom, left, and right attributes pinning to custom items with Cully + @discardableResult + public func constrain(pinTopTo itemTopAttribute: NSLayoutConstraint.Attribute, of topItem: Any? = nil, leftTo itemLeftAttribute: NSLayoutConstraint.Attribute, of leftItem: Any? = nil, bottomTo itemBottomAttribute: NSLayoutConstraint.Attribute, of bottomItem: Any? = nil, rightTo itemRightAttribute: NSLayoutConstraint.Attribute, of rightItem: Any? = nil, multipler: CGFloat = 1, constant: CGFloat = 0, insets: UIEdgeInsets = .zero, isActive: Bool = true) -> [NSLayoutConstraint] { + // The most adventurous of the API in this framework + return [ + constrain(.top, .equal, itemTopAttribute, of: topItem, multiplier: multipler, constant: constant + insets.top, isActive: isActive), + constrain(.left, .equal, itemLeftAttribute, of: leftItem, multiplier: multipler, constant: constant + insets.left, isActive: isActive), + constrain(.bottom, .equal, itemBottomAttribute, of: bottomItem, multiplier: multipler, constant: -constant - insets.bottom, isActive: isActive), + constrain(.right, .equal, itemRightAttribute, of: rightItem, multiplier: multipler, constant: -constant - insets.right, isActive: isActive) + ] + } + + // MARK: - Accessors + + /// Get constraint for attribute with Cully + public func constraint(for attribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint? { + return constraints.first(where: {$0.firstAttribute == attribute}) + } + + /// Get constraints for attributes with Cully + public func constraints(for attributes: [NSLayoutConstraint.Attribute]) -> [NSLayoutConstraint] { + return constraints.filter { attributes.contains($0.firstAttribute) } + } + + // MARK: - Constraint Activation/Deactivation + + /// Deactivate constraint for attribute with Cully + public func constraint(deactivate attribute: NSLayoutConstraint.Attribute) { + constraints(deactivate: [attribute]) + } + + /// Deactivate constraints for attributes with Cully + public func constraints(deactivate attributes: [NSLayoutConstraint.Attribute]) { + NSLayoutConstraint.deactivate(constraints(for: attributes)) + } + + /// Activate constraint with Cully + public func constraints(activate constraint: NSLayoutConstraint) { + constraints(activate: [constraint]) + } + + /// Activate constraints with Cully + public func constraints(activate constraints: [NSLayoutConstraint]) { + NSLayoutConstraint.activate(constraints) + } + +} diff --git a/CullyTests/CullyTests.swift b/CullyTests/CullyTests.swift new file mode 100644 index 0000000..10908f2 --- /dev/null +++ b/CullyTests/CullyTests.swift @@ -0,0 +1,14 @@ +// +// CullyTests.swift +// CullyTests +// +// Created by Deepak Mantena +// Copyright © 2019 Stay Kids. All rights reserved. +// + +import XCTest +@testable import Cully + +class CullyTests: XCTestCase { + // 👋🏽 +} diff --git a/CullyTests/Info.plist b/CullyTests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/CullyTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..addd54a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Deepak Mantena + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f921783 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +# 🏡 Cully +A Swift-based DSL to make Auto Layout friendlier. + +## Motivation + +Auto Layout has gotten nice, but it can still be pretty verbose. Cully provides handy lil' helpers to reduce the amount of boilerplate code needed to layout views and dramatically improves the readability of that code. + +Let's say we want to position a fixed-size `UIView` in the center of another `UIView`. + +```swift +// With `NSLayoutConstraint`s + +let box = UIView() +view.addSubview(box) +box.translatesAutoresizingMaskIntoConstraints = false +NSLayoutConstraint(item: box, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0).isActive = true +NSLayoutConstraint(item: box, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1.0, constant: 0).isActive = true +NSLayoutConstraint(item: box, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 100).isActive = true +NSLayoutConstraint(item: box, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 100).isActive = true +``` + +*Woof* – that's a lot of parse. The same layout with Cully: + +```swift +// With Cully + +let box = UIView() +view.addSubview(box) +box.constrain(width: 100, height: 100) +box.constrain(centerIn: view) +``` + +That's friendlier. + +- Works directly with native types (`NSLayoutConstraint`) and retains the full-power of the native API +- Adds just enough syntactic sugar to be useful, but not so much as to be overwhelming +- Reads more naturally than the native API +- [Saves some time](https://twitter.com/BigZaphod/status/1149876955879268354) + +Yes, one could use `NSLayoutAnchors`, and yes, perhaps when the world is all **SwiftUI** this becomes less relevant, and yes, _____________________, but this is the house I built and I intend on living in it. 🏡 + +## Usage + +```swift +import Cully +``` + +By default, Cully sets the receiver view's `translatesAutoresizingMaskIntoConstraints` to `false` and activates the requested constraint. + +### Adding Constraints + +Methods for adding constraints are namespaced `constrain(...)` on `UIView`. + +```swift +myView.constrain(bottom: .equal, .top, of: someView) +``` + +Single constraint method names mirror `NSLayoutContraint.Attribute`s. They all return a single `NSLayoutConstraint`. Some special case convenience helpers that require creating more than one constraint return `[NSLayoutConstraint]`. + +```swift +// Singly-activated `NSLayoutConstraint`s +let leftConstraint = myView.constrain(left: .equal, .left, of: someView) +let baselineConstraint = myView.constrain(lastBaseline: .lessThanOrEqual, .lastBaseline, of: someView) + +// An array of `NSLayoutConstraint`s +let dimensionConstraints = myView.constrain(width: 50, height: 80) +``` + +Methods for accessing and modifying `NSLayoutConstraints` are namespaced `constraint(...)` and `constraints(...)` on `UIView`. + +```swift +myView.constraints(for [.width, .height]) +myView.constraints(activate: trailingConstraint) +``` + +`KeyedConstraints` is a typealias representing a Dictionary of `NSLayoutConstraint`s keyed by `NSLayoutConstraint.Attribute`s on `Collection`. + +```swift +let keyedConstraints = myView.constraints.keyed +keyedConstraints[.width]?.constant = 150 +``` + +You can also easily access the native `NSLayoutConstraint` initialization API, as well as a helper on `UIView` to activate and deactivate constraints. + +```swift +let topConstraint = myView.constrain(.top, .equal, .top, of: view, multiplier: 1.0, constant: 100, isActive: false) +... +myView.constraints(activate: topConstraint) +``` + +## Installation + +Give [Carthage](https://github.com/Carthage/Carthage) a try, how 'bout? + +1. Add the following to your `Cartfile`: + + ```swift + github "staykids/Cully" "1.0" + ``` + +2. Follow the [Carthage instructions for adding a framework to your project](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application). + +## Improvements + +Cully is opinionatedly tailored to my own needs and uses, but feel free to open a pull request if you think of something cool, notice an issue, or feel something's missing.