├── .gitignore ├── FILE_LICENSE ├── Podfile ├── SwiftHandyFrame.podspec ├── SwiftHandyFrame.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── casa.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── SwiftHandyFrame ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── SwiftHandyFrame │ └── SwiftHandyFrame.swift └── ViewController.swift ├── readme.md ├── sample.png └── upload.sh /.gitignore: -------------------------------------------------------------------------------- 1 | SwiftHandyFrame.xcodeproj/xcuserdata/ 2 | SwiftHandyFrame.xcworkspace/ 3 | Pods/ 4 | *.swp 5 | Podfile.lock 6 | .DS_Store 7 | SwiftHandyFrame.xcodeproj/project.xcworkspace/xcuserdata/ 8 | -------------------------------------------------------------------------------- /FILE_LICENSE: -------------------------------------------------------------------------------- 1 | MIT 2 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | source 'https://github.com/CocoaPods/Specs.git' 5 | 6 | target 'SwiftHandyFrame' do 7 | 8 | end 9 | -------------------------------------------------------------------------------- /SwiftHandyFrame.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint BLAPIManagers.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "SwiftHandyFrame" 19 | s.version = "18" 20 | s.summary = "SwiftHandyFrame." 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = <<-DESC 28 | this is SwiftHandyFrame 29 | DESC 30 | 31 | s.homepage = "https://github.com/casatwy/SwiftHandyFrame" 32 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 33 | 34 | 35 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 36 | # 37 | # Licensing your code is important. See http://choosealicense.com for more info. 38 | # CocoaPods will detect a license file if there is a named LICENSE* 39 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 40 | # 41 | 42 | # s.license = "MIT (example)" 43 | s.license = { :type => "MIT", :file => "FILE_LICENSE" } 44 | 45 | 46 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 47 | # 48 | # Specify the authors of the library, with email addresses. Email addresses 49 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 50 | # accepts just a name if you'd rather not provide an email address. 51 | # 52 | # Specify a social_media_url where others can refer to, for example a twitter 53 | # profile URL. 54 | # 55 | 56 | s.author = { "CasaTaloyum" => "casatwy@msn.com" } 57 | # Or just: s.author = "CasaTaloyum" 58 | # s.authors = { "CasaTaloyum" => "casatwy@msn.com" } 59 | # s.social_media_url = "http://twitter.com/CasaTaloyum" 60 | 61 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 62 | # 63 | # If this Pod runs only on iOS or OS X, then specify the platform and 64 | # the deployment target. You can optionally include the target after the platform. 65 | # 66 | 67 | # s.platform = :ios 68 | s.platform = :ios, "8.0" 69 | 70 | # When using multiple platforms 71 | # s.ios.deployment_target = "5.0" 72 | # s.osx.deployment_target = "10.7" 73 | # s.watchos.deployment_target = "2.0" 74 | # s.tvos.deployment_target = "9.0" 75 | 76 | 77 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 78 | # 79 | # Specify the location from where the source should be retrieved. 80 | # Supports git, hg, bzr, svn and HTTP. 81 | # 82 | 83 | s.source = { :git => "https://github.com/casatwy/SwiftHandyFrame.git", :tag => s.version.to_s } 84 | 85 | 86 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 87 | # 88 | # CocoaPods is smart about how it includes source code. For source files 89 | # giving a folder will include any swift, h, m, mm, c & cpp files. 90 | # For header files it will include any header in the folder. 91 | # Not including the public_header_files will make all headers public. 92 | # 93 | 94 | s.source_files = "SwiftHandyFrame/SwiftHandyFrame/**/*.{h,m,swift}" 95 | # s.exclude_files = "Classes/Exclude" 96 | 97 | # s.public_header_files = "Classes/**/*.h" 98 | 99 | 100 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 101 | # 102 | # A list of resources included with the Pod. These are copied into the 103 | # target bundle with a build phase script. Anything else will be cleaned. 104 | # You can preserve files from being cleaned, please don't preserve 105 | # non-essential files like tests, examples and documentation. 106 | # 107 | 108 | # s.resource = "icon.png" 109 | # s.resources = "Resources/*.png" 110 | 111 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 112 | 113 | 114 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 115 | # 116 | # Link your library with frameworks, or libraries. Libraries do not include 117 | # the lib prefix of their name. 118 | # 119 | 120 | # s.framework = "SomeFramework" 121 | # s.frameworks = "SomeFramework", "AnotherFramework" 122 | 123 | # s.library = "iconv" 124 | # s.libraries = "iconv", "xml2" 125 | 126 | 127 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 128 | # 129 | # If your library depends on compiler flags you can set them in the xcconfig hash 130 | # where they will only apply to your library. If you depend on other Podspecs 131 | # you can include multiple dependencies to ensure it works. 132 | 133 | s.requires_arc = true 134 | 135 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 136 | # s.dependency "BLNetworking" 137 | # s.dependency "BLAPIManagers" 138 | # s.dependency "BLMediator" 139 | 140 | end 141 | -------------------------------------------------------------------------------- /SwiftHandyFrame.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4A69619C212C0E6D00FC634A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A69619B212C0E6D00FC634A /* AppDelegate.swift */; }; 11 | 4A69619E212C0E6D00FC634A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A69619D212C0E6D00FC634A /* ViewController.swift */; }; 12 | 4A6961A1212C0E6D00FC634A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4A69619F212C0E6D00FC634A /* Main.storyboard */; }; 13 | 4A6961A3212C0E6D00FC634A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4A6961A2212C0E6D00FC634A /* Assets.xcassets */; }; 14 | 4A6961A6212C0E6D00FC634A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4A6961A4212C0E6D00FC634A /* LaunchScreen.storyboard */; }; 15 | 4A6961AF212C0F9600FC634A /* SwiftHandyFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A6961AE212C0F9600FC634A /* SwiftHandyFrame.swift */; }; 16 | 773EC2A6E66EBE74AE4227F6 /* libPods-SwiftHandyFrame.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5FBB5EFAE2ED4C17B54FAC15 /* libPods-SwiftHandyFrame.a */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 4A696198212C0E6D00FC634A /* SwiftHandyFrame.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftHandyFrame.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 4A69619B212C0E6D00FC634A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 22 | 4A69619D212C0E6D00FC634A /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 23 | 4A6961A0212C0E6D00FC634A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 24 | 4A6961A2212C0E6D00FC634A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 25 | 4A6961A5212C0E6D00FC634A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 26 | 4A6961A7212C0E6D00FC634A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 27 | 4A6961AE212C0F9600FC634A /* SwiftHandyFrame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftHandyFrame.swift; sourceTree = ""; }; 28 | 5FBB5EFAE2ED4C17B54FAC15 /* libPods-SwiftHandyFrame.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SwiftHandyFrame.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 9EC1B1A74BDED01764FEAA8C /* Pods-SwiftHandyFrame.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftHandyFrame.debug.xcconfig"; path = "Target Support Files/Pods-SwiftHandyFrame/Pods-SwiftHandyFrame.debug.xcconfig"; sourceTree = ""; }; 30 | B2A6A3DCB710019AB276AB9A /* Pods-SwiftHandyFrame.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftHandyFrame.release.xcconfig"; path = "Target Support Files/Pods-SwiftHandyFrame/Pods-SwiftHandyFrame.release.xcconfig"; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 4A696195212C0E6D00FC634A /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | 773EC2A6E66EBE74AE4227F6 /* libPods-SwiftHandyFrame.a in Frameworks */, 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | 4A69618F212C0E6D00FC634A = { 46 | isa = PBXGroup; 47 | children = ( 48 | 4A69619A212C0E6D00FC634A /* SwiftHandyFrame */, 49 | 4A696199212C0E6D00FC634A /* Products */, 50 | 7AD9EC351AF8CCDF0E8E41DB /* Pods */, 51 | A1ACFC92492EA871923314A9 /* Frameworks */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 4A696199212C0E6D00FC634A /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 4A696198212C0E6D00FC634A /* SwiftHandyFrame.app */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 4A69619A212C0E6D00FC634A /* SwiftHandyFrame */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 4A6961AD212C0F7B00FC634A /* SwiftHandyFrame */, 67 | 4A69619B212C0E6D00FC634A /* AppDelegate.swift */, 68 | 4A69619D212C0E6D00FC634A /* ViewController.swift */, 69 | 4A69619F212C0E6D00FC634A /* Main.storyboard */, 70 | 4A6961A2212C0E6D00FC634A /* Assets.xcassets */, 71 | 4A6961A4212C0E6D00FC634A /* LaunchScreen.storyboard */, 72 | 4A6961A7212C0E6D00FC634A /* Info.plist */, 73 | ); 74 | path = SwiftHandyFrame; 75 | sourceTree = ""; 76 | }; 77 | 4A6961AD212C0F7B00FC634A /* SwiftHandyFrame */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 4A6961AE212C0F9600FC634A /* SwiftHandyFrame.swift */, 81 | ); 82 | path = SwiftHandyFrame; 83 | sourceTree = ""; 84 | }; 85 | 7AD9EC351AF8CCDF0E8E41DB /* Pods */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9EC1B1A74BDED01764FEAA8C /* Pods-SwiftHandyFrame.debug.xcconfig */, 89 | B2A6A3DCB710019AB276AB9A /* Pods-SwiftHandyFrame.release.xcconfig */, 90 | ); 91 | name = Pods; 92 | path = Pods; 93 | sourceTree = ""; 94 | }; 95 | A1ACFC92492EA871923314A9 /* Frameworks */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 5FBB5EFAE2ED4C17B54FAC15 /* libPods-SwiftHandyFrame.a */, 99 | ); 100 | name = Frameworks; 101 | sourceTree = ""; 102 | }; 103 | /* End PBXGroup section */ 104 | 105 | /* Begin PBXNativeTarget section */ 106 | 4A696197212C0E6D00FC634A /* SwiftHandyFrame */ = { 107 | isa = PBXNativeTarget; 108 | buildConfigurationList = 4A6961AA212C0E6D00FC634A /* Build configuration list for PBXNativeTarget "SwiftHandyFrame" */; 109 | buildPhases = ( 110 | 9722CC1A190D86E00EE43D09 /* [CP] Check Pods Manifest.lock */, 111 | 4A696194212C0E6D00FC634A /* Sources */, 112 | 4A696195212C0E6D00FC634A /* Frameworks */, 113 | 4A696196212C0E6D00FC634A /* Resources */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = SwiftHandyFrame; 120 | productName = SwiftHandyFrame; 121 | productReference = 4A696198212C0E6D00FC634A /* SwiftHandyFrame.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 4A696190212C0E6D00FC634A /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastSwiftUpdateCheck = 0940; 131 | LastUpgradeCheck = 0940; 132 | ORGANIZATIONNAME = casa; 133 | TargetAttributes = { 134 | 4A696197212C0E6D00FC634A = { 135 | CreatedOnToolsVersion = 9.4.1; 136 | LastSwiftMigration = 1100; 137 | }; 138 | }; 139 | }; 140 | buildConfigurationList = 4A696193212C0E6D00FC634A /* Build configuration list for PBXProject "SwiftHandyFrame" */; 141 | compatibilityVersion = "Xcode 9.3"; 142 | developmentRegion = en; 143 | hasScannedForEncodings = 0; 144 | knownRegions = ( 145 | en, 146 | Base, 147 | ); 148 | mainGroup = 4A69618F212C0E6D00FC634A; 149 | productRefGroup = 4A696199212C0E6D00FC634A /* Products */; 150 | projectDirPath = ""; 151 | projectRoot = ""; 152 | targets = ( 153 | 4A696197212C0E6D00FC634A /* SwiftHandyFrame */, 154 | ); 155 | }; 156 | /* End PBXProject section */ 157 | 158 | /* Begin PBXResourcesBuildPhase section */ 159 | 4A696196212C0E6D00FC634A /* Resources */ = { 160 | isa = PBXResourcesBuildPhase; 161 | buildActionMask = 2147483647; 162 | files = ( 163 | 4A6961A6212C0E6D00FC634A /* LaunchScreen.storyboard in Resources */, 164 | 4A6961A3212C0E6D00FC634A /* Assets.xcassets in Resources */, 165 | 4A6961A1212C0E6D00FC634A /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 9722CC1A190D86E00EE43D09 /* [CP] Check Pods Manifest.lock */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | inputFileListPaths = ( 178 | ); 179 | inputPaths = ( 180 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 181 | "${PODS_ROOT}/Manifest.lock", 182 | ); 183 | name = "[CP] Check Pods Manifest.lock"; 184 | outputFileListPaths = ( 185 | ); 186 | outputPaths = ( 187 | "$(DERIVED_FILE_DIR)/Pods-SwiftHandyFrame-checkManifestLockResult.txt", 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | shellPath = /bin/sh; 191 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 192 | showEnvVarsInLog = 0; 193 | }; 194 | /* End PBXShellScriptBuildPhase section */ 195 | 196 | /* Begin PBXSourcesBuildPhase section */ 197 | 4A696194212C0E6D00FC634A /* Sources */ = { 198 | isa = PBXSourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 4A6961AF212C0F9600FC634A /* SwiftHandyFrame.swift in Sources */, 202 | 4A69619E212C0E6D00FC634A /* ViewController.swift in Sources */, 203 | 4A69619C212C0E6D00FC634A /* AppDelegate.swift in Sources */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | /* End PBXSourcesBuildPhase section */ 208 | 209 | /* Begin PBXVariantGroup section */ 210 | 4A69619F212C0E6D00FC634A /* Main.storyboard */ = { 211 | isa = PBXVariantGroup; 212 | children = ( 213 | 4A6961A0212C0E6D00FC634A /* Base */, 214 | ); 215 | name = Main.storyboard; 216 | sourceTree = ""; 217 | }; 218 | 4A6961A4212C0E6D00FC634A /* LaunchScreen.storyboard */ = { 219 | isa = PBXVariantGroup; 220 | children = ( 221 | 4A6961A5212C0E6D00FC634A /* Base */, 222 | ); 223 | name = LaunchScreen.storyboard; 224 | sourceTree = ""; 225 | }; 226 | /* End PBXVariantGroup section */ 227 | 228 | /* Begin XCBuildConfiguration section */ 229 | 4A6961A8212C0E6D00FC634A /* Debug */ = { 230 | isa = XCBuildConfiguration; 231 | buildSettings = { 232 | ALWAYS_SEARCH_USER_PATHS = NO; 233 | CLANG_ANALYZER_NONNULL = YES; 234 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 236 | CLANG_CXX_LIBRARY = "libc++"; 237 | CLANG_ENABLE_MODULES = YES; 238 | CLANG_ENABLE_OBJC_ARC = YES; 239 | CLANG_ENABLE_OBJC_WEAK = YES; 240 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 241 | CLANG_WARN_BOOL_CONVERSION = YES; 242 | CLANG_WARN_COMMA = YES; 243 | CLANG_WARN_CONSTANT_CONVERSION = YES; 244 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 245 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 246 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 247 | CLANG_WARN_EMPTY_BODY = YES; 248 | CLANG_WARN_ENUM_CONVERSION = YES; 249 | CLANG_WARN_INFINITE_RECURSION = YES; 250 | CLANG_WARN_INT_CONVERSION = YES; 251 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 252 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 253 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 255 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 256 | CLANG_WARN_STRICT_PROTOTYPES = YES; 257 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 258 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 259 | CLANG_WARN_UNREACHABLE_CODE = YES; 260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 261 | CODE_SIGN_IDENTITY = "iPhone Developer"; 262 | COPY_PHASE_STRIP = NO; 263 | DEBUG_INFORMATION_FORMAT = dwarf; 264 | ENABLE_STRICT_OBJC_MSGSEND = YES; 265 | ENABLE_TESTABILITY = YES; 266 | GCC_C_LANGUAGE_STANDARD = gnu11; 267 | GCC_DYNAMIC_NO_PIC = NO; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_OPTIMIZATION_LEVEL = 0; 270 | GCC_PREPROCESSOR_DEFINITIONS = ( 271 | "DEBUG=1", 272 | "$(inherited)", 273 | ); 274 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 275 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 276 | GCC_WARN_UNDECLARED_SELECTOR = YES; 277 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 278 | GCC_WARN_UNUSED_FUNCTION = YES; 279 | GCC_WARN_UNUSED_VARIABLE = YES; 280 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 281 | MTL_ENABLE_DEBUG_INFO = YES; 282 | ONLY_ACTIVE_ARCH = YES; 283 | SDKROOT = iphoneos; 284 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 285 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 286 | }; 287 | name = Debug; 288 | }; 289 | 4A6961A9212C0E6D00FC634A /* Release */ = { 290 | isa = XCBuildConfiguration; 291 | buildSettings = { 292 | ALWAYS_SEARCH_USER_PATHS = NO; 293 | CLANG_ANALYZER_NONNULL = YES; 294 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 295 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 296 | CLANG_CXX_LIBRARY = "libc++"; 297 | CLANG_ENABLE_MODULES = YES; 298 | CLANG_ENABLE_OBJC_ARC = YES; 299 | CLANG_ENABLE_OBJC_WEAK = YES; 300 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 301 | CLANG_WARN_BOOL_CONVERSION = YES; 302 | CLANG_WARN_COMMA = YES; 303 | CLANG_WARN_CONSTANT_CONVERSION = YES; 304 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 305 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 306 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 307 | CLANG_WARN_EMPTY_BODY = YES; 308 | CLANG_WARN_ENUM_CONVERSION = YES; 309 | CLANG_WARN_INFINITE_RECURSION = YES; 310 | CLANG_WARN_INT_CONVERSION = YES; 311 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 312 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 313 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 314 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 315 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 316 | CLANG_WARN_STRICT_PROTOTYPES = YES; 317 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 318 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 319 | CLANG_WARN_UNREACHABLE_CODE = YES; 320 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 321 | CODE_SIGN_IDENTITY = "iPhone Developer"; 322 | COPY_PHASE_STRIP = NO; 323 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 324 | ENABLE_NS_ASSERTIONS = NO; 325 | ENABLE_STRICT_OBJC_MSGSEND = YES; 326 | GCC_C_LANGUAGE_STANDARD = gnu11; 327 | GCC_NO_COMMON_BLOCKS = YES; 328 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 329 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 330 | GCC_WARN_UNDECLARED_SELECTOR = YES; 331 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 332 | GCC_WARN_UNUSED_FUNCTION = YES; 333 | GCC_WARN_UNUSED_VARIABLE = YES; 334 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 335 | MTL_ENABLE_DEBUG_INFO = NO; 336 | SDKROOT = iphoneos; 337 | SWIFT_COMPILATION_MODE = wholemodule; 338 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 339 | VALIDATE_PRODUCT = YES; 340 | }; 341 | name = Release; 342 | }; 343 | 4A6961AB212C0E6D00FC634A /* Debug */ = { 344 | isa = XCBuildConfiguration; 345 | baseConfigurationReference = 9EC1B1A74BDED01764FEAA8C /* Pods-SwiftHandyFrame.debug.xcconfig */; 346 | buildSettings = { 347 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 348 | CODE_SIGN_STYLE = Automatic; 349 | DEVELOPMENT_TEAM = THS9SRQVES; 350 | INFOPLIST_FILE = SwiftHandyFrame/Info.plist; 351 | LD_RUNPATH_SEARCH_PATHS = ( 352 | "$(inherited)", 353 | "@executable_path/Frameworks", 354 | ); 355 | PRODUCT_BUNDLE_IDENTIFIER = casa.SwiftHandyFrame; 356 | PRODUCT_NAME = "$(TARGET_NAME)"; 357 | SWIFT_VERSION = 5.0; 358 | TARGETED_DEVICE_FAMILY = "1,2"; 359 | }; 360 | name = Debug; 361 | }; 362 | 4A6961AC212C0E6D00FC634A /* Release */ = { 363 | isa = XCBuildConfiguration; 364 | baseConfigurationReference = B2A6A3DCB710019AB276AB9A /* Pods-SwiftHandyFrame.release.xcconfig */; 365 | buildSettings = { 366 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 367 | CODE_SIGN_STYLE = Automatic; 368 | DEVELOPMENT_TEAM = THS9SRQVES; 369 | INFOPLIST_FILE = SwiftHandyFrame/Info.plist; 370 | LD_RUNPATH_SEARCH_PATHS = ( 371 | "$(inherited)", 372 | "@executable_path/Frameworks", 373 | ); 374 | PRODUCT_BUNDLE_IDENTIFIER = casa.SwiftHandyFrame; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | SWIFT_VERSION = 5.0; 377 | TARGETED_DEVICE_FAMILY = "1,2"; 378 | }; 379 | name = Release; 380 | }; 381 | /* End XCBuildConfiguration section */ 382 | 383 | /* Begin XCConfigurationList section */ 384 | 4A696193212C0E6D00FC634A /* Build configuration list for PBXProject "SwiftHandyFrame" */ = { 385 | isa = XCConfigurationList; 386 | buildConfigurations = ( 387 | 4A6961A8212C0E6D00FC634A /* Debug */, 388 | 4A6961A9212C0E6D00FC634A /* Release */, 389 | ); 390 | defaultConfigurationIsVisible = 0; 391 | defaultConfigurationName = Release; 392 | }; 393 | 4A6961AA212C0E6D00FC634A /* Build configuration list for PBXNativeTarget "SwiftHandyFrame" */ = { 394 | isa = XCConfigurationList; 395 | buildConfigurations = ( 396 | 4A6961AB212C0E6D00FC634A /* Debug */, 397 | 4A6961AC212C0E6D00FC634A /* Release */, 398 | ); 399 | defaultConfigurationIsVisible = 0; 400 | defaultConfigurationName = Release; 401 | }; 402 | /* End XCConfigurationList section */ 403 | }; 404 | rootObject = 4A696190212C0E6D00FC634A /* Project object */; 405 | } 406 | -------------------------------------------------------------------------------- /SwiftHandyFrame.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftHandyFrame.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftHandyFrame.xcodeproj/xcuserdata/casa.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SwiftHandyFrame.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | SwiftHandyFrame.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /SwiftHandyFrame/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftHandyFrame 4 | // 5 | // Created by casa on 2018/8/21. 6 | // Copyright © 2018年 casa. All rights reserved. 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: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SwiftHandyFrame/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /SwiftHandyFrame/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftHandyFrame/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SwiftHandyFrame/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftHandyFrame/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /SwiftHandyFrame/SwiftHandyFrame/SwiftHandyFrame.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HandyFrame.swift 3 | // Leetcode_swift 4 | // 5 | // Created by casa on 2018/8/17. 6 | // Copyright © 2018年 casa. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class HandyFrameView { 12 | private let targetView:UIView 13 | 14 | public lazy var safeAreaBottomGap:CGFloat = { 15 | guard let superview = targetView.superview else { return 0 } 16 | if #available(iOS 11.0, *) { 17 | if superview.safeAreaLayoutGuide.layoutFrame.size.height > 0 { 18 | return superview.frame.size.height - superview.safeAreaLayoutGuide.layoutFrame.origin.y - superview.safeAreaLayoutGuide.layoutFrame.size.height 19 | } else { 20 | return 0 21 | } 22 | } else { 23 | return 0 24 | } 25 | }() 26 | 27 | public lazy var safeAreaTopGap:CGFloat = { 28 | guard let superview = targetView.superview else { return 0 } 29 | if #available(iOS 11.0, *) { 30 | return superview.safeAreaLayoutGuide.layoutFrame.origin.y 31 | } else { 32 | return 0 33 | } 34 | }() 35 | 36 | public lazy var safeAreaLeftGap:CGFloat = { 37 | guard let superview = targetView.superview else { return 0 } 38 | if #available(iOS 11.0, *) { 39 | return superview.safeAreaLayoutGuide.layoutFrame.origin.x 40 | } else { 41 | return 0 42 | } 43 | }() 44 | 45 | public lazy var safeAreaRightGap:CGFloat = { 46 | guard let superview = targetView.superview else { return 0 } 47 | if #available(iOS 11.0, *) { 48 | return superview.safeAreaLayoutGuide.layoutFrame.origin.x 49 | } else { 50 | return 0 51 | } 52 | }() 53 | 54 | init(_ targetView:UIView) { 55 | self.targetView = targetView 56 | } 57 | } 58 | 59 | public protocol HandyFrameExtended { 60 | var hf: HandyFrameView { get } 61 | } 62 | 63 | public extension HandyFrameExtended { 64 | var hf: HandyFrameView { 65 | get { 66 | return HandyFrameView(self as! UIView) 67 | } 68 | } 69 | } 70 | 71 | extension UIView: HandyFrameExtended {} 72 | 73 | // MARK: Size: width, height, size 74 | public extension HandyFrameView { 75 | func width() -> CGFloat { 76 | return targetView.frame.size.width 77 | } 78 | 79 | @discardableResult 80 | func setWidth(_ width:CGFloat) -> HandyFrameView { 81 | targetView.frame.size.width = width 82 | return self 83 | } 84 | 85 | func height() -> CGFloat { 86 | return targetView.frame.size.height 87 | } 88 | 89 | @discardableResult 90 | func setHeight(_ height:CGFloat) -> HandyFrameView { 91 | targetView.frame.size.height = height 92 | return self 93 | } 94 | 95 | func size() -> CGSize { 96 | return targetView.frame.size 97 | } 98 | 99 | @discardableResult 100 | func setSize(_ size:CGSize) -> HandyFrameView { 101 | targetView.frame.size = size 102 | return self 103 | } 104 | } 105 | 106 | // MARK: add width or height 107 | public extension HandyFrameView { 108 | enum HorizontalSide { 109 | case left,right 110 | } 111 | enum VerticalSide { 112 | case top,bottom 113 | } 114 | 115 | @discardableResult 116 | func addWidth(_ widthToAdd:CGFloat, on side:HorizontalSide) -> HandyFrameView { 117 | switch side { 118 | case .left: 119 | targetView.frame.size.width += widthToAdd 120 | targetView.frame.origin.x -= widthToAdd 121 | case .right: 122 | targetView.frame.size.width += widthToAdd 123 | } 124 | return self 125 | } 126 | 127 | @discardableResult 128 | func addHeight(_ heightToAdd:CGFloat, on side:VerticalSide) -> HandyFrameView { 129 | switch side { 130 | case .top: 131 | targetView.frame.size.height += heightToAdd 132 | targetView.frame.origin.y -= heightToAdd 133 | case .bottom: 134 | targetView.frame.size.height += heightToAdd 135 | } 136 | return self 137 | } 138 | } 139 | 140 | // MARK: Position: x, y, centerX, centerY, center 141 | public extension HandyFrameView { 142 | func x() -> CGFloat { 143 | return targetView.frame.origin.x 144 | } 145 | 146 | @discardableResult 147 | func setX(_ x:CGFloat) -> HandyFrameView { 148 | targetView.frame.origin.x = x 149 | return self 150 | } 151 | 152 | func y() -> CGFloat { 153 | return targetView.frame.origin.y 154 | } 155 | 156 | @discardableResult 157 | func setY(_ y:CGFloat) -> HandyFrameView { 158 | targetView.frame.origin.y = y 159 | return self 160 | } 161 | 162 | func centerX() -> CGFloat { 163 | return targetView.center.x 164 | } 165 | 166 | @discardableResult 167 | func setCenterX(_ centerX:CGFloat) -> HandyFrameView { 168 | targetView.center.x = centerX 169 | return self 170 | } 171 | 172 | func centerY() -> CGFloat { 173 | return targetView.center.y 174 | } 175 | 176 | @discardableResult 177 | func setCenterY(_ centerY:CGFloat) -> HandyFrameView { 178 | targetView.center.y = centerY 179 | return self 180 | } 181 | 182 | func center() -> CGPoint { 183 | return targetView.center 184 | } 185 | 186 | @discardableResult 187 | func setCenter(_ center:CGPoint) -> HandyFrameView { 188 | targetView.center = center 189 | return self 190 | } 191 | } 192 | 193 | // MARK: Center related to other view 194 | public extension HandyFrameView { 195 | @discardableResult 196 | func setCenterXEqualToView(_ view:UIView?) -> HandyFrameView { 197 | guard let view = view else { return self } 198 | 199 | let viewSuperView = view.superview ?? view 200 | let topView = fetchTopView() 201 | 202 | let viewCenterPoint = viewSuperView.convert(view.center, to:topView) 203 | let centerPoint = topView.convert(viewCenterPoint, to:targetView.superview) 204 | return setCenterX(centerPoint.x) 205 | } 206 | 207 | @discardableResult 208 | func setCenterYEqualToView(_ view:UIView?) -> HandyFrameView { 209 | guard let view = view else { return self } 210 | 211 | let viewSuperView = view.superview ?? view 212 | let topView = fetchTopView() 213 | 214 | let viewCenterPoint = viewSuperView.convert(view.center, to:topView) 215 | let centerPoint = topView.convert(viewCenterPoint, to:targetView.superview) 216 | return setCenterY(centerPoint.y) 217 | } 218 | 219 | @discardableResult 220 | func setCenterEqualToView(_ view:UIView?) -> HandyFrameView { 221 | guard let view = view else { return self } 222 | 223 | let viewSuperView = view.superview ?? view 224 | let topView = fetchTopView() 225 | 226 | let viewCenterPoint = viewSuperView.convert(view.center, to:topView) 227 | let centerPoint = topView.convert(viewCenterPoint, to:targetView.superview) 228 | return setCenter(centerPoint) 229 | } 230 | } 231 | 232 | // MARK: Positon: left, right, top, bottom 233 | public extension HandyFrameView { 234 | func left() -> CGFloat { 235 | return targetView.frame.origin.x 236 | } 237 | 238 | @discardableResult 239 | func setLeft(_ left:CGFloat, shouldResize:Bool) -> HandyFrameView { 240 | if shouldResize { 241 | targetView.frame.size.width = targetView.frame.origin.x - left + targetView.frame.size.width 242 | } 243 | targetView.frame.origin.x = left 244 | return self 245 | } 246 | 247 | func right() -> CGFloat { 248 | return targetView.frame.origin.x + targetView.frame.size.width 249 | } 250 | 251 | @discardableResult 252 | func setRight(_ right:CGFloat, shouldResize:Bool) -> HandyFrameView { 253 | if shouldResize { 254 | targetView.frame.size.width = right - targetView.frame.origin.x 255 | } else { 256 | targetView.frame.origin.x = right - targetView.frame.size.width 257 | } 258 | return self 259 | } 260 | 261 | func bottom() -> CGFloat { 262 | return targetView.frame.origin.y + targetView.frame.size.height 263 | } 264 | 265 | @discardableResult 266 | func setBottom(_ bottom:CGFloat, shouldResize:Bool) -> HandyFrameView { 267 | if shouldResize { 268 | targetView.frame.size.height = bottom - targetView.frame.origin.y 269 | } else { 270 | targetView.frame.origin.y = bottom - targetView.frame.size.height 271 | } 272 | return self 273 | } 274 | 275 | func top() -> CGFloat { 276 | return targetView.frame.origin.y 277 | } 278 | 279 | @discardableResult 280 | func setTop(_ top:CGFloat, shouldResize:Bool) -> HandyFrameView { 281 | if shouldResize { 282 | targetView.frame.size.height = targetView.frame.size.height - top + targetView.frame.origin.y 283 | } 284 | targetView.frame.origin.y = top 285 | return self 286 | } 287 | } 288 | 289 | // MARK: Position equal to view 290 | public extension HandyFrameView { 291 | @discardableResult 292 | func topEqualToView(_ view:UIView?) -> HandyFrameView { 293 | guard let view = view else { return self } 294 | 295 | let viewSuperView = view.superview ?? view 296 | let topSuperView = fetchTopView() 297 | let viewOriginPoint = viewSuperView.convert(view.frame.origin, to:topSuperView) 298 | let newOriginPoint = topSuperView.convert(viewOriginPoint, to: targetView.superview) 299 | targetView.frame.origin.y = newOriginPoint.y 300 | return self 301 | } 302 | 303 | @discardableResult 304 | func bottomEqualToView(_ view:UIView?) -> HandyFrameView { 305 | guard let view = view else { return self } 306 | 307 | let viewSuperView = view.superview ?? view 308 | let topSuperView = fetchTopView() 309 | let viewOriginPoint = viewSuperView.convert(view.frame.origin, to:topSuperView) 310 | let newOriginPoint = topSuperView.convert(viewOriginPoint, to: targetView.superview) 311 | targetView.frame.origin.y = newOriginPoint.y + view.frame.size.height - targetView.frame.size.height 312 | return self 313 | } 314 | 315 | @discardableResult 316 | func leftEqualToView(_ view:UIView?) -> HandyFrameView { 317 | guard let view = view else { return self } 318 | 319 | let viewSuperView = view.superview ?? view 320 | let topSuperView = fetchTopView() 321 | let viewOriginPoint = viewSuperView.convert(view.frame.origin, to:topSuperView) 322 | let newOriginPoint = topSuperView.convert(viewOriginPoint, to: targetView.superview) 323 | targetView.frame.origin.x = newOriginPoint.x 324 | return self 325 | } 326 | 327 | @discardableResult 328 | func rightEqualToView(_ view:UIView?) -> HandyFrameView { 329 | guard let view = view else { return self } 330 | 331 | let viewSuperView = view.superview ?? view 332 | let topSuperView = fetchTopView() 333 | let viewOriginPoint = viewSuperView.convert(view.frame.origin, to:topSuperView) 334 | let newOriginPoint = topSuperView.convert(viewOriginPoint, to: targetView.superview) 335 | targetView.frame.origin.x = newOriginPoint.x + view.frame.size.width - targetView.frame.size.width 336 | return self 337 | } 338 | } 339 | 340 | // MARK: inner gap with superview 341 | /* 342 | --------------------------------------------- 343 | | superview top | 344 | | gap | 345 | | ---------------------- | 346 | | | | | 347 | | | | | 348 | | | | | 349 | | left | VIEW | right | 350 | | gap | | gap | 351 | | | | | 352 | | | | | 353 | | ---------------------- | 354 | | bottom | 355 | | gap | 356 | --------------------------------------------- 357 | */ 358 | public extension HandyFrameView { 359 | @discardableResult 360 | func setInnerTopGap(_ topGap:CGFloat, shouldResize:Bool) -> HandyFrameView { 361 | if shouldResize { 362 | targetView.frame.size.height = targetView.frame.origin.y - topGap + targetView.frame.size.height 363 | } 364 | targetView.frame.origin.y = topGap 365 | return self 366 | } 367 | 368 | @discardableResult 369 | func setInnerBottomGap(_ bottomGap:CGFloat, shouldResize:Bool) -> HandyFrameView { 370 | guard let superview = targetView.superview else { return self } 371 | if shouldResize { 372 | targetView.frame.size.height = superview.frame.size.height - bottomGap - targetView.frame.origin.y - safeAreaBottomGap 373 | } else { 374 | targetView.frame.origin.y = superview.frame.size.height - targetView.frame.size.height - bottomGap - safeAreaBottomGap 375 | } 376 | return self 377 | } 378 | 379 | @discardableResult 380 | func setInnerLeftGap(_ leftGap:CGFloat, shouldResize:Bool) -> HandyFrameView { 381 | if shouldResize { 382 | targetView.frame.size.width = targetView.frame.origin.x - leftGap + targetView.frame.size.width 383 | } 384 | targetView.frame.origin.x = leftGap 385 | return self 386 | } 387 | 388 | @discardableResult 389 | func setInnerRightGap(_ rightGap:CGFloat, shouldResize:Bool) -> HandyFrameView { 390 | guard let superview = targetView.superview else { return self } 391 | if shouldResize { 392 | targetView.frame.size.width = superview.frame.size.width - rightGap - targetView.frame.origin.x 393 | } else { 394 | targetView.frame.origin.x = superview.frame.size.width - targetView.frame.size.width - rightGap 395 | } 396 | return self 397 | } 398 | } 399 | 400 | // MARK: gap with other view 401 | /* 402 | | | 403 | | | 404 | --------------------- 405 | top gap 406 | --- --------------------- --- 407 | | | | | 408 | | | | | 409 | | | | | 410 | | left gap | VIEW | right gap | 411 | | | | | 412 | | | | | 413 | | | | | 414 | --- --------------------- --- 415 | bottom gap 416 | --------------------- 417 | | | 418 | | | 419 | */ 420 | public extension HandyFrameView { 421 | @discardableResult 422 | func setTopGap(_ topGap:CGFloat, fromView:UIView?) -> HandyFrameView { 423 | guard let fromView = fromView else { return self } 424 | 425 | let fromViewSuperView = fromView.superview ?? fromView 426 | let topView = fetchTopView() 427 | let viewOriginPoint = fromViewSuperView.convert(fromView.frame.origin, to: topView) 428 | let newOriginPoint = topView.convert(viewOriginPoint, to:targetView.superview) 429 | targetView.frame.origin.y = newOriginPoint.y + topGap + fromView.frame.size.height 430 | return self 431 | } 432 | 433 | @discardableResult 434 | func setBottomGap(_ bottomGap:CGFloat, fromView:UIView?) -> HandyFrameView { 435 | guard let fromView = fromView else { return self } 436 | 437 | let fromViewSuperView = fromView.superview ?? fromView 438 | let fromViewOriginPoint = fromViewSuperView.convert(fromView.frame.origin, to:targetView.superview) 439 | targetView.frame.origin.y = fromViewOriginPoint.y - bottomGap - targetView.frame.size.height 440 | return self 441 | } 442 | 443 | @discardableResult 444 | func setLeftGap(_ leftGap:CGFloat, fromView:UIView?) -> HandyFrameView { 445 | guard let fromView = fromView else { return self } 446 | 447 | let fromViewSuperView = fromView.superview ?? fromView 448 | let topView = fetchTopView() 449 | let fromViewOriginPoint = fromViewSuperView.convert(fromView.frame.origin, to:topView) 450 | let newOriginPoint = topView.convert(fromViewOriginPoint, to: targetView.superview) 451 | targetView.frame.origin.x = newOriginPoint.x + leftGap + fromView.frame.size.width 452 | return self 453 | } 454 | 455 | @discardableResult 456 | func setRightGap(_ rightGap:CGFloat, fromView:UIView?) -> HandyFrameView { 457 | guard let fromView = fromView else { return self } 458 | 459 | let fromViewSuperView = fromView.superview ?? fromView 460 | let topView = fetchTopView() 461 | let fromViewOriginPoint = fromViewSuperView.convert(fromView.frame.origin, to:topView) 462 | let newOriginPoint = topView.convert(fromViewOriginPoint, to: targetView.superview) 463 | targetView.frame.origin.x = newOriginPoint.x - rightGap - fromView.frame.size.width 464 | return self 465 | } 466 | } 467 | 468 | // MARK: Fill 469 | public extension HandyFrameView { 470 | @discardableResult 471 | func fillWidth() -> HandyFrameView { 472 | guard let superview = targetView.superview else { return self } 473 | targetView.frame.size.width = superview.frame.size.width 474 | targetView.frame.origin.x = 0 475 | return self 476 | } 477 | 478 | @discardableResult 479 | func fillHeight() -> HandyFrameView { 480 | guard let superview = targetView.superview else { return self } 481 | targetView.frame.size.height = superview.frame.size.height 482 | targetView.frame.origin.y = 0 483 | return self 484 | } 485 | 486 | @discardableResult 487 | func fill() -> HandyFrameView { 488 | guard let superview = targetView.superview else { return self } 489 | let height = superview.frame.size.height - safeAreaBottomGap 490 | targetView.frame = CGRect(x: 0, y: 0, width: superview.frame.size.width, height: height) 491 | return self 492 | } 493 | } 494 | 495 | // MARK: helper methods 496 | public extension HandyFrameView { 497 | func fetchTopView() -> UIView { 498 | var result = targetView 499 | 500 | while result.superview != nil { 501 | result = result.superview ?? result 502 | } 503 | 504 | return result 505 | } 506 | } 507 | -------------------------------------------------------------------------------- /SwiftHandyFrame/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SwiftHandyFrame 4 | // 5 | // Created by casa on 2018/8/21. 6 | // Copyright © 2018年 casa. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | view.addSubview(shapeView) 16 | view.addSubview(centerPositionWithOtherView) 17 | view.addSubview(gapInOtherView) 18 | view.addSubview(gapFromOtherView) 19 | view.addSubview(positionEqualOtherView) 20 | view.addSubview(fillView) 21 | view.addSubview(containerView) 22 | containerView.addSubview(containerInnerItemView) 23 | } 24 | 25 | override func viewWillLayoutSubviews() { 26 | super.viewWillLayoutSubviews() 27 | shapeView.hf.setSize(CGSize(width: 100, height: 100)).setLeft(20, shouldResize: false).setBottom(200, shouldResize: true).setTop(10, shouldResize: false) 28 | 29 | centerPositionWithOtherView.hf.setSize(CGSize(width: 50, height: 50)).setCenterEqualToView(shapeView) 30 | 31 | gapInOtherView.frame = centerPositionWithOtherView.frame 32 | gapInOtherView.hf.setInnerTopGap(200, shouldResize: false).setInnerBottomGap(200, shouldResize: true) 33 | 34 | gapFromOtherView.frame = gapInOtherView.frame 35 | gapFromOtherView.hf.setLeftGap(10, fromView: gapInOtherView) 36 | 37 | positionEqualOtherView.frame = gapFromOtherView.frame 38 | positionEqualOtherView.hf.setInnerTopGap(500, shouldResize: true).bottomEqualToView(gapFromOtherView).setLeftGap(10, fromView: gapFromOtherView) 39 | 40 | fillView.hf.setSize(CGSize(width: 20, height: 20)).fillWidth().setCenterYEqualToView(positionEqualOtherView) 41 | 42 | containerView.hf.setSize(CGSize(width: 100, height: 100)).setCenterEqualToView(view) 43 | 44 | containerInnerItemView.hf.setWidth(50).setCenterEqualToView(containerView).setInnerBottomGap(0, shouldResize: true) 45 | } 46 | 47 | lazy var containerView:UIView = { 48 | let _containerView = UIView() 49 | _containerView.backgroundColor = .black 50 | return _containerView 51 | }() 52 | 53 | lazy var containerInnerItemView:UIView = { 54 | let _containerInnerItemView = UIView() 55 | _containerInnerItemView.backgroundColor = .red 56 | return _containerInnerItemView 57 | }() 58 | 59 | lazy var shapeView : UIView = { 60 | let _shapeView = UIView() 61 | _shapeView.backgroundColor = .red 62 | return _shapeView 63 | }() 64 | 65 | lazy var centerPositionWithOtherView : UIView = { 66 | let _centerPositionWithOtherView = UIView() 67 | _centerPositionWithOtherView.backgroundColor = .green 68 | return _centerPositionWithOtherView 69 | }() 70 | 71 | lazy var gapInOtherView : UIView = { 72 | let _gapInOtherView = UIView() 73 | _gapInOtherView.backgroundColor = .cyan 74 | return _gapInOtherView 75 | }() 76 | 77 | lazy var gapFromOtherView : UIView = { 78 | let _gapFromOtherView = UIView() 79 | _gapFromOtherView.backgroundColor = .yellow 80 | return _gapFromOtherView 81 | }() 82 | 83 | lazy var positionEqualOtherView : UIView = { 84 | let _positionEqualOtherView = UIView() 85 | _positionEqualOtherView.backgroundColor = .magenta 86 | return _positionEqualOtherView 87 | }() 88 | 89 | lazy var fillView : UIView = { 90 | let _fillView = UIView() 91 | _fillView.backgroundColor = .orange 92 | return _fillView 93 | }() 94 | } 95 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | > pod "SwiftHandyFrame" 2 | 3 | # 最佳实践 4 | 5 | 由于SwiftHandyFrame是基于frame的布局系统,所以在任何场景都可以直接修改view的布局。 6 | 7 | 但有些frame调整在计算布局的时候会依赖父view,所以为了保证能够使用完整的功能,第一次布局的时候应该在viewDidLoad的时候addSubview,在viewWillLayoutSubviews的时候使用SwiftHandyFrame进行布局。 8 | 9 | 以后修改布局就不受限制了。 10 | 11 | ```swift 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | 15 | view.addSubview(shapeView) 16 | } 17 | 18 | override func viewWillLayoutSubviews() { 19 | super.viewWillLayoutSubviews() 20 | 21 | shapeView.hf.setSize(CGSize(width: 100, height: 100)) 22 | shapeView.hf.setLeft(20, shouldResize: false) 23 | shapeView.hf.setBottom(200, shouldResize: true) 24 | shapeView.hf.setTop(10, shouldResize: false) 25 | } 26 | ``` 27 | 28 | # shouldResize的意义 29 | 30 | shouldResize是指在设置值的时候,是否要改变view的尺寸。 31 | 32 | 假设某个view的left为100,width为100: 33 | 34 | ``` 35 | 36 | left=100 37 | width=100 38 | 39 | ----------------------------------------------------- 40 | | | 41 | | --------width:100------ | 42 | | | | | 43 | | | | | 44 | | | | | 45 | | left:100 | | 46 | | | | | 47 | | | | | 48 | | | | | 49 | | | | | 50 | | ----------------------- | 51 | | | 52 | ----------------------------------------------------- 53 | ``` 54 | 55 | 当我们设置left为10,shouldResize为false的时候,相当于把这个view往左挪到了left为10的地方: 56 | 57 | ``` 58 | 59 | left=10 60 | width=100 61 | shouldResize=false 62 | 63 | ----------------------------------------------------- 64 | | | 65 | | --------width:100------ | 66 | | | | | 67 | | | | | 68 | | | | | 69 | | left:10 | | 70 | | | | | 71 | | | | | 72 | | | | | 73 | | | | | 74 | | ----------------------- | 75 | | | 76 | ----------------------------------------------------- 77 | ``` 78 | 79 | 当我们设置left为10,shouldResize为true的时候,相当于把这个view延展到了left为10的地方: 80 | 81 | ``` 82 | 83 | left=10 84 | width=190 85 | shouldResize=true 86 | 87 | ----------------------------------------------------- 88 | | | 89 | | -------------width:190------------ | 90 | | | | | 91 | | | | | 92 | | | | | 93 | | | | | 94 | | left:10 | | 95 | | | | | 96 | | | | | 97 | | | | | 98 | | | | | 99 | | ---------------------------------- | 100 | | | 101 | ----------------------------------------------------- 102 | ``` 103 | 104 | # view的尺寸 105 | 106 | ## 基础方法(width, height, size) 107 | ```swift 108 | 109 | // 基础方法 110 | let width = view.hf.width() 111 | view.hf.setWidth(20) 112 | 113 | let height = view.hf.height() 114 | view.hf.setHeight(20) 115 | 116 | let size = view.hf.size() 117 | view.hf.setSize(CGSize(width: 10, height: 20)) 118 | ``` 119 | 120 | ## 修改宽度高度(width, height) 121 | 122 | ```swift 123 | // 修改宽度高度 124 | view.hf.addWidth(10, on: .left) 125 | view.hf.addWidth(10, on: .right) 126 | 127 | view.hf.addHeight(10, on: .top) 128 | view.hf.addHeight(10, on: .bottom) 129 | ``` 130 | 131 | ## 填满父view 132 | 133 | ```swift 134 | view.hf.fillWidth() // 宽度填满父view 135 | view.hf.fillHeight() // 高度填满父view 136 | view.hf.fill() // 宽度和高度都填满父view 137 | ``` 138 | 139 | # view的位置 140 | 141 | ## 基础方法(x, y, center, centerX, centerY) 142 | 143 | ```swift 144 | let x = view.hf.x() 145 | view.hf.setX(10) 146 | 147 | let y = view.hf.y() 148 | view.hf.setY(10) 149 | 150 | let center = view.hf.center() 151 | view.hf.setCenter(CGPoint(x: 10, y: 10)) 152 | 153 | let centerX = view.hf.centerX() 154 | view.hf.setCenterX(10) 155 | 156 | let centerY = view.hf.centerY() 157 | view.hf.setCenterY(10) 158 | ``` 159 | 160 | ## Center与其它View的Center相等 161 | 162 | ```swift 163 | 164 | // otherView为UIView 165 | 166 | view.hf.setCenterEqualToView(otherView) 167 | view.hf.setCenterXEqualToView(otherView) 168 | view.hf.setCenterYEqualToView(otherView) 169 | ``` 170 | 171 | ## 根据View的left,right,top, bottom调整位置 172 | 173 | ```swift 174 | let left = view.hf.left() 175 | view.hf.setLeft(10, shouldResize:true) 176 | 177 | let right = view.hf.right() 178 | view.hf.setRight(10, shouldResize:true) 179 | 180 | let bottom = view.hf.bottom() 181 | view.hf.setBottom(10, shouldResize:false) 182 | 183 | let top = view.hf.top() 184 | view.hf.setTop(10, shouldResize:false) 185 | ``` 186 | 187 | ## 使View的left,right,top,bottom等于另一个View 188 | 189 | ```swift 190 | view.hf.topEqualToView(otherView) 191 | view.hf.bottomEqualToView(otherView) 192 | view.hf.leftEqualToView(otherView) 193 | view.hf.rightEqualToView(otherView) 194 | ``` 195 | 196 | ## 让view的left,right,top,bottom相对于自己的父view做配置 197 | 198 | ``` 199 | --------------------------------------------- 200 | | superview top | 201 | | gap | 202 | | ---------------------- | 203 | | | | | 204 | | | | | 205 | | | | | 206 | | left | VIEW | right | 207 | | gap | | gap | 208 | | | | | 209 | | | | | 210 | | ---------------------- | 211 | | bottom | 212 | | gap | 213 | --------------------------------------------- 214 | ``` 215 | 216 | ```swift 217 | view.hf.setInnerTopGap(10, shouldResize:true) 218 | view.hf.setInnerBottomGap(10, shouldResize:true) 219 | view.hf.setInnerLeftGap(10, shouldResize:false) 220 | view.hf.setInnerRightGap(10, shouldResize:false) 221 | ``` 222 | 223 | ## 让view的left,right,top,bottom相对于其它不是父view的view做配置 224 | 225 | ``` 226 | | | 227 | | view | 228 | --------------------- 229 | top gap 230 | --- --------------------- --- 231 | | | | | 232 | | | | | 233 | | | | | 234 | view | left gap | VIEW | right gap | view 235 | | | | | 236 | | | | | 237 | | | | | 238 | --- --------------------- --- 239 | bottom gap 240 | --------------------- 241 | | view | 242 | | | 243 | ``` 244 | 245 | ```swift 246 | // otherView是UIView 247 | view.hf.setTopGap(10, fromView:otherView) 248 | view.hf.setBottomGap(10, fromView:otherView) 249 | view.hf.setLeftGap(10, fromView:otherView) 250 | view.hf.setRightGap(10, fromView:otherView) 251 | ``` 252 | -------------------------------------------------------------------------------- /sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casatwy/SwiftHandyFrame/f5dc0fe2c73e88a7e84d5b0a17f15d27bbb76d64/sample.png -------------------------------------------------------------------------------- /upload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | git stash 4 | git pull origin master --tags 5 | git stash pop 6 | 7 | VersionString=`grep -E 's.version.*=' SwiftHandyFrame.podspec` 8 | VersionNumber=`tr -cd 0-9 <<<"$VersionString"` 9 | 10 | NewVersionNumber=$(($VersionNumber + 1)) 11 | LineNumber=`grep -nE 's.version.*=' SwiftHandyFrame.podspec | cut -d : -f1` 12 | sed -i "" "${LineNumber}s/${VersionNumber}/${NewVersionNumber}/g" SwiftHandyFrame.podspec 13 | 14 | echo "current version is ${VersionNumber}, new version is ${NewVersionNumber}" 15 | 16 | git add . 17 | git commit -am ${NewVersionNumber} 18 | git tag ${NewVersionNumber} 19 | git push origin master --tags 20 | pod trunk push ./SwiftHandyFrame.podspec --verbose --use-libraries --allow-warnings 21 | 22 | --------------------------------------------------------------------------------