├── DetectFakeLocation.xcodeproj └── project.pbxproj ├── DetectFakeLocation ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── DetectFakeLocation │ ├── CLLocationManager+DetectFakeLocation.h │ ├── CLLocationManager+DetectFakeLocation.m │ ├── MWFakeLocationDetectUtil.h │ └── MWFakeLocationDetectUtil.m ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m ├── Podfile ├── Pods ├── Masonry │ ├── LICENSE │ ├── Masonry │ │ ├── MASCompositeConstraint.h │ │ ├── MASCompositeConstraint.m │ │ ├── MASConstraint+Private.h │ │ ├── MASConstraint.h │ │ ├── MASConstraint.m │ │ ├── MASConstraintMaker.h │ │ ├── MASConstraintMaker.m │ │ ├── MASLayoutConstraint.h │ │ ├── MASLayoutConstraint.m │ │ ├── MASUtilities.h │ │ ├── MASViewAttribute.h │ │ ├── MASViewAttribute.m │ │ ├── MASViewConstraint.h │ │ ├── MASViewConstraint.m │ │ ├── Masonry.h │ │ ├── NSArray+MASAdditions.h │ │ ├── NSArray+MASAdditions.m │ │ ├── NSArray+MASShorthandAdditions.h │ │ ├── NSLayoutConstraint+MASDebugAdditions.h │ │ ├── NSLayoutConstraint+MASDebugAdditions.m │ │ ├── View+MASAdditions.h │ │ ├── View+MASAdditions.m │ │ ├── View+MASShorthandAdditions.h │ │ ├── ViewController+MASAdditions.h │ │ └── ViewController+MASAdditions.m │ └── README.md ├── Pods.xcodeproj │ └── project.pbxproj └── Target Support Files │ ├── Masonry │ ├── Masonry-Info.plist │ ├── Masonry-dummy.m │ ├── Masonry-prefix.pch │ ├── Masonry-umbrella.h │ ├── Masonry.debug.xcconfig │ ├── Masonry.modulemap │ └── Masonry.release.xcconfig │ └── Pods-DetectFakeLocation │ ├── Pods-DetectFakeLocation-Info.plist │ ├── Pods-DetectFakeLocation-acknowledgements.markdown │ ├── Pods-DetectFakeLocation-acknowledgements.plist │ ├── Pods-DetectFakeLocation-dummy.m │ ├── Pods-DetectFakeLocation-frameworks-Debug-input-files.xcfilelist │ ├── Pods-DetectFakeLocation-frameworks-Debug-output-files.xcfilelist │ ├── Pods-DetectFakeLocation-frameworks-Release-input-files.xcfilelist │ ├── Pods-DetectFakeLocation-frameworks-Release-output-files.xcfilelist │ ├── Pods-DetectFakeLocation-frameworks.sh │ ├── Pods-DetectFakeLocation-umbrella.h │ ├── Pods-DetectFakeLocation.debug.xcconfig │ ├── Pods-DetectFakeLocation.modulemap │ └── Pods-DetectFakeLocation.release.xcconfig └── README.md /DetectFakeLocation.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 48AD485526BD24F50096711B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 48AD485426BD24F50096711B /* AppDelegate.m */; }; 11 | 48AD485B26BD24F50096711B /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 48AD485A26BD24F50096711B /* ViewController.m */; }; 12 | 48AD486026BD24F70096711B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 48AD485F26BD24F70096711B /* Assets.xcassets */; }; 13 | 48AD486326BD24F70096711B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 48AD486126BD24F70096711B /* LaunchScreen.storyboard */; }; 14 | 48AD486626BD24F70096711B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 48AD486526BD24F70096711B /* main.m */; }; 15 | 48AD487126BD27860096711B /* MWFakeLocationDetectUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 48AD486D26BD27860096711B /* MWFakeLocationDetectUtil.m */; }; 16 | 48AD487226BD27860096711B /* CLLocationManager+DetectFakeLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 48AD486E26BD27860096711B /* CLLocationManager+DetectFakeLocation.m */; }; 17 | CDEA0EB0A1F0BC2EFD427CE1 /* Pods_DetectFakeLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 47985B2C0B4A485D79396101 /* Pods_DetectFakeLocation.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 35B973EF03B284D12639148E /* Pods-DetectFakeLocation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DetectFakeLocation.release.xcconfig"; path = "Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation.release.xcconfig"; sourceTree = ""; }; 22 | 47985B2C0B4A485D79396101 /* Pods_DetectFakeLocation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DetectFakeLocation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 48AD485026BD24F50096711B /* DetectFakeLocation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DetectFakeLocation.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | 48AD485326BD24F50096711B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 25 | 48AD485426BD24F50096711B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 26 | 48AD485926BD24F50096711B /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 27 | 48AD485A26BD24F50096711B /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 28 | 48AD485F26BD24F70096711B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | 48AD486226BD24F70096711B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | 48AD486426BD24F70096711B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 48AD486526BD24F70096711B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32 | 48AD486D26BD27860096711B /* MWFakeLocationDetectUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MWFakeLocationDetectUtil.m; sourceTree = ""; }; 33 | 48AD486E26BD27860096711B /* CLLocationManager+DetectFakeLocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CLLocationManager+DetectFakeLocation.m"; sourceTree = ""; }; 34 | 48AD486F26BD27860096711B /* MWFakeLocationDetectUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWFakeLocationDetectUtil.h; sourceTree = ""; }; 35 | 48AD487026BD27860096711B /* CLLocationManager+DetectFakeLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CLLocationManager+DetectFakeLocation.h"; sourceTree = ""; }; 36 | BAE1BFBABFE9E8D8FB6D6E1F /* Pods-DetectFakeLocation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DetectFakeLocation.debug.xcconfig"; path = "Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation.debug.xcconfig"; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 48AD484D26BD24F50096711B /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | CDEA0EB0A1F0BC2EFD427CE1 /* Pods_DetectFakeLocation.framework in Frameworks */, 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXFrameworksBuildPhase section */ 49 | 50 | /* Begin PBXGroup section */ 51 | 48AD484726BD24F40096711B = { 52 | isa = PBXGroup; 53 | children = ( 54 | 48AD485226BD24F50096711B /* DetectFakeLocation */, 55 | 48AD485126BD24F50096711B /* Products */, 56 | 6CC79C6E7BED5694B29D0FF0 /* Pods */, 57 | E7164FCE5A2A05A0AF95506B /* Frameworks */, 58 | ); 59 | sourceTree = ""; 60 | }; 61 | 48AD485126BD24F50096711B /* Products */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | 48AD485026BD24F50096711B /* DetectFakeLocation.app */, 65 | ); 66 | name = Products; 67 | sourceTree = ""; 68 | }; 69 | 48AD485226BD24F50096711B /* DetectFakeLocation */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 48AD486C26BD27860096711B /* DetectFakeLocation */, 73 | 48AD485326BD24F50096711B /* AppDelegate.h */, 74 | 48AD485426BD24F50096711B /* AppDelegate.m */, 75 | 48AD485926BD24F50096711B /* ViewController.h */, 76 | 48AD485A26BD24F50096711B /* ViewController.m */, 77 | 48AD485F26BD24F70096711B /* Assets.xcassets */, 78 | 48AD486126BD24F70096711B /* LaunchScreen.storyboard */, 79 | 48AD486426BD24F70096711B /* Info.plist */, 80 | 48AD486526BD24F70096711B /* main.m */, 81 | ); 82 | path = DetectFakeLocation; 83 | sourceTree = ""; 84 | }; 85 | 48AD486C26BD27860096711B /* DetectFakeLocation */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 48AD486F26BD27860096711B /* MWFakeLocationDetectUtil.h */, 89 | 48AD486D26BD27860096711B /* MWFakeLocationDetectUtil.m */, 90 | 48AD487026BD27860096711B /* CLLocationManager+DetectFakeLocation.h */, 91 | 48AD486E26BD27860096711B /* CLLocationManager+DetectFakeLocation.m */, 92 | ); 93 | path = DetectFakeLocation; 94 | sourceTree = ""; 95 | }; 96 | 6CC79C6E7BED5694B29D0FF0 /* Pods */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | BAE1BFBABFE9E8D8FB6D6E1F /* Pods-DetectFakeLocation.debug.xcconfig */, 100 | 35B973EF03B284D12639148E /* Pods-DetectFakeLocation.release.xcconfig */, 101 | ); 102 | path = Pods; 103 | sourceTree = ""; 104 | }; 105 | E7164FCE5A2A05A0AF95506B /* Frameworks */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 47985B2C0B4A485D79396101 /* Pods_DetectFakeLocation.framework */, 109 | ); 110 | name = Frameworks; 111 | sourceTree = ""; 112 | }; 113 | /* End PBXGroup section */ 114 | 115 | /* Begin PBXNativeTarget section */ 116 | 48AD484F26BD24F50096711B /* DetectFakeLocation */ = { 117 | isa = PBXNativeTarget; 118 | buildConfigurationList = 48AD486926BD24F70096711B /* Build configuration list for PBXNativeTarget "DetectFakeLocation" */; 119 | buildPhases = ( 120 | F5E4B80FEB44AC21A81912B7 /* [CP] Check Pods Manifest.lock */, 121 | 48AD484C26BD24F50096711B /* Sources */, 122 | 48AD484D26BD24F50096711B /* Frameworks */, 123 | 48AD484E26BD24F50096711B /* Resources */, 124 | C564149E0C6FCF09432D7728 /* [CP] Embed Pods Frameworks */, 125 | ); 126 | buildRules = ( 127 | ); 128 | dependencies = ( 129 | ); 130 | name = DetectFakeLocation; 131 | productName = DetectFakeLocation; 132 | productReference = 48AD485026BD24F50096711B /* DetectFakeLocation.app */; 133 | productType = "com.apple.product-type.application"; 134 | }; 135 | /* End PBXNativeTarget section */ 136 | 137 | /* Begin PBXProject section */ 138 | 48AD484826BD24F40096711B /* Project object */ = { 139 | isa = PBXProject; 140 | attributes = { 141 | LastUpgradeCheck = 1250; 142 | TargetAttributes = { 143 | 48AD484F26BD24F50096711B = { 144 | CreatedOnToolsVersion = 12.5.1; 145 | }; 146 | }; 147 | }; 148 | buildConfigurationList = 48AD484B26BD24F40096711B /* Build configuration list for PBXProject "DetectFakeLocation" */; 149 | compatibilityVersion = "Xcode 9.3"; 150 | developmentRegion = en; 151 | hasScannedForEncodings = 0; 152 | knownRegions = ( 153 | en, 154 | Base, 155 | ); 156 | mainGroup = 48AD484726BD24F40096711B; 157 | productRefGroup = 48AD485126BD24F50096711B /* Products */; 158 | projectDirPath = ""; 159 | projectRoot = ""; 160 | targets = ( 161 | 48AD484F26BD24F50096711B /* DetectFakeLocation */, 162 | ); 163 | }; 164 | /* End PBXProject section */ 165 | 166 | /* Begin PBXResourcesBuildPhase section */ 167 | 48AD484E26BD24F50096711B /* Resources */ = { 168 | isa = PBXResourcesBuildPhase; 169 | buildActionMask = 2147483647; 170 | files = ( 171 | 48AD486326BD24F70096711B /* LaunchScreen.storyboard in Resources */, 172 | 48AD486026BD24F70096711B /* Assets.xcassets in Resources */, 173 | ); 174 | runOnlyForDeploymentPostprocessing = 0; 175 | }; 176 | /* End PBXResourcesBuildPhase section */ 177 | 178 | /* Begin PBXShellScriptBuildPhase section */ 179 | C564149E0C6FCF09432D7728 /* [CP] Embed Pods Frameworks */ = { 180 | isa = PBXShellScriptBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | ); 184 | inputFileListPaths = ( 185 | "${PODS_ROOT}/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-frameworks-${CONFIGURATION}-input-files.xcfilelist", 186 | ); 187 | name = "[CP] Embed Pods Frameworks"; 188 | outputFileListPaths = ( 189 | "${PODS_ROOT}/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-frameworks-${CONFIGURATION}-output-files.xcfilelist", 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | shellPath = /bin/sh; 193 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-frameworks.sh\"\n"; 194 | showEnvVarsInLog = 0; 195 | }; 196 | F5E4B80FEB44AC21A81912B7 /* [CP] Check Pods Manifest.lock */ = { 197 | isa = PBXShellScriptBuildPhase; 198 | buildActionMask = 2147483647; 199 | files = ( 200 | ); 201 | inputFileListPaths = ( 202 | ); 203 | inputPaths = ( 204 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 205 | "${PODS_ROOT}/Manifest.lock", 206 | ); 207 | name = "[CP] Check Pods Manifest.lock"; 208 | outputFileListPaths = ( 209 | ); 210 | outputPaths = ( 211 | "$(DERIVED_FILE_DIR)/Pods-DetectFakeLocation-checkManifestLockResult.txt", 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | shellPath = /bin/sh; 215 | 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"; 216 | showEnvVarsInLog = 0; 217 | }; 218 | /* End PBXShellScriptBuildPhase section */ 219 | 220 | /* Begin PBXSourcesBuildPhase section */ 221 | 48AD484C26BD24F50096711B /* Sources */ = { 222 | isa = PBXSourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | 48AD487126BD27860096711B /* MWFakeLocationDetectUtil.m in Sources */, 226 | 48AD487226BD27860096711B /* CLLocationManager+DetectFakeLocation.m in Sources */, 227 | 48AD485B26BD24F50096711B /* ViewController.m in Sources */, 228 | 48AD485526BD24F50096711B /* AppDelegate.m in Sources */, 229 | 48AD486626BD24F70096711B /* main.m in Sources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXSourcesBuildPhase section */ 234 | 235 | /* Begin PBXVariantGroup section */ 236 | 48AD486126BD24F70096711B /* LaunchScreen.storyboard */ = { 237 | isa = PBXVariantGroup; 238 | children = ( 239 | 48AD486226BD24F70096711B /* Base */, 240 | ); 241 | name = LaunchScreen.storyboard; 242 | sourceTree = ""; 243 | }; 244 | /* End PBXVariantGroup section */ 245 | 246 | /* Begin XCBuildConfiguration section */ 247 | 48AD486726BD24F70096711B /* Debug */ = { 248 | isa = XCBuildConfiguration; 249 | buildSettings = { 250 | ALWAYS_SEARCH_USER_PATHS = NO; 251 | CLANG_ANALYZER_NONNULL = YES; 252 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 253 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 254 | CLANG_CXX_LIBRARY = "libc++"; 255 | CLANG_ENABLE_MODULES = YES; 256 | CLANG_ENABLE_OBJC_ARC = YES; 257 | CLANG_ENABLE_OBJC_WEAK = YES; 258 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 259 | CLANG_WARN_BOOL_CONVERSION = YES; 260 | CLANG_WARN_COMMA = YES; 261 | CLANG_WARN_CONSTANT_CONVERSION = YES; 262 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 263 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 264 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 265 | CLANG_WARN_EMPTY_BODY = YES; 266 | CLANG_WARN_ENUM_CONVERSION = YES; 267 | CLANG_WARN_INFINITE_RECURSION = YES; 268 | CLANG_WARN_INT_CONVERSION = YES; 269 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 270 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 271 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 272 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 273 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 274 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 275 | CLANG_WARN_STRICT_PROTOTYPES = YES; 276 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 277 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 278 | CLANG_WARN_UNREACHABLE_CODE = YES; 279 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 280 | COPY_PHASE_STRIP = NO; 281 | DEBUG_INFORMATION_FORMAT = dwarf; 282 | ENABLE_STRICT_OBJC_MSGSEND = YES; 283 | ENABLE_TESTABILITY = YES; 284 | GCC_C_LANGUAGE_STANDARD = gnu11; 285 | GCC_DYNAMIC_NO_PIC = NO; 286 | GCC_NO_COMMON_BLOCKS = YES; 287 | GCC_OPTIMIZATION_LEVEL = 0; 288 | GCC_PREPROCESSOR_DEFINITIONS = ( 289 | "DEBUG=1", 290 | "$(inherited)", 291 | ); 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 299 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 300 | MTL_FAST_MATH = YES; 301 | ONLY_ACTIVE_ARCH = YES; 302 | SDKROOT = iphoneos; 303 | }; 304 | name = Debug; 305 | }; 306 | 48AD486826BD24F70096711B /* Release */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | CLANG_ANALYZER_NONNULL = YES; 311 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 313 | CLANG_CXX_LIBRARY = "libc++"; 314 | CLANG_ENABLE_MODULES = YES; 315 | CLANG_ENABLE_OBJC_ARC = YES; 316 | CLANG_ENABLE_OBJC_WEAK = YES; 317 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 318 | CLANG_WARN_BOOL_CONVERSION = YES; 319 | CLANG_WARN_COMMA = YES; 320 | CLANG_WARN_CONSTANT_CONVERSION = YES; 321 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 323 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 324 | CLANG_WARN_EMPTY_BODY = YES; 325 | CLANG_WARN_ENUM_CONVERSION = YES; 326 | CLANG_WARN_INFINITE_RECURSION = YES; 327 | CLANG_WARN_INT_CONVERSION = YES; 328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 333 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 334 | CLANG_WARN_STRICT_PROTOTYPES = YES; 335 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 336 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | COPY_PHASE_STRIP = NO; 340 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 341 | ENABLE_NS_ASSERTIONS = NO; 342 | ENABLE_STRICT_OBJC_MSGSEND = YES; 343 | GCC_C_LANGUAGE_STANDARD = gnu11; 344 | GCC_NO_COMMON_BLOCKS = YES; 345 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 346 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 347 | GCC_WARN_UNDECLARED_SELECTOR = YES; 348 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 349 | GCC_WARN_UNUSED_FUNCTION = YES; 350 | GCC_WARN_UNUSED_VARIABLE = YES; 351 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 352 | MTL_ENABLE_DEBUG_INFO = NO; 353 | MTL_FAST_MATH = YES; 354 | SDKROOT = iphoneos; 355 | VALIDATE_PRODUCT = YES; 356 | }; 357 | name = Release; 358 | }; 359 | 48AD486A26BD24F70096711B /* Debug */ = { 360 | isa = XCBuildConfiguration; 361 | baseConfigurationReference = BAE1BFBABFE9E8D8FB6D6E1F /* Pods-DetectFakeLocation.debug.xcconfig */; 362 | buildSettings = { 363 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 364 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 365 | CODE_SIGN_IDENTITY = "iPhone Developer"; 366 | CODE_SIGN_STYLE = Manual; 367 | DEVELOPMENT_TEAM = QKG3ZU5CZE; 368 | INFOPLIST_FILE = DetectFakeLocation/Info.plist; 369 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 370 | LD_RUNPATH_SEARCH_PATHS = ( 371 | "$(inherited)", 372 | "@executable_path/Frameworks", 373 | ); 374 | PRODUCT_BUNDLE_IDENTIFIER = com.hongxinjianfa.DetectFakeLocation; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | PROVISIONING_PROFILE_SPECIFIER = "hongxinjianfa_*"; 377 | TARGETED_DEVICE_FAMILY = "1,2"; 378 | }; 379 | name = Debug; 380 | }; 381 | 48AD486B26BD24F70096711B /* Release */ = { 382 | isa = XCBuildConfiguration; 383 | baseConfigurationReference = 35B973EF03B284D12639148E /* Pods-DetectFakeLocation.release.xcconfig */; 384 | buildSettings = { 385 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 386 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 387 | CODE_SIGN_IDENTITY = "iPhone Developer"; 388 | CODE_SIGN_STYLE = Manual; 389 | DEVELOPMENT_TEAM = QKG3ZU5CZE; 390 | INFOPLIST_FILE = DetectFakeLocation/Info.plist; 391 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 392 | LD_RUNPATH_SEARCH_PATHS = ( 393 | "$(inherited)", 394 | "@executable_path/Frameworks", 395 | ); 396 | PRODUCT_BUNDLE_IDENTIFIER = com.hongxinjianfa.DetectFakeLocation; 397 | PRODUCT_NAME = "$(TARGET_NAME)"; 398 | PROVISIONING_PROFILE_SPECIFIER = "hongxinjianfa_*"; 399 | TARGETED_DEVICE_FAMILY = "1,2"; 400 | }; 401 | name = Release; 402 | }; 403 | /* End XCBuildConfiguration section */ 404 | 405 | /* Begin XCConfigurationList section */ 406 | 48AD484B26BD24F40096711B /* Build configuration list for PBXProject "DetectFakeLocation" */ = { 407 | isa = XCConfigurationList; 408 | buildConfigurations = ( 409 | 48AD486726BD24F70096711B /* Debug */, 410 | 48AD486826BD24F70096711B /* Release */, 411 | ); 412 | defaultConfigurationIsVisible = 0; 413 | defaultConfigurationName = Release; 414 | }; 415 | 48AD486926BD24F70096711B /* Build configuration list for PBXNativeTarget "DetectFakeLocation" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | 48AD486A26BD24F70096711B /* Debug */, 419 | 48AD486B26BD24F70096711B /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | defaultConfigurationName = Release; 423 | }; 424 | /* End XCConfigurationList section */ 425 | }; 426 | rootObject = 48AD484826BD24F40096711B /* Project object */; 427 | } 428 | -------------------------------------------------------------------------------- /DetectFakeLocation/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ForFakeLocationDetect 4 | // 5 | // Created by MorganWangon 2021/8/6. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (nonatomic, strong) UIWindow *window; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /DetectFakeLocation/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ForFakeLocationDetect 4 | // 5 | // Created by MorganWangon 2021/8/6. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | #import "ViewController.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 21 | self.window.backgroundColor = [UIColor whiteColor]; 22 | self.window.rootViewController = [[ViewController alloc] init]; 23 | [self.window makeKeyAndVisible]; 24 | 25 | return YES; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /DetectFakeLocation/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /DetectFakeLocation/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /DetectFakeLocation/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /DetectFakeLocation/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 | -------------------------------------------------------------------------------- /DetectFakeLocation/DetectFakeLocation/CLLocationManager+DetectFakeLocation.h: -------------------------------------------------------------------------------- 1 | // 2 | // CLLocationManager+ForFakeLocationDetect.h 3 | // MorganWang 4 | // 5 | // Created by MorganWangon 2021/8/6. 6 | // 7 | 8 | #import 9 | 10 | @interface CLLocationManager (ForFakeLocationDetect) 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /DetectFakeLocation/DetectFakeLocation/CLLocationManager+DetectFakeLocation.m: -------------------------------------------------------------------------------- 1 | // 2 | // CLLocationManager+ForFakeLocationDetect.m 3 | // MorganWang 4 | // 5 | // Created by MorganWangon 2021/8/6. 6 | // 7 | 8 | #import "CLLocationManager+DetectFakeLocation.h" 9 | #import 10 | #import "MWFakeLocationDetectUtil.h" 11 | #import 12 | 13 | @implementation CLLocationManager (ForFakeLocationDetect) 14 | 15 | + (void)load { 16 | static dispatch_once_t onceToken; 17 | dispatch_once(&onceToken, ^{ 18 | // 交换CoreLocation中的代理方法 19 | Method originalMethod = class_getInstanceMethod(self, @selector(setDelegate:)); 20 | Method exchangeMethod = class_getInstanceMethod(self, @selector(hk_setDelegate:)); 21 | method_exchangeImplementations(originalMethod, exchangeMethod); 22 | 23 | }); 24 | } 25 | 26 | - (void)hk_setDelegate:(id)delegate { 27 | [self hk_setDelegate:delegate]; 28 | 29 | // 获得delegate的实际调用类 30 | Class aClass = [delegate class]; 31 | // 传递给HXFakeLocationDetectUtil来交互方法 32 | [MWFakeLocationDetectUtil exchangeLocationMethods]; 33 | [MWFakeLocationDetectUtil exchangeLocationDelegateMethods:aClass]; 34 | } 35 | 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /DetectFakeLocation/DetectFakeLocation/MWFakeLocationDetectUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // MWFakeLocationDetectUtil.h 3 | // MorganWang 4 | // 5 | // Created by MorganWangon 2021/8/6. 6 | // Copyright © 2021 MorganWang. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface MWFakeLocationDetectUtil : NSObject 13 | 14 | + (instancetype)util; 15 | @property (nonatomic, copy) void(^ resultCallback)(BOOL isSuspecious); 16 | 17 | + (void)exchangeLocationDelegateMethods:(Class)aClass; 18 | + (void)exchangeLocationMethods; 19 | 20 | @end 21 | 22 | -------------------------------------------------------------------------------- /DetectFakeLocation/DetectFakeLocation/MWFakeLocationDetectUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // HXFakeLocationDetectUtil.m 3 | // MorganWang 4 | // 5 | // Created by MorganWangon 2021/8/6. 6 | // Copyright © 2021 MorganWang. All rights reserved. 7 | // 8 | 9 | #import "MWFakeLocationDetectUtil.h" 10 | #import 11 | 12 | @interface MWFakeLocationDetectUtil () 13 | 14 | @property (nonatomic, assign) NSInteger requestCount; // 回调次数 15 | @property (nonatomic, assign) BOOL isSuspecious; // 是否可疑 16 | @property (nonatomic, assign) BOOL isSuspeciousJail; // 越狱检测 17 | @property (nonatomic, assign) BOOL isSuspeciousAltitue; // 根据海拔和海拔精度检测 18 | @property (nonatomic, assign) BOOL isSuspeciousCallbackCount; // 根据回调位置检测 19 | @property (nonatomic, assign) BOOL isSuspeciousType; // 根据定位类型检测 20 | @property (nonatomic, strong) CLLocation *location; 21 | 22 | @end 23 | 24 | @implementation MWFakeLocationDetectUtil 25 | 26 | + (void)exchangeLocationDelegateMethods:(Class)aClass { 27 | // locationManager:didUpdateLocations: 是CLLocationManagerDelegate的方法,所以originalClass是传入的delegate 28 | hook_exchangeMethod(aClass, @selector(locationManager:didUpdateLocations:), 29 | [self class], @selector(hx_locationManager:didUpdateLocations:)); 30 | } 31 | 32 | + (void)exchangeLocationMethods { 33 | // startUpdatingLocation 是 CLLocationManager的方法,所以originalClass是[CLLocationManager class] 34 | hook_exchangeMethod([CLLocationManager class], @selector(startUpdatingLocation), 35 | [self class], @selector(hx_startUpdatingLocation)); 36 | } 37 | 38 | static void hook_exchangeMethod(Class originalClass, SEL originalSel, Class replacedClass, SEL replacedSel){ 39 | Method originalMethod = class_getInstanceMethod(originalClass, originalSel); 40 | Method replacedMethod = class_getInstanceMethod(replacedClass, replacedSel); 41 | IMP replacedMethodIMP = method_getImplementation(replacedMethod); 42 | // 将样替换的方法往代理类中添加, (一般都能添加成功, 因为代理类中不会有我们自定义的函数) 43 | BOOL didAddMethod = 44 | class_addMethod(originalClass, 45 | replacedSel, 46 | replacedMethodIMP, 47 | method_getTypeEncoding(replacedMethod)); 48 | 49 | if (didAddMethod) {// 添加成功 50 | NSLog(@"class_addMethod succeed --> (%@)", NSStringFromSelector(replacedSel)); 51 | // 获取新方法在代理类中的地址 52 | Method newMethod = class_getInstanceMethod(originalClass, replacedSel); 53 | // 交互原方法和自定义方法 54 | method_exchangeImplementations(originalMethod, newMethod); 55 | }else{// 如果失败, 则证明自定义方法在代理方法中, 直接交互就可以 56 | method_exchangeImplementations(originalMethod, replacedMethod); 57 | } 58 | } 59 | 60 | + (instancetype)util { 61 | static dispatch_once_t onceToken; 62 | static MWFakeLocationDetectUtil *instance = nil; 63 | dispatch_once(&onceToken, ^{ 64 | instance = [[super allocWithZone:NULL] init]; 65 | }); 66 | return instance; 67 | } 68 | 69 | - (instancetype)init { 70 | self = [super init]; 71 | if (self) { 72 | self.isSuspeciousJail = [MWFakeLocationDetectUtil isJailedDevice]; 73 | self.isSuspeciousAltitue = NO; 74 | self.isSuspeciousCallbackCount = NO; 75 | self.isSuspeciousType = NO; 76 | self.location = nil; 77 | } 78 | return self; 79 | } 80 | 81 | - (BOOL)isSuspecious { 82 | MWFakeLocationDetectUtil *util = [MWFakeLocationDetectUtil util]; 83 | return util.isSuspeciousAltitue && util.isSuspeciousType; 84 | // return util.isSuspeciousJail || 85 | // util.isSuspeciousAltitue || 86 | // util.isSuspeciousCallbackCount || 87 | // util.isSuspeciousType; 88 | } 89 | 90 | - (void)hx_startUpdatingLocation { 91 | [self hx_startUpdatingLocation]; 92 | 93 | MWFakeLocationDetectUtil *util = [MWFakeLocationDetectUtil util]; 94 | util.requestCount = 0; 95 | util.isSuspeciousAltitue = NO; 96 | util.isSuspeciousCallbackCount = NO; 97 | util.isSuspeciousType = NO; 98 | util.location = nil; 99 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.7 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{ 100 | [util checkCallbackCount]; 101 | [util checkLocationAltitudeAccuracy:util.location]; 102 | [util checkLocationType:util.location]; 103 | [util reportSuspicousLocation]; 104 | }); 105 | } 106 | 107 | - (void)hx_locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { 108 | [self hx_locationManager:manager 109 | didUpdateLocations:locations]; 110 | 111 | CLLocation *location = [locations lastObject]; 112 | 113 | MWFakeLocationDetectUtil *util = [MWFakeLocationDetectUtil util]; 114 | util.requestCount += 1; 115 | util.location = location; 116 | } 117 | 118 | /// 上报虚假 119 | - (void)reportSuspicousLocation { 120 | MWFakeLocationDetectUtil *util = [MWFakeLocationDetectUtil util]; 121 | NSLog(@"isSuspeciousJail: %d, isSuspeciousCallbackCount :%d, isSuspeciousAltitue: %d, isSuspeciousType: %d", util.isSuspeciousJail, util.isSuspeciousCallbackCount, util.isSuspeciousAltitue, util.isSuspeciousType); 122 | BOOL isSuspicousLocation = util.isSuspecious; 123 | if (isSuspicousLocation) { 124 | NSLog(@"可疑位置--------------"); 125 | } 126 | else { 127 | NSLog(@"正常定位--------------"); 128 | } 129 | if (util.resultCallback) { 130 | util.resultCallback(isSuspicousLocation); 131 | } 132 | } 133 | 134 | /// 根据回调次数判断是否是虚假定位 135 | - (void)checkCallbackCount { 136 | MWFakeLocationDetectUtil *util = [MWFakeLocationDetectUtil util]; 137 | if (util.requestCount == 1) { 138 | util.isSuspeciousCallbackCount = YES; 139 | } 140 | } 141 | 142 | /// 定位海拔、海拔垂直精度 143 | /// @param location 定位Item 144 | - (void)checkLocationAltitudeAccuracy:(CLLocation *)location { 145 | // NSLog(@"海拔高度:%f", location.altitude); 146 | // NSLog(@"海拔垂直精度:%f", location.verticalAccuracy); 147 | MWFakeLocationDetectUtil *util = [MWFakeLocationDetectUtil util]; 148 | if ((location.altitude == 0.0) && (location.verticalAccuracy == -1.0)) { 149 | util.isSuspeciousAltitue = YES; 150 | } 151 | } 152 | 153 | /// 根据type判断 154 | /// @param location 定位Item 155 | - (void)checkLocationType:(CLLocation *)location { 156 | MWFakeLocationDetectUtil *util = [MWFakeLocationDetectUtil util]; 157 | NSString *type = [location valueForKey:@"type"]; 158 | if (type) { 159 | if ((type.integerValue == 1) || (type.integerValue == 3)) { 160 | util.isSuspeciousType = YES; 161 | } 162 | } 163 | } 164 | 165 | /// 判断是否是越狱设备 166 | + (BOOL)isJailedDevice { 167 | NSString *appPathStr = @"/User/Applications"; 168 | if ([[NSFileManager defaultManager] fileExistsAtPath:appPathStr]) { 169 | NSError *error; 170 | NSArray *appList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:appPathStr error:&error]; 171 | if ((!error) && (appList != nil) && (appList.count > 0)) { 172 | return YES; 173 | } 174 | else { 175 | return NO; 176 | } 177 | } 178 | else { 179 | return NO; 180 | } 181 | } 182 | 183 | @end 184 | -------------------------------------------------------------------------------- /DetectFakeLocation/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSLocationWhenInUseUsageDescription 24 | 申请访问位置权限 25 | UIApplicationSupportsIndirectInputEvents 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /DetectFakeLocation/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ForFakeLocationDetect 4 | // 5 | // Created by MorganWangon 2021/8/6. 6 | // 7 | 8 | #import 9 | 10 | @interface ViewController : UIViewController 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /DetectFakeLocation/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ForFakeLocationDetect 4 | // 5 | // Created by MorganWangon 2021/8/6. 6 | // 7 | 8 | #import "ViewController.h" 9 | #import 10 | #import 11 | #import "MWFakeLocationDetectUtil.h" 12 | #import "CLLocationManager+DetectFakeLocation.h" 13 | 14 | @interface ViewController () 15 | 16 | @property (nonatomic, strong) CLLocationManager *locationManager; 17 | @property (nonatomic, strong) UIButton *locateButton; 18 | @property (nonatomic, strong) UILabel *resultLabel; 19 | 20 | @end 21 | 22 | @implementation ViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | // Do any additional setup after loading the view. 27 | [self setupResultLabel]; 28 | [self setupLocateButton]; 29 | [self setupLocationManager]; 30 | } 31 | 32 | #pragma mark - init 33 | - (void)setupResultLabel { 34 | if (!_resultLabel) { 35 | _resultLabel = [UILabel new]; 36 | _resultLabel.font = [UIFont systemFontOfSize:16.0]; 37 | _resultLabel.textColor = [UIColor orangeColor]; 38 | _resultLabel.textAlignment = NSTextAlignmentCenter; 39 | } 40 | [self.view addSubview:_resultLabel]; 41 | 42 | [_resultLabel mas_makeConstraints:^(MASConstraintMaker *make) { 43 | make.centerX.equalTo(self.view); 44 | make.top.equalTo(self.view).inset(80.0); 45 | }]; 46 | } 47 | 48 | - (void)setupLocateButton { 49 | if (!_locateButton) { 50 | _locateButton = [UIButton buttonWithType:UIButtonTypeCustom]; 51 | [_locateButton setTitle:@"定 位" forState:UIControlStateNormal]; 52 | [_locateButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; 53 | [_locateButton addTarget:self 54 | action:@selector(locateBtnTapped:) 55 | forControlEvents:UIControlEventTouchUpInside]; 56 | } 57 | [self.view addSubview:_locateButton]; 58 | 59 | [_locateButton mas_makeConstraints:^(MASConstraintMaker *make) { 60 | make.centerX.equalTo(self.view); 61 | make.width.mas_equalTo(200.0); 62 | make.height.mas_equalTo(64.0); 63 | make.top.equalTo(self.resultLabel.mas_bottom).inset(42.0); 64 | }]; 65 | } 66 | 67 | - (void)setupLocationManager { 68 | if (!_locationManager) { 69 | _locationManager = [[CLLocationManager alloc] init]; 70 | } 71 | _locationManager.delegate = self; 72 | [self requestLocationAuth]; 73 | } 74 | 75 | - (void)requestLocationAuth { 76 | [self.locationManager requestWhenInUseAuthorization]; 77 | } 78 | 79 | #pragma mark - utils 80 | 81 | 82 | #pragma mark - action 83 | - (void)locateBtnTapped:(UIButton *)sender { 84 | _resultLabel.text = @"定位检测..."; 85 | 86 | [self.locationManager startUpdatingLocation]; 87 | 88 | [MWFakeLocationDetectUtil util].resultCallback = ^(BOOL isSuspecious) { 89 | NSString *text = @"正常定位"; 90 | if (isSuspecious) { 91 | text = @"疑似虚拟位置"; 92 | } 93 | dispatch_async(dispatch_get_main_queue(), ^{ 94 | self.resultLabel.text = text; 95 | }); 96 | }; 97 | } 98 | 99 | #pragma mark - other 100 | 101 | #pragma mark - delegate 102 | - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { 103 | [self.locationManager stopUpdatingLocation]; 104 | } 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /DetectFakeLocation/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ForFakeLocationDetect 4 | // 5 | // Created by MorganWangon 2021/8/6. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | NSString * appDelegateClassName; 13 | @autoreleasepool { 14 | // Setup code that might create autoreleased objects goes here. 15 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 16 | } 17 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 18 | } 19 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'DetectFakeLocation' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for DetectFakeLocation 9 | pod 'Masonry' 10 | 11 | end 12 | -------------------------------------------------------------------------------- /Pods/Masonry/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2012 Masonry Team - https://github.com/Masonry 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASCompositeConstraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // MASCompositeConstraint.h 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 21/07/13. 6 | // Copyright (c) 2013 cloudling. All rights reserved. 7 | // 8 | 9 | #import "MASConstraint.h" 10 | #import "MASUtilities.h" 11 | 12 | /** 13 | * A group of MASConstraint objects 14 | */ 15 | @interface MASCompositeConstraint : MASConstraint 16 | 17 | /** 18 | * Creates a composite with a predefined array of children 19 | * 20 | * @param children child MASConstraints 21 | * 22 | * @return a composite constraint 23 | */ 24 | - (id)initWithChildren:(NSArray *)children; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASCompositeConstraint.m: -------------------------------------------------------------------------------- 1 | // 2 | // MASCompositeConstraint.m 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 21/07/13. 6 | // Copyright (c) 2013 cloudling. All rights reserved. 7 | // 8 | 9 | #import "MASCompositeConstraint.h" 10 | #import "MASConstraint+Private.h" 11 | 12 | @interface MASCompositeConstraint () 13 | 14 | @property (nonatomic, strong) id mas_key; 15 | @property (nonatomic, strong) NSMutableArray *childConstraints; 16 | 17 | @end 18 | 19 | @implementation MASCompositeConstraint 20 | 21 | - (id)initWithChildren:(NSArray *)children { 22 | self = [super init]; 23 | if (!self) return nil; 24 | 25 | _childConstraints = [children mutableCopy]; 26 | for (MASConstraint *constraint in _childConstraints) { 27 | constraint.delegate = self; 28 | } 29 | 30 | return self; 31 | } 32 | 33 | #pragma mark - MASConstraintDelegate 34 | 35 | - (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint { 36 | NSUInteger index = [self.childConstraints indexOfObject:constraint]; 37 | NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint); 38 | [self.childConstraints replaceObjectAtIndex:index withObject:replacementConstraint]; 39 | } 40 | 41 | - (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { 42 | id strongDelegate = self.delegate; 43 | MASConstraint *newConstraint = [strongDelegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute]; 44 | newConstraint.delegate = self; 45 | [self.childConstraints addObject:newConstraint]; 46 | return newConstraint; 47 | } 48 | 49 | #pragma mark - NSLayoutConstraint multiplier proxies 50 | 51 | - (MASConstraint * (^)(CGFloat))multipliedBy { 52 | return ^id(CGFloat multiplier) { 53 | for (MASConstraint *constraint in self.childConstraints) { 54 | constraint.multipliedBy(multiplier); 55 | } 56 | return self; 57 | }; 58 | } 59 | 60 | - (MASConstraint * (^)(CGFloat))dividedBy { 61 | return ^id(CGFloat divider) { 62 | for (MASConstraint *constraint in self.childConstraints) { 63 | constraint.dividedBy(divider); 64 | } 65 | return self; 66 | }; 67 | } 68 | 69 | #pragma mark - MASLayoutPriority proxy 70 | 71 | - (MASConstraint * (^)(MASLayoutPriority))priority { 72 | return ^id(MASLayoutPriority priority) { 73 | for (MASConstraint *constraint in self.childConstraints) { 74 | constraint.priority(priority); 75 | } 76 | return self; 77 | }; 78 | } 79 | 80 | #pragma mark - NSLayoutRelation proxy 81 | 82 | - (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { 83 | return ^id(id attr, NSLayoutRelation relation) { 84 | for (MASConstraint *constraint in self.childConstraints.copy) { 85 | constraint.equalToWithRelation(attr, relation); 86 | } 87 | return self; 88 | }; 89 | } 90 | 91 | #pragma mark - attribute chaining 92 | 93 | - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { 94 | [self constraint:self addConstraintWithLayoutAttribute:layoutAttribute]; 95 | return self; 96 | } 97 | 98 | #pragma mark - Animator proxy 99 | 100 | #if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) 101 | 102 | - (MASConstraint *)animator { 103 | for (MASConstraint *constraint in self.childConstraints) { 104 | [constraint animator]; 105 | } 106 | return self; 107 | } 108 | 109 | #endif 110 | 111 | #pragma mark - debug helpers 112 | 113 | - (MASConstraint * (^)(id))key { 114 | return ^id(id key) { 115 | self.mas_key = key; 116 | int i = 0; 117 | for (MASConstraint *constraint in self.childConstraints) { 118 | constraint.key([NSString stringWithFormat:@"%@[%d]", key, i++]); 119 | } 120 | return self; 121 | }; 122 | } 123 | 124 | #pragma mark - NSLayoutConstraint constant setters 125 | 126 | - (void)setInsets:(MASEdgeInsets)insets { 127 | for (MASConstraint *constraint in self.childConstraints) { 128 | constraint.insets = insets; 129 | } 130 | } 131 | 132 | - (void)setInset:(CGFloat)inset { 133 | for (MASConstraint *constraint in self.childConstraints) { 134 | constraint.inset = inset; 135 | } 136 | } 137 | 138 | - (void)setOffset:(CGFloat)offset { 139 | for (MASConstraint *constraint in self.childConstraints) { 140 | constraint.offset = offset; 141 | } 142 | } 143 | 144 | - (void)setSizeOffset:(CGSize)sizeOffset { 145 | for (MASConstraint *constraint in self.childConstraints) { 146 | constraint.sizeOffset = sizeOffset; 147 | } 148 | } 149 | 150 | - (void)setCenterOffset:(CGPoint)centerOffset { 151 | for (MASConstraint *constraint in self.childConstraints) { 152 | constraint.centerOffset = centerOffset; 153 | } 154 | } 155 | 156 | #pragma mark - MASConstraint 157 | 158 | - (void)activate { 159 | for (MASConstraint *constraint in self.childConstraints) { 160 | [constraint activate]; 161 | } 162 | } 163 | 164 | - (void)deactivate { 165 | for (MASConstraint *constraint in self.childConstraints) { 166 | [constraint deactivate]; 167 | } 168 | } 169 | 170 | - (void)install { 171 | for (MASConstraint *constraint in self.childConstraints) { 172 | constraint.updateExisting = self.updateExisting; 173 | [constraint install]; 174 | } 175 | } 176 | 177 | - (void)uninstall { 178 | for (MASConstraint *constraint in self.childConstraints) { 179 | [constraint uninstall]; 180 | } 181 | } 182 | 183 | @end 184 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASConstraint+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // MASConstraint+Private.h 3 | // Masonry 4 | // 5 | // Created by Nick Tymchenko on 29/04/14. 6 | // Copyright (c) 2014 cloudling. All rights reserved. 7 | // 8 | 9 | #import "MASConstraint.h" 10 | 11 | @protocol MASConstraintDelegate; 12 | 13 | 14 | @interface MASConstraint () 15 | 16 | /** 17 | * Whether or not to check for an existing constraint instead of adding constraint 18 | */ 19 | @property (nonatomic, assign) BOOL updateExisting; 20 | 21 | /** 22 | * Usually MASConstraintMaker but could be a parent MASConstraint 23 | */ 24 | @property (nonatomic, weak) id delegate; 25 | 26 | /** 27 | * Based on a provided value type, is equal to calling: 28 | * NSNumber - setOffset: 29 | * NSValue with CGPoint - setPointOffset: 30 | * NSValue with CGSize - setSizeOffset: 31 | * NSValue with MASEdgeInsets - setInsets: 32 | */ 33 | - (void)setLayoutConstantWithValue:(NSValue *)value; 34 | 35 | @end 36 | 37 | 38 | @interface MASConstraint (Abstract) 39 | 40 | /** 41 | * Sets the constraint relation to given NSLayoutRelation 42 | * returns a block which accepts one of the following: 43 | * MASViewAttribute, UIView, NSValue, NSArray 44 | * see readme for more details. 45 | */ 46 | - (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation; 47 | 48 | /** 49 | * Override to set a custom chaining behaviour 50 | */ 51 | - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute; 52 | 53 | @end 54 | 55 | 56 | @protocol MASConstraintDelegate 57 | 58 | /** 59 | * Notifies the delegate when the constraint needs to be replaced with another constraint. For example 60 | * A MASViewConstraint may turn into a MASCompositeConstraint when an array is passed to one of the equality blocks 61 | */ 62 | - (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint; 63 | 64 | - (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute; 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASConstraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // MASConstraint.h 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 22/07/13. 6 | // Copyright (c) 2013 cloudling. All rights reserved. 7 | // 8 | 9 | #import "MASUtilities.h" 10 | 11 | /** 12 | * Enables Constraints to be created with chainable syntax 13 | * Constraint can represent single NSLayoutConstraint (MASViewConstraint) 14 | * or a group of NSLayoutConstraints (MASComposisteConstraint) 15 | */ 16 | @interface MASConstraint : NSObject 17 | 18 | // Chaining Support 19 | 20 | /** 21 | * Modifies the NSLayoutConstraint constant, 22 | * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following 23 | * NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight 24 | */ 25 | - (MASConstraint * (^)(MASEdgeInsets insets))insets; 26 | 27 | /** 28 | * Modifies the NSLayoutConstraint constant, 29 | * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following 30 | * NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight 31 | */ 32 | - (MASConstraint * (^)(CGFloat inset))inset; 33 | 34 | /** 35 | * Modifies the NSLayoutConstraint constant, 36 | * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following 37 | * NSLayoutAttributeWidth, NSLayoutAttributeHeight 38 | */ 39 | - (MASConstraint * (^)(CGSize offset))sizeOffset; 40 | 41 | /** 42 | * Modifies the NSLayoutConstraint constant, 43 | * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following 44 | * NSLayoutAttributeCenterX, NSLayoutAttributeCenterY 45 | */ 46 | - (MASConstraint * (^)(CGPoint offset))centerOffset; 47 | 48 | /** 49 | * Modifies the NSLayoutConstraint constant 50 | */ 51 | - (MASConstraint * (^)(CGFloat offset))offset; 52 | 53 | /** 54 | * Modifies the NSLayoutConstraint constant based on a value type 55 | */ 56 | - (MASConstraint * (^)(NSValue *value))valueOffset; 57 | 58 | /** 59 | * Sets the NSLayoutConstraint multiplier property 60 | */ 61 | - (MASConstraint * (^)(CGFloat multiplier))multipliedBy; 62 | 63 | /** 64 | * Sets the NSLayoutConstraint multiplier to 1.0/dividedBy 65 | */ 66 | - (MASConstraint * (^)(CGFloat divider))dividedBy; 67 | 68 | /** 69 | * Sets the NSLayoutConstraint priority to a float or MASLayoutPriority 70 | */ 71 | - (MASConstraint * (^)(MASLayoutPriority priority))priority; 72 | 73 | /** 74 | * Sets the NSLayoutConstraint priority to MASLayoutPriorityLow 75 | */ 76 | - (MASConstraint * (^)(void))priorityLow; 77 | 78 | /** 79 | * Sets the NSLayoutConstraint priority to MASLayoutPriorityMedium 80 | */ 81 | - (MASConstraint * (^)(void))priorityMedium; 82 | 83 | /** 84 | * Sets the NSLayoutConstraint priority to MASLayoutPriorityHigh 85 | */ 86 | - (MASConstraint * (^)(void))priorityHigh; 87 | 88 | /** 89 | * Sets the constraint relation to NSLayoutRelationEqual 90 | * returns a block which accepts one of the following: 91 | * MASViewAttribute, UIView, NSValue, NSArray 92 | * see readme for more details. 93 | */ 94 | - (MASConstraint * (^)(id attr))equalTo; 95 | 96 | /** 97 | * Sets the constraint relation to NSLayoutRelationGreaterThanOrEqual 98 | * returns a block which accepts one of the following: 99 | * MASViewAttribute, UIView, NSValue, NSArray 100 | * see readme for more details. 101 | */ 102 | - (MASConstraint * (^)(id attr))greaterThanOrEqualTo; 103 | 104 | /** 105 | * Sets the constraint relation to NSLayoutRelationLessThanOrEqual 106 | * returns a block which accepts one of the following: 107 | * MASViewAttribute, UIView, NSValue, NSArray 108 | * see readme for more details. 109 | */ 110 | - (MASConstraint * (^)(id attr))lessThanOrEqualTo; 111 | 112 | /** 113 | * Optional semantic property which has no effect but improves the readability of constraint 114 | */ 115 | - (MASConstraint *)with; 116 | 117 | /** 118 | * Optional semantic property which has no effect but improves the readability of constraint 119 | */ 120 | - (MASConstraint *)and; 121 | 122 | /** 123 | * Creates a new MASCompositeConstraint with the called attribute and reciever 124 | */ 125 | - (MASConstraint *)left; 126 | - (MASConstraint *)top; 127 | - (MASConstraint *)right; 128 | - (MASConstraint *)bottom; 129 | - (MASConstraint *)leading; 130 | - (MASConstraint *)trailing; 131 | - (MASConstraint *)width; 132 | - (MASConstraint *)height; 133 | - (MASConstraint *)centerX; 134 | - (MASConstraint *)centerY; 135 | - (MASConstraint *)baseline; 136 | 137 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) 138 | 139 | - (MASConstraint *)firstBaseline; 140 | - (MASConstraint *)lastBaseline; 141 | 142 | #endif 143 | 144 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) 145 | 146 | - (MASConstraint *)leftMargin; 147 | - (MASConstraint *)rightMargin; 148 | - (MASConstraint *)topMargin; 149 | - (MASConstraint *)bottomMargin; 150 | - (MASConstraint *)leadingMargin; 151 | - (MASConstraint *)trailingMargin; 152 | - (MASConstraint *)centerXWithinMargins; 153 | - (MASConstraint *)centerYWithinMargins; 154 | 155 | #endif 156 | 157 | 158 | /** 159 | * Sets the constraint debug name 160 | */ 161 | - (MASConstraint * (^)(id key))key; 162 | 163 | // NSLayoutConstraint constant Setters 164 | // for use outside of mas_updateConstraints/mas_makeConstraints blocks 165 | 166 | /** 167 | * Modifies the NSLayoutConstraint constant, 168 | * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following 169 | * NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight 170 | */ 171 | - (void)setInsets:(MASEdgeInsets)insets; 172 | 173 | /** 174 | * Modifies the NSLayoutConstraint constant, 175 | * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following 176 | * NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight 177 | */ 178 | - (void)setInset:(CGFloat)inset; 179 | 180 | /** 181 | * Modifies the NSLayoutConstraint constant, 182 | * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following 183 | * NSLayoutAttributeWidth, NSLayoutAttributeHeight 184 | */ 185 | - (void)setSizeOffset:(CGSize)sizeOffset; 186 | 187 | /** 188 | * Modifies the NSLayoutConstraint constant, 189 | * only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following 190 | * NSLayoutAttributeCenterX, NSLayoutAttributeCenterY 191 | */ 192 | - (void)setCenterOffset:(CGPoint)centerOffset; 193 | 194 | /** 195 | * Modifies the NSLayoutConstraint constant 196 | */ 197 | - (void)setOffset:(CGFloat)offset; 198 | 199 | 200 | // NSLayoutConstraint Installation support 201 | 202 | #if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) 203 | /** 204 | * Whether or not to go through the animator proxy when modifying the constraint 205 | */ 206 | @property (nonatomic, copy, readonly) MASConstraint *animator; 207 | #endif 208 | 209 | /** 210 | * Activates an NSLayoutConstraint if it's supported by an OS. 211 | * Invokes install otherwise. 212 | */ 213 | - (void)activate; 214 | 215 | /** 216 | * Deactivates previously installed/activated NSLayoutConstraint. 217 | */ 218 | - (void)deactivate; 219 | 220 | /** 221 | * Creates a NSLayoutConstraint and adds it to the appropriate view. 222 | */ 223 | - (void)install; 224 | 225 | /** 226 | * Removes previously installed NSLayoutConstraint 227 | */ 228 | - (void)uninstall; 229 | 230 | @end 231 | 232 | 233 | /** 234 | * Convenience auto-boxing macros for MASConstraint methods. 235 | * 236 | * Defining MAS_SHORTHAND_GLOBALS will turn on auto-boxing for default syntax. 237 | * A potential drawback of this is that the unprefixed macros will appear in global scope. 238 | */ 239 | #define mas_equalTo(...) equalTo(MASBoxValue((__VA_ARGS__))) 240 | #define mas_greaterThanOrEqualTo(...) greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__))) 241 | #define mas_lessThanOrEqualTo(...) lessThanOrEqualTo(MASBoxValue((__VA_ARGS__))) 242 | 243 | #define mas_offset(...) valueOffset(MASBoxValue((__VA_ARGS__))) 244 | 245 | 246 | #ifdef MAS_SHORTHAND_GLOBALS 247 | 248 | #define equalTo(...) mas_equalTo(__VA_ARGS__) 249 | #define greaterThanOrEqualTo(...) mas_greaterThanOrEqualTo(__VA_ARGS__) 250 | #define lessThanOrEqualTo(...) mas_lessThanOrEqualTo(__VA_ARGS__) 251 | 252 | #define offset(...) mas_offset(__VA_ARGS__) 253 | 254 | #endif 255 | 256 | 257 | @interface MASConstraint (AutoboxingSupport) 258 | 259 | /** 260 | * Aliases to corresponding relation methods (for shorthand macros) 261 | * Also needed to aid autocompletion 262 | */ 263 | - (MASConstraint * (^)(id attr))mas_equalTo; 264 | - (MASConstraint * (^)(id attr))mas_greaterThanOrEqualTo; 265 | - (MASConstraint * (^)(id attr))mas_lessThanOrEqualTo; 266 | 267 | /** 268 | * A dummy method to aid autocompletion 269 | */ 270 | - (MASConstraint * (^)(id offset))mas_offset; 271 | 272 | @end 273 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASConstraint.m: -------------------------------------------------------------------------------- 1 | // 2 | // MASConstraint.m 3 | // Masonry 4 | // 5 | // Created by Nick Tymchenko on 1/20/14. 6 | // 7 | 8 | #import "MASConstraint.h" 9 | #import "MASConstraint+Private.h" 10 | 11 | #define MASMethodNotImplemented() \ 12 | @throw [NSException exceptionWithName:NSInternalInconsistencyException \ 13 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \ 14 | userInfo:nil] 15 | 16 | @implementation MASConstraint 17 | 18 | #pragma mark - Init 19 | 20 | - (id)init { 21 | NSAssert(![self isMemberOfClass:[MASConstraint class]], @"MASConstraint is an abstract class, you should not instantiate it directly."); 22 | return [super init]; 23 | } 24 | 25 | #pragma mark - NSLayoutRelation proxies 26 | 27 | - (MASConstraint * (^)(id))equalTo { 28 | return ^id(id attribute) { 29 | return self.equalToWithRelation(attribute, NSLayoutRelationEqual); 30 | }; 31 | } 32 | 33 | - (MASConstraint * (^)(id))mas_equalTo { 34 | return ^id(id attribute) { 35 | return self.equalToWithRelation(attribute, NSLayoutRelationEqual); 36 | }; 37 | } 38 | 39 | - (MASConstraint * (^)(id))greaterThanOrEqualTo { 40 | return ^id(id attribute) { 41 | return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual); 42 | }; 43 | } 44 | 45 | - (MASConstraint * (^)(id))mas_greaterThanOrEqualTo { 46 | return ^id(id attribute) { 47 | return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual); 48 | }; 49 | } 50 | 51 | - (MASConstraint * (^)(id))lessThanOrEqualTo { 52 | return ^id(id attribute) { 53 | return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual); 54 | }; 55 | } 56 | 57 | - (MASConstraint * (^)(id))mas_lessThanOrEqualTo { 58 | return ^id(id attribute) { 59 | return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual); 60 | }; 61 | } 62 | 63 | #pragma mark - MASLayoutPriority proxies 64 | 65 | - (MASConstraint * (^)(void))priorityLow { 66 | return ^id{ 67 | self.priority(MASLayoutPriorityDefaultLow); 68 | return self; 69 | }; 70 | } 71 | 72 | - (MASConstraint * (^)(void))priorityMedium { 73 | return ^id{ 74 | self.priority(MASLayoutPriorityDefaultMedium); 75 | return self; 76 | }; 77 | } 78 | 79 | - (MASConstraint * (^)(void))priorityHigh { 80 | return ^id{ 81 | self.priority(MASLayoutPriorityDefaultHigh); 82 | return self; 83 | }; 84 | } 85 | 86 | #pragma mark - NSLayoutConstraint constant proxies 87 | 88 | - (MASConstraint * (^)(MASEdgeInsets))insets { 89 | return ^id(MASEdgeInsets insets){ 90 | self.insets = insets; 91 | return self; 92 | }; 93 | } 94 | 95 | - (MASConstraint * (^)(CGFloat))inset { 96 | return ^id(CGFloat inset){ 97 | self.inset = inset; 98 | return self; 99 | }; 100 | } 101 | 102 | - (MASConstraint * (^)(CGSize))sizeOffset { 103 | return ^id(CGSize offset) { 104 | self.sizeOffset = offset; 105 | return self; 106 | }; 107 | } 108 | 109 | - (MASConstraint * (^)(CGPoint))centerOffset { 110 | return ^id(CGPoint offset) { 111 | self.centerOffset = offset; 112 | return self; 113 | }; 114 | } 115 | 116 | - (MASConstraint * (^)(CGFloat))offset { 117 | return ^id(CGFloat offset){ 118 | self.offset = offset; 119 | return self; 120 | }; 121 | } 122 | 123 | - (MASConstraint * (^)(NSValue *value))valueOffset { 124 | return ^id(NSValue *offset) { 125 | NSAssert([offset isKindOfClass:NSValue.class], @"expected an NSValue offset, got: %@", offset); 126 | [self setLayoutConstantWithValue:offset]; 127 | return self; 128 | }; 129 | } 130 | 131 | - (MASConstraint * (^)(id offset))mas_offset { 132 | // Will never be called due to macro 133 | return nil; 134 | } 135 | 136 | #pragma mark - NSLayoutConstraint constant setter 137 | 138 | - (void)setLayoutConstantWithValue:(NSValue *)value { 139 | if ([value isKindOfClass:NSNumber.class]) { 140 | self.offset = [(NSNumber *)value doubleValue]; 141 | } else if (strcmp(value.objCType, @encode(CGPoint)) == 0) { 142 | CGPoint point; 143 | [value getValue:&point]; 144 | self.centerOffset = point; 145 | } else if (strcmp(value.objCType, @encode(CGSize)) == 0) { 146 | CGSize size; 147 | [value getValue:&size]; 148 | self.sizeOffset = size; 149 | } else if (strcmp(value.objCType, @encode(MASEdgeInsets)) == 0) { 150 | MASEdgeInsets insets; 151 | [value getValue:&insets]; 152 | self.insets = insets; 153 | } else { 154 | NSAssert(NO, @"attempting to set layout constant with unsupported value: %@", value); 155 | } 156 | } 157 | 158 | #pragma mark - Semantic properties 159 | 160 | - (MASConstraint *)with { 161 | return self; 162 | } 163 | 164 | - (MASConstraint *)and { 165 | return self; 166 | } 167 | 168 | #pragma mark - Chaining 169 | 170 | - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute __unused)layoutAttribute { 171 | MASMethodNotImplemented(); 172 | } 173 | 174 | - (MASConstraint *)left { 175 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft]; 176 | } 177 | 178 | - (MASConstraint *)top { 179 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop]; 180 | } 181 | 182 | - (MASConstraint *)right { 183 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight]; 184 | } 185 | 186 | - (MASConstraint *)bottom { 187 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottom]; 188 | } 189 | 190 | - (MASConstraint *)leading { 191 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeading]; 192 | } 193 | 194 | - (MASConstraint *)trailing { 195 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailing]; 196 | } 197 | 198 | - (MASConstraint *)width { 199 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeWidth]; 200 | } 201 | 202 | - (MASConstraint *)height { 203 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight]; 204 | } 205 | 206 | - (MASConstraint *)centerX { 207 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterX]; 208 | } 209 | 210 | - (MASConstraint *)centerY { 211 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterY]; 212 | } 213 | 214 | - (MASConstraint *)baseline { 215 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBaseline]; 216 | } 217 | 218 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) 219 | 220 | - (MASConstraint *)firstBaseline { 221 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeFirstBaseline]; 222 | } 223 | - (MASConstraint *)lastBaseline { 224 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLastBaseline]; 225 | } 226 | 227 | #endif 228 | 229 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) 230 | 231 | - (MASConstraint *)leftMargin { 232 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeftMargin]; 233 | } 234 | 235 | - (MASConstraint *)rightMargin { 236 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRightMargin]; 237 | } 238 | 239 | - (MASConstraint *)topMargin { 240 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTopMargin]; 241 | } 242 | 243 | - (MASConstraint *)bottomMargin { 244 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottomMargin]; 245 | } 246 | 247 | - (MASConstraint *)leadingMargin { 248 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeadingMargin]; 249 | } 250 | 251 | - (MASConstraint *)trailingMargin { 252 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailingMargin]; 253 | } 254 | 255 | - (MASConstraint *)centerXWithinMargins { 256 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterXWithinMargins]; 257 | } 258 | 259 | - (MASConstraint *)centerYWithinMargins { 260 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterYWithinMargins]; 261 | } 262 | 263 | #endif 264 | 265 | #pragma mark - Abstract 266 | 267 | - (MASConstraint * (^)(CGFloat multiplier))multipliedBy { MASMethodNotImplemented(); } 268 | 269 | - (MASConstraint * (^)(CGFloat divider))dividedBy { MASMethodNotImplemented(); } 270 | 271 | - (MASConstraint * (^)(MASLayoutPriority priority))priority { MASMethodNotImplemented(); } 272 | 273 | - (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { MASMethodNotImplemented(); } 274 | 275 | - (MASConstraint * (^)(id key))key { MASMethodNotImplemented(); } 276 | 277 | - (void)setInsets:(MASEdgeInsets __unused)insets { MASMethodNotImplemented(); } 278 | 279 | - (void)setInset:(CGFloat __unused)inset { MASMethodNotImplemented(); } 280 | 281 | - (void)setSizeOffset:(CGSize __unused)sizeOffset { MASMethodNotImplemented(); } 282 | 283 | - (void)setCenterOffset:(CGPoint __unused)centerOffset { MASMethodNotImplemented(); } 284 | 285 | - (void)setOffset:(CGFloat __unused)offset { MASMethodNotImplemented(); } 286 | 287 | #if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) 288 | 289 | - (MASConstraint *)animator { MASMethodNotImplemented(); } 290 | 291 | #endif 292 | 293 | - (void)activate { MASMethodNotImplemented(); } 294 | 295 | - (void)deactivate { MASMethodNotImplemented(); } 296 | 297 | - (void)install { MASMethodNotImplemented(); } 298 | 299 | - (void)uninstall { MASMethodNotImplemented(); } 300 | 301 | @end 302 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASConstraintMaker.h: -------------------------------------------------------------------------------- 1 | // 2 | // MASConstraintMaker.h 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 20/07/13. 6 | // Copyright (c) 2013 cloudling. All rights reserved. 7 | // 8 | 9 | #import "MASConstraint.h" 10 | #import "MASUtilities.h" 11 | 12 | typedef NS_OPTIONS(NSInteger, MASAttribute) { 13 | MASAttributeLeft = 1 << NSLayoutAttributeLeft, 14 | MASAttributeRight = 1 << NSLayoutAttributeRight, 15 | MASAttributeTop = 1 << NSLayoutAttributeTop, 16 | MASAttributeBottom = 1 << NSLayoutAttributeBottom, 17 | MASAttributeLeading = 1 << NSLayoutAttributeLeading, 18 | MASAttributeTrailing = 1 << NSLayoutAttributeTrailing, 19 | MASAttributeWidth = 1 << NSLayoutAttributeWidth, 20 | MASAttributeHeight = 1 << NSLayoutAttributeHeight, 21 | MASAttributeCenterX = 1 << NSLayoutAttributeCenterX, 22 | MASAttributeCenterY = 1 << NSLayoutAttributeCenterY, 23 | MASAttributeBaseline = 1 << NSLayoutAttributeBaseline, 24 | 25 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) 26 | 27 | MASAttributeFirstBaseline = 1 << NSLayoutAttributeFirstBaseline, 28 | MASAttributeLastBaseline = 1 << NSLayoutAttributeLastBaseline, 29 | 30 | #endif 31 | 32 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) 33 | 34 | MASAttributeLeftMargin = 1 << NSLayoutAttributeLeftMargin, 35 | MASAttributeRightMargin = 1 << NSLayoutAttributeRightMargin, 36 | MASAttributeTopMargin = 1 << NSLayoutAttributeTopMargin, 37 | MASAttributeBottomMargin = 1 << NSLayoutAttributeBottomMargin, 38 | MASAttributeLeadingMargin = 1 << NSLayoutAttributeLeadingMargin, 39 | MASAttributeTrailingMargin = 1 << NSLayoutAttributeTrailingMargin, 40 | MASAttributeCenterXWithinMargins = 1 << NSLayoutAttributeCenterXWithinMargins, 41 | MASAttributeCenterYWithinMargins = 1 << NSLayoutAttributeCenterYWithinMargins, 42 | 43 | #endif 44 | 45 | }; 46 | 47 | /** 48 | * Provides factory methods for creating MASConstraints. 49 | * Constraints are collected until they are ready to be installed 50 | * 51 | */ 52 | @interface MASConstraintMaker : NSObject 53 | 54 | /** 55 | * The following properties return a new MASViewConstraint 56 | * with the first item set to the makers associated view and the appropriate MASViewAttribute 57 | */ 58 | @property (nonatomic, strong, readonly) MASConstraint *left; 59 | @property (nonatomic, strong, readonly) MASConstraint *top; 60 | @property (nonatomic, strong, readonly) MASConstraint *right; 61 | @property (nonatomic, strong, readonly) MASConstraint *bottom; 62 | @property (nonatomic, strong, readonly) MASConstraint *leading; 63 | @property (nonatomic, strong, readonly) MASConstraint *trailing; 64 | @property (nonatomic, strong, readonly) MASConstraint *width; 65 | @property (nonatomic, strong, readonly) MASConstraint *height; 66 | @property (nonatomic, strong, readonly) MASConstraint *centerX; 67 | @property (nonatomic, strong, readonly) MASConstraint *centerY; 68 | @property (nonatomic, strong, readonly) MASConstraint *baseline; 69 | 70 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) 71 | 72 | @property (nonatomic, strong, readonly) MASConstraint *firstBaseline; 73 | @property (nonatomic, strong, readonly) MASConstraint *lastBaseline; 74 | 75 | #endif 76 | 77 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) 78 | 79 | @property (nonatomic, strong, readonly) MASConstraint *leftMargin; 80 | @property (nonatomic, strong, readonly) MASConstraint *rightMargin; 81 | @property (nonatomic, strong, readonly) MASConstraint *topMargin; 82 | @property (nonatomic, strong, readonly) MASConstraint *bottomMargin; 83 | @property (nonatomic, strong, readonly) MASConstraint *leadingMargin; 84 | @property (nonatomic, strong, readonly) MASConstraint *trailingMargin; 85 | @property (nonatomic, strong, readonly) MASConstraint *centerXWithinMargins; 86 | @property (nonatomic, strong, readonly) MASConstraint *centerYWithinMargins; 87 | 88 | #endif 89 | 90 | /** 91 | * Returns a block which creates a new MASCompositeConstraint with the first item set 92 | * to the makers associated view and children corresponding to the set bits in the 93 | * MASAttribute parameter. Combine multiple attributes via binary-or. 94 | */ 95 | @property (nonatomic, strong, readonly) MASConstraint *(^attributes)(MASAttribute attrs); 96 | 97 | /** 98 | * Creates a MASCompositeConstraint with type MASCompositeConstraintTypeEdges 99 | * which generates the appropriate MASViewConstraint children (top, left, bottom, right) 100 | * with the first item set to the makers associated view 101 | */ 102 | @property (nonatomic, strong, readonly) MASConstraint *edges; 103 | 104 | /** 105 | * Creates a MASCompositeConstraint with type MASCompositeConstraintTypeSize 106 | * which generates the appropriate MASViewConstraint children (width, height) 107 | * with the first item set to the makers associated view 108 | */ 109 | @property (nonatomic, strong, readonly) MASConstraint *size; 110 | 111 | /** 112 | * Creates a MASCompositeConstraint with type MASCompositeConstraintTypeCenter 113 | * which generates the appropriate MASViewConstraint children (centerX, centerY) 114 | * with the first item set to the makers associated view 115 | */ 116 | @property (nonatomic, strong, readonly) MASConstraint *center; 117 | 118 | /** 119 | * Whether or not to check for an existing constraint instead of adding constraint 120 | */ 121 | @property (nonatomic, assign) BOOL updateExisting; 122 | 123 | /** 124 | * Whether or not to remove existing constraints prior to installing 125 | */ 126 | @property (nonatomic, assign) BOOL removeExisting; 127 | 128 | /** 129 | * initialises the maker with a default view 130 | * 131 | * @param view any MASConstraint are created with this view as the first item 132 | * 133 | * @return a new MASConstraintMaker 134 | */ 135 | - (id)initWithView:(MAS_VIEW *)view; 136 | 137 | /** 138 | * Calls install method on any MASConstraints which have been created by this maker 139 | * 140 | * @return an array of all the installed MASConstraints 141 | */ 142 | - (NSArray *)install; 143 | 144 | - (MASConstraint * (^)(dispatch_block_t))group; 145 | 146 | @end 147 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASConstraintMaker.m: -------------------------------------------------------------------------------- 1 | // 2 | // MASConstraintMaker.m 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 20/07/13. 6 | // Copyright (c) 2013 cloudling. All rights reserved. 7 | // 8 | 9 | #import "MASConstraintMaker.h" 10 | #import "MASViewConstraint.h" 11 | #import "MASCompositeConstraint.h" 12 | #import "MASConstraint+Private.h" 13 | #import "MASViewAttribute.h" 14 | #import "View+MASAdditions.h" 15 | 16 | @interface MASConstraintMaker () 17 | 18 | @property (nonatomic, weak) MAS_VIEW *view; 19 | @property (nonatomic, strong) NSMutableArray *constraints; 20 | 21 | @end 22 | 23 | @implementation MASConstraintMaker 24 | 25 | - (id)initWithView:(MAS_VIEW *)view { 26 | self = [super init]; 27 | if (!self) return nil; 28 | 29 | self.view = view; 30 | self.constraints = NSMutableArray.new; 31 | 32 | return self; 33 | } 34 | 35 | - (NSArray *)install { 36 | if (self.removeExisting) { 37 | NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view]; 38 | for (MASConstraint *constraint in installedConstraints) { 39 | [constraint uninstall]; 40 | } 41 | } 42 | NSArray *constraints = self.constraints.copy; 43 | for (MASConstraint *constraint in constraints) { 44 | constraint.updateExisting = self.updateExisting; 45 | [constraint install]; 46 | } 47 | [self.constraints removeAllObjects]; 48 | return constraints; 49 | } 50 | 51 | #pragma mark - MASConstraintDelegate 52 | 53 | - (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint { 54 | NSUInteger index = [self.constraints indexOfObject:constraint]; 55 | NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint); 56 | [self.constraints replaceObjectAtIndex:index withObject:replacementConstraint]; 57 | } 58 | 59 | - (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { 60 | MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute]; 61 | MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute]; 62 | if ([constraint isKindOfClass:MASViewConstraint.class]) { 63 | //replace with composite constraint 64 | NSArray *children = @[constraint, newConstraint]; 65 | MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children]; 66 | compositeConstraint.delegate = self; 67 | [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint]; 68 | return compositeConstraint; 69 | } 70 | if (!constraint) { 71 | newConstraint.delegate = self; 72 | [self.constraints addObject:newConstraint]; 73 | } 74 | return newConstraint; 75 | } 76 | 77 | - (MASConstraint *)addConstraintWithAttributes:(MASAttribute)attrs { 78 | __unused MASAttribute anyAttribute = (MASAttributeLeft | MASAttributeRight | MASAttributeTop | MASAttributeBottom | MASAttributeLeading 79 | | MASAttributeTrailing | MASAttributeWidth | MASAttributeHeight | MASAttributeCenterX 80 | | MASAttributeCenterY | MASAttributeBaseline 81 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) 82 | | MASAttributeFirstBaseline | MASAttributeLastBaseline 83 | #endif 84 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) 85 | | MASAttributeLeftMargin | MASAttributeRightMargin | MASAttributeTopMargin | MASAttributeBottomMargin 86 | | MASAttributeLeadingMargin | MASAttributeTrailingMargin | MASAttributeCenterXWithinMargins 87 | | MASAttributeCenterYWithinMargins 88 | #endif 89 | ); 90 | 91 | NSAssert((attrs & anyAttribute) != 0, @"You didn't pass any attribute to make.attributes(...)"); 92 | 93 | NSMutableArray *attributes = [NSMutableArray array]; 94 | 95 | if (attrs & MASAttributeLeft) [attributes addObject:self.view.mas_left]; 96 | if (attrs & MASAttributeRight) [attributes addObject:self.view.mas_right]; 97 | if (attrs & MASAttributeTop) [attributes addObject:self.view.mas_top]; 98 | if (attrs & MASAttributeBottom) [attributes addObject:self.view.mas_bottom]; 99 | if (attrs & MASAttributeLeading) [attributes addObject:self.view.mas_leading]; 100 | if (attrs & MASAttributeTrailing) [attributes addObject:self.view.mas_trailing]; 101 | if (attrs & MASAttributeWidth) [attributes addObject:self.view.mas_width]; 102 | if (attrs & MASAttributeHeight) [attributes addObject:self.view.mas_height]; 103 | if (attrs & MASAttributeCenterX) [attributes addObject:self.view.mas_centerX]; 104 | if (attrs & MASAttributeCenterY) [attributes addObject:self.view.mas_centerY]; 105 | if (attrs & MASAttributeBaseline) [attributes addObject:self.view.mas_baseline]; 106 | 107 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) 108 | 109 | if (attrs & MASAttributeFirstBaseline) [attributes addObject:self.view.mas_firstBaseline]; 110 | if (attrs & MASAttributeLastBaseline) [attributes addObject:self.view.mas_lastBaseline]; 111 | 112 | #endif 113 | 114 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) 115 | 116 | if (attrs & MASAttributeLeftMargin) [attributes addObject:self.view.mas_leftMargin]; 117 | if (attrs & MASAttributeRightMargin) [attributes addObject:self.view.mas_rightMargin]; 118 | if (attrs & MASAttributeTopMargin) [attributes addObject:self.view.mas_topMargin]; 119 | if (attrs & MASAttributeBottomMargin) [attributes addObject:self.view.mas_bottomMargin]; 120 | if (attrs & MASAttributeLeadingMargin) [attributes addObject:self.view.mas_leadingMargin]; 121 | if (attrs & MASAttributeTrailingMargin) [attributes addObject:self.view.mas_trailingMargin]; 122 | if (attrs & MASAttributeCenterXWithinMargins) [attributes addObject:self.view.mas_centerXWithinMargins]; 123 | if (attrs & MASAttributeCenterYWithinMargins) [attributes addObject:self.view.mas_centerYWithinMargins]; 124 | 125 | #endif 126 | 127 | NSMutableArray *children = [NSMutableArray arrayWithCapacity:attributes.count]; 128 | 129 | for (MASViewAttribute *a in attributes) { 130 | [children addObject:[[MASViewConstraint alloc] initWithFirstViewAttribute:a]]; 131 | } 132 | 133 | MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children]; 134 | constraint.delegate = self; 135 | [self.constraints addObject:constraint]; 136 | return constraint; 137 | } 138 | 139 | #pragma mark - standard Attributes 140 | 141 | - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { 142 | return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute]; 143 | } 144 | 145 | - (MASConstraint *)left { 146 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft]; 147 | } 148 | 149 | - (MASConstraint *)top { 150 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop]; 151 | } 152 | 153 | - (MASConstraint *)right { 154 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight]; 155 | } 156 | 157 | - (MASConstraint *)bottom { 158 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottom]; 159 | } 160 | 161 | - (MASConstraint *)leading { 162 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeading]; 163 | } 164 | 165 | - (MASConstraint *)trailing { 166 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailing]; 167 | } 168 | 169 | - (MASConstraint *)width { 170 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeWidth]; 171 | } 172 | 173 | - (MASConstraint *)height { 174 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight]; 175 | } 176 | 177 | - (MASConstraint *)centerX { 178 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterX]; 179 | } 180 | 181 | - (MASConstraint *)centerY { 182 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterY]; 183 | } 184 | 185 | - (MASConstraint *)baseline { 186 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBaseline]; 187 | } 188 | 189 | - (MASConstraint *(^)(MASAttribute))attributes { 190 | return ^(MASAttribute attrs){ 191 | return [self addConstraintWithAttributes:attrs]; 192 | }; 193 | } 194 | 195 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) 196 | 197 | - (MASConstraint *)firstBaseline { 198 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeFirstBaseline]; 199 | } 200 | 201 | - (MASConstraint *)lastBaseline { 202 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLastBaseline]; 203 | } 204 | 205 | #endif 206 | 207 | 208 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) 209 | 210 | - (MASConstraint *)leftMargin { 211 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeftMargin]; 212 | } 213 | 214 | - (MASConstraint *)rightMargin { 215 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRightMargin]; 216 | } 217 | 218 | - (MASConstraint *)topMargin { 219 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTopMargin]; 220 | } 221 | 222 | - (MASConstraint *)bottomMargin { 223 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottomMargin]; 224 | } 225 | 226 | - (MASConstraint *)leadingMargin { 227 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeadingMargin]; 228 | } 229 | 230 | - (MASConstraint *)trailingMargin { 231 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailingMargin]; 232 | } 233 | 234 | - (MASConstraint *)centerXWithinMargins { 235 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterXWithinMargins]; 236 | } 237 | 238 | - (MASConstraint *)centerYWithinMargins { 239 | return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterYWithinMargins]; 240 | } 241 | 242 | #endif 243 | 244 | 245 | #pragma mark - composite Attributes 246 | 247 | - (MASConstraint *)edges { 248 | return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom]; 249 | } 250 | 251 | - (MASConstraint *)size { 252 | return [self addConstraintWithAttributes:MASAttributeWidth | MASAttributeHeight]; 253 | } 254 | 255 | - (MASConstraint *)center { 256 | return [self addConstraintWithAttributes:MASAttributeCenterX | MASAttributeCenterY]; 257 | } 258 | 259 | #pragma mark - grouping 260 | 261 | - (MASConstraint *(^)(dispatch_block_t group))group { 262 | return ^id(dispatch_block_t group) { 263 | NSInteger previousCount = self.constraints.count; 264 | group(); 265 | 266 | NSArray *children = [self.constraints subarrayWithRange:NSMakeRange(previousCount, self.constraints.count - previousCount)]; 267 | MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children]; 268 | constraint.delegate = self; 269 | return constraint; 270 | }; 271 | } 272 | 273 | @end 274 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASLayoutConstraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // MASLayoutConstraint.h 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 3/08/13. 6 | // Copyright (c) 2013 Jonas Budelmann. All rights reserved. 7 | // 8 | 9 | #import "MASUtilities.h" 10 | 11 | /** 12 | * When you are debugging or printing the constraints attached to a view this subclass 13 | * makes it easier to identify which constraints have been created via Masonry 14 | */ 15 | @interface MASLayoutConstraint : NSLayoutConstraint 16 | 17 | /** 18 | * a key to associate with this constraint 19 | */ 20 | @property (nonatomic, strong) id mas_key; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASLayoutConstraint.m: -------------------------------------------------------------------------------- 1 | // 2 | // MASLayoutConstraint.m 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 3/08/13. 6 | // Copyright (c) 2013 Jonas Budelmann. All rights reserved. 7 | // 8 | 9 | #import "MASLayoutConstraint.h" 10 | 11 | @implementation MASLayoutConstraint 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASUtilities.h: -------------------------------------------------------------------------------- 1 | // 2 | // MASUtilities.h 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 19/08/13. 6 | // Copyright (c) 2013 Jonas Budelmann. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | 13 | #if TARGET_OS_IPHONE || TARGET_OS_TV 14 | 15 | #import 16 | #define MAS_VIEW UIView 17 | #define MAS_VIEW_CONTROLLER UIViewController 18 | #define MASEdgeInsets UIEdgeInsets 19 | 20 | typedef UILayoutPriority MASLayoutPriority; 21 | static const MASLayoutPriority MASLayoutPriorityRequired = UILayoutPriorityRequired; 22 | static const MASLayoutPriority MASLayoutPriorityDefaultHigh = UILayoutPriorityDefaultHigh; 23 | static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 500; 24 | static const MASLayoutPriority MASLayoutPriorityDefaultLow = UILayoutPriorityDefaultLow; 25 | static const MASLayoutPriority MASLayoutPriorityFittingSizeLevel = UILayoutPriorityFittingSizeLevel; 26 | 27 | #elif TARGET_OS_MAC 28 | 29 | #import 30 | #define MAS_VIEW NSView 31 | #define MASEdgeInsets NSEdgeInsets 32 | 33 | typedef NSLayoutPriority MASLayoutPriority; 34 | static const MASLayoutPriority MASLayoutPriorityRequired = NSLayoutPriorityRequired; 35 | static const MASLayoutPriority MASLayoutPriorityDefaultHigh = NSLayoutPriorityDefaultHigh; 36 | static const MASLayoutPriority MASLayoutPriorityDragThatCanResizeWindow = NSLayoutPriorityDragThatCanResizeWindow; 37 | static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 501; 38 | static const MASLayoutPriority MASLayoutPriorityWindowSizeStayPut = NSLayoutPriorityWindowSizeStayPut; 39 | static const MASLayoutPriority MASLayoutPriorityDragThatCannotResizeWindow = NSLayoutPriorityDragThatCannotResizeWindow; 40 | static const MASLayoutPriority MASLayoutPriorityDefaultLow = NSLayoutPriorityDefaultLow; 41 | static const MASLayoutPriority MASLayoutPriorityFittingSizeCompression = NSLayoutPriorityFittingSizeCompression; 42 | 43 | #endif 44 | 45 | /** 46 | * Allows you to attach keys to objects matching the variable names passed. 47 | * 48 | * view1.mas_key = @"view1", view2.mas_key = @"view2"; 49 | * 50 | * is equivalent to: 51 | * 52 | * MASAttachKeys(view1, view2); 53 | */ 54 | #define MASAttachKeys(...) \ 55 | { \ 56 | NSDictionary *keyPairs = NSDictionaryOfVariableBindings(__VA_ARGS__); \ 57 | for (id key in keyPairs.allKeys) { \ 58 | id obj = keyPairs[key]; \ 59 | NSAssert([obj respondsToSelector:@selector(setMas_key:)], \ 60 | @"Cannot attach mas_key to %@", obj); \ 61 | [obj setMas_key:key]; \ 62 | } \ 63 | } 64 | 65 | /** 66 | * Used to create object hashes 67 | * Based on http://www.mikeash.com/pyblog/friday-qa-2010-06-18-implementing-equality-and-hashing.html 68 | */ 69 | #define MAS_NSUINT_BIT (CHAR_BIT * sizeof(NSUInteger)) 70 | #define MAS_NSUINTROTATE(val, howmuch) ((((NSUInteger)val) << howmuch) | (((NSUInteger)val) >> (MAS_NSUINT_BIT - howmuch))) 71 | 72 | /** 73 | * Given a scalar or struct value, wraps it in NSValue 74 | * Based on EXPObjectify: https://github.com/specta/expecta 75 | */ 76 | static inline id _MASBoxValue(const char *type, ...) { 77 | va_list v; 78 | va_start(v, type); 79 | id obj = nil; 80 | if (strcmp(type, @encode(id)) == 0) { 81 | id actual = va_arg(v, id); 82 | obj = actual; 83 | } else if (strcmp(type, @encode(CGPoint)) == 0) { 84 | CGPoint actual = (CGPoint)va_arg(v, CGPoint); 85 | obj = [NSValue value:&actual withObjCType:type]; 86 | } else if (strcmp(type, @encode(CGSize)) == 0) { 87 | CGSize actual = (CGSize)va_arg(v, CGSize); 88 | obj = [NSValue value:&actual withObjCType:type]; 89 | } else if (strcmp(type, @encode(MASEdgeInsets)) == 0) { 90 | MASEdgeInsets actual = (MASEdgeInsets)va_arg(v, MASEdgeInsets); 91 | obj = [NSValue value:&actual withObjCType:type]; 92 | } else if (strcmp(type, @encode(double)) == 0) { 93 | double actual = (double)va_arg(v, double); 94 | obj = [NSNumber numberWithDouble:actual]; 95 | } else if (strcmp(type, @encode(float)) == 0) { 96 | float actual = (float)va_arg(v, double); 97 | obj = [NSNumber numberWithFloat:actual]; 98 | } else if (strcmp(type, @encode(int)) == 0) { 99 | int actual = (int)va_arg(v, int); 100 | obj = [NSNumber numberWithInt:actual]; 101 | } else if (strcmp(type, @encode(long)) == 0) { 102 | long actual = (long)va_arg(v, long); 103 | obj = [NSNumber numberWithLong:actual]; 104 | } else if (strcmp(type, @encode(long long)) == 0) { 105 | long long actual = (long long)va_arg(v, long long); 106 | obj = [NSNumber numberWithLongLong:actual]; 107 | } else if (strcmp(type, @encode(short)) == 0) { 108 | short actual = (short)va_arg(v, int); 109 | obj = [NSNumber numberWithShort:actual]; 110 | } else if (strcmp(type, @encode(char)) == 0) { 111 | char actual = (char)va_arg(v, int); 112 | obj = [NSNumber numberWithChar:actual]; 113 | } else if (strcmp(type, @encode(bool)) == 0) { 114 | bool actual = (bool)va_arg(v, int); 115 | obj = [NSNumber numberWithBool:actual]; 116 | } else if (strcmp(type, @encode(unsigned char)) == 0) { 117 | unsigned char actual = (unsigned char)va_arg(v, unsigned int); 118 | obj = [NSNumber numberWithUnsignedChar:actual]; 119 | } else if (strcmp(type, @encode(unsigned int)) == 0) { 120 | unsigned int actual = (unsigned int)va_arg(v, unsigned int); 121 | obj = [NSNumber numberWithUnsignedInt:actual]; 122 | } else if (strcmp(type, @encode(unsigned long)) == 0) { 123 | unsigned long actual = (unsigned long)va_arg(v, unsigned long); 124 | obj = [NSNumber numberWithUnsignedLong:actual]; 125 | } else if (strcmp(type, @encode(unsigned long long)) == 0) { 126 | unsigned long long actual = (unsigned long long)va_arg(v, unsigned long long); 127 | obj = [NSNumber numberWithUnsignedLongLong:actual]; 128 | } else if (strcmp(type, @encode(unsigned short)) == 0) { 129 | unsigned short actual = (unsigned short)va_arg(v, unsigned int); 130 | obj = [NSNumber numberWithUnsignedShort:actual]; 131 | } 132 | va_end(v); 133 | return obj; 134 | } 135 | 136 | #define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value)) 137 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASViewAttribute.h: -------------------------------------------------------------------------------- 1 | // 2 | // MASViewAttribute.h 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 21/07/13. 6 | // Copyright (c) 2013 cloudling. All rights reserved. 7 | // 8 | 9 | #import "MASUtilities.h" 10 | 11 | /** 12 | * An immutable tuple which stores the view and the related NSLayoutAttribute. 13 | * Describes part of either the left or right hand side of a constraint equation 14 | */ 15 | @interface MASViewAttribute : NSObject 16 | 17 | /** 18 | * The view which the reciever relates to. Can be nil if item is not a view. 19 | */ 20 | @property (nonatomic, weak, readonly) MAS_VIEW *view; 21 | 22 | /** 23 | * The item which the reciever relates to. 24 | */ 25 | @property (nonatomic, weak, readonly) id item; 26 | 27 | /** 28 | * The attribute which the reciever relates to 29 | */ 30 | @property (nonatomic, assign, readonly) NSLayoutAttribute layoutAttribute; 31 | 32 | /** 33 | * Convenience initializer. 34 | */ 35 | - (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute; 36 | 37 | /** 38 | * The designated initializer. 39 | */ 40 | - (id)initWithView:(MAS_VIEW *)view item:(id)item layoutAttribute:(NSLayoutAttribute)layoutAttribute; 41 | 42 | /** 43 | * Determine whether the layoutAttribute is a size attribute 44 | * 45 | * @return YES if layoutAttribute is equal to NSLayoutAttributeWidth or NSLayoutAttributeHeight 46 | */ 47 | - (BOOL)isSizeAttribute; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASViewAttribute.m: -------------------------------------------------------------------------------- 1 | // 2 | // MASViewAttribute.m 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 21/07/13. 6 | // Copyright (c) 2013 cloudling. All rights reserved. 7 | // 8 | 9 | #import "MASViewAttribute.h" 10 | 11 | @implementation MASViewAttribute 12 | 13 | - (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute { 14 | self = [self initWithView:view item:view layoutAttribute:layoutAttribute]; 15 | return self; 16 | } 17 | 18 | - (id)initWithView:(MAS_VIEW *)view item:(id)item layoutAttribute:(NSLayoutAttribute)layoutAttribute { 19 | self = [super init]; 20 | if (!self) return nil; 21 | 22 | _view = view; 23 | _item = item; 24 | _layoutAttribute = layoutAttribute; 25 | 26 | return self; 27 | } 28 | 29 | - (BOOL)isSizeAttribute { 30 | return self.layoutAttribute == NSLayoutAttributeWidth 31 | || self.layoutAttribute == NSLayoutAttributeHeight; 32 | } 33 | 34 | - (BOOL)isEqual:(MASViewAttribute *)viewAttribute { 35 | if ([viewAttribute isKindOfClass:self.class]) { 36 | return self.view == viewAttribute.view 37 | && self.layoutAttribute == viewAttribute.layoutAttribute; 38 | } 39 | return [super isEqual:viewAttribute]; 40 | } 41 | 42 | - (NSUInteger)hash { 43 | return MAS_NSUINTROTATE([self.view hash], MAS_NSUINT_BIT / 2) ^ self.layoutAttribute; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASViewConstraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // MASViewConstraint.h 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 20/07/13. 6 | // Copyright (c) 2013 cloudling. All rights reserved. 7 | // 8 | 9 | #import "MASViewAttribute.h" 10 | #import "MASConstraint.h" 11 | #import "MASLayoutConstraint.h" 12 | #import "MASUtilities.h" 13 | 14 | /** 15 | * A single constraint. 16 | * Contains the attributes neccessary for creating a NSLayoutConstraint and adding it to the appropriate view 17 | */ 18 | @interface MASViewConstraint : MASConstraint 19 | 20 | /** 21 | * First item/view and first attribute of the NSLayoutConstraint 22 | */ 23 | @property (nonatomic, strong, readonly) MASViewAttribute *firstViewAttribute; 24 | 25 | /** 26 | * Second item/view and second attribute of the NSLayoutConstraint 27 | */ 28 | @property (nonatomic, strong, readonly) MASViewAttribute *secondViewAttribute; 29 | 30 | /** 31 | * initialises the MASViewConstraint with the first part of the equation 32 | * 33 | * @param firstViewAttribute view.mas_left, view.mas_width etc. 34 | * 35 | * @return a new view constraint 36 | */ 37 | - (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute; 38 | 39 | /** 40 | * Returns all MASViewConstraints installed with this view as a first item. 41 | * 42 | * @param view A view to retrieve constraints for. 43 | * 44 | * @return An array of MASViewConstraints. 45 | */ 46 | + (NSArray *)installedConstraintsForView:(MAS_VIEW *)view; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/MASViewConstraint.m: -------------------------------------------------------------------------------- 1 | // 2 | // MASViewConstraint.m 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 20/07/13. 6 | // Copyright (c) 2013 cloudling. All rights reserved. 7 | // 8 | 9 | #import "MASViewConstraint.h" 10 | #import "MASConstraint+Private.h" 11 | #import "MASCompositeConstraint.h" 12 | #import "MASLayoutConstraint.h" 13 | #import "View+MASAdditions.h" 14 | #import 15 | 16 | @interface MAS_VIEW (MASConstraints) 17 | 18 | @property (nonatomic, readonly) NSMutableSet *mas_installedConstraints; 19 | 20 | @end 21 | 22 | @implementation MAS_VIEW (MASConstraints) 23 | 24 | static char kInstalledConstraintsKey; 25 | 26 | - (NSMutableSet *)mas_installedConstraints { 27 | NSMutableSet *constraints = objc_getAssociatedObject(self, &kInstalledConstraintsKey); 28 | if (!constraints) { 29 | constraints = [NSMutableSet set]; 30 | objc_setAssociatedObject(self, &kInstalledConstraintsKey, constraints, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 31 | } 32 | return constraints; 33 | } 34 | 35 | @end 36 | 37 | 38 | @interface MASViewConstraint () 39 | 40 | @property (nonatomic, strong, readwrite) MASViewAttribute *secondViewAttribute; 41 | @property (nonatomic, weak) MAS_VIEW *installedView; 42 | @property (nonatomic, weak) MASLayoutConstraint *layoutConstraint; 43 | @property (nonatomic, assign) NSLayoutRelation layoutRelation; 44 | @property (nonatomic, assign) MASLayoutPriority layoutPriority; 45 | @property (nonatomic, assign) CGFloat layoutMultiplier; 46 | @property (nonatomic, assign) CGFloat layoutConstant; 47 | @property (nonatomic, assign) BOOL hasLayoutRelation; 48 | @property (nonatomic, strong) id mas_key; 49 | @property (nonatomic, assign) BOOL useAnimator; 50 | 51 | @end 52 | 53 | @implementation MASViewConstraint 54 | 55 | - (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute { 56 | self = [super init]; 57 | if (!self) return nil; 58 | 59 | _firstViewAttribute = firstViewAttribute; 60 | self.layoutPriority = MASLayoutPriorityRequired; 61 | self.layoutMultiplier = 1; 62 | 63 | return self; 64 | } 65 | 66 | #pragma mark - NSCoping 67 | 68 | - (id)copyWithZone:(NSZone __unused *)zone { 69 | MASViewConstraint *constraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:self.firstViewAttribute]; 70 | constraint.layoutConstant = self.layoutConstant; 71 | constraint.layoutRelation = self.layoutRelation; 72 | constraint.layoutPriority = self.layoutPriority; 73 | constraint.layoutMultiplier = self.layoutMultiplier; 74 | constraint.delegate = self.delegate; 75 | return constraint; 76 | } 77 | 78 | #pragma mark - Public 79 | 80 | + (NSArray *)installedConstraintsForView:(MAS_VIEW *)view { 81 | return [view.mas_installedConstraints allObjects]; 82 | } 83 | 84 | #pragma mark - Private 85 | 86 | - (void)setLayoutConstant:(CGFloat)layoutConstant { 87 | _layoutConstant = layoutConstant; 88 | 89 | #if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) 90 | if (self.useAnimator) { 91 | [self.layoutConstraint.animator setConstant:layoutConstant]; 92 | } else { 93 | self.layoutConstraint.constant = layoutConstant; 94 | } 95 | #else 96 | self.layoutConstraint.constant = layoutConstant; 97 | #endif 98 | } 99 | 100 | - (void)setLayoutRelation:(NSLayoutRelation)layoutRelation { 101 | _layoutRelation = layoutRelation; 102 | self.hasLayoutRelation = YES; 103 | } 104 | 105 | - (BOOL)supportsActiveProperty { 106 | return [self.layoutConstraint respondsToSelector:@selector(isActive)]; 107 | } 108 | 109 | - (BOOL)isActive { 110 | BOOL active = YES; 111 | if ([self supportsActiveProperty]) { 112 | active = [self.layoutConstraint isActive]; 113 | } 114 | 115 | return active; 116 | } 117 | 118 | - (BOOL)hasBeenInstalled { 119 | return (self.layoutConstraint != nil) && [self isActive]; 120 | } 121 | 122 | - (void)setSecondViewAttribute:(id)secondViewAttribute { 123 | if ([secondViewAttribute isKindOfClass:NSValue.class]) { 124 | [self setLayoutConstantWithValue:secondViewAttribute]; 125 | } else if ([secondViewAttribute isKindOfClass:MAS_VIEW.class]) { 126 | _secondViewAttribute = [[MASViewAttribute alloc] initWithView:secondViewAttribute layoutAttribute:self.firstViewAttribute.layoutAttribute]; 127 | } else if ([secondViewAttribute isKindOfClass:MASViewAttribute.class]) { 128 | _secondViewAttribute = secondViewAttribute; 129 | } else { 130 | NSAssert(NO, @"attempting to add unsupported attribute: %@", secondViewAttribute); 131 | } 132 | } 133 | 134 | #pragma mark - NSLayoutConstraint multiplier proxies 135 | 136 | - (MASConstraint * (^)(CGFloat))multipliedBy { 137 | return ^id(CGFloat multiplier) { 138 | NSAssert(!self.hasBeenInstalled, 139 | @"Cannot modify constraint multiplier after it has been installed"); 140 | 141 | self.layoutMultiplier = multiplier; 142 | return self; 143 | }; 144 | } 145 | 146 | 147 | - (MASConstraint * (^)(CGFloat))dividedBy { 148 | return ^id(CGFloat divider) { 149 | NSAssert(!self.hasBeenInstalled, 150 | @"Cannot modify constraint multiplier after it has been installed"); 151 | 152 | self.layoutMultiplier = 1.0/divider; 153 | return self; 154 | }; 155 | } 156 | 157 | #pragma mark - MASLayoutPriority proxy 158 | 159 | - (MASConstraint * (^)(MASLayoutPriority))priority { 160 | return ^id(MASLayoutPriority priority) { 161 | NSAssert(!self.hasBeenInstalled, 162 | @"Cannot modify constraint priority after it has been installed"); 163 | 164 | self.layoutPriority = priority; 165 | return self; 166 | }; 167 | } 168 | 169 | #pragma mark - NSLayoutRelation proxy 170 | 171 | - (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { 172 | return ^id(id attribute, NSLayoutRelation relation) { 173 | if ([attribute isKindOfClass:NSArray.class]) { 174 | NSAssert(!self.hasLayoutRelation, @"Redefinition of constraint relation"); 175 | NSMutableArray *children = NSMutableArray.new; 176 | for (id attr in attribute) { 177 | MASViewConstraint *viewConstraint = [self copy]; 178 | viewConstraint.layoutRelation = relation; 179 | viewConstraint.secondViewAttribute = attr; 180 | [children addObject:viewConstraint]; 181 | } 182 | MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children]; 183 | compositeConstraint.delegate = self.delegate; 184 | [self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint]; 185 | return compositeConstraint; 186 | } else { 187 | NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation"); 188 | self.layoutRelation = relation; 189 | self.secondViewAttribute = attribute; 190 | return self; 191 | } 192 | }; 193 | } 194 | 195 | #pragma mark - Semantic properties 196 | 197 | - (MASConstraint *)with { 198 | return self; 199 | } 200 | 201 | - (MASConstraint *)and { 202 | return self; 203 | } 204 | 205 | #pragma mark - attribute chaining 206 | 207 | - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { 208 | NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation"); 209 | 210 | return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute]; 211 | } 212 | 213 | #pragma mark - Animator proxy 214 | 215 | #if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) 216 | 217 | - (MASConstraint *)animator { 218 | self.useAnimator = YES; 219 | return self; 220 | } 221 | 222 | #endif 223 | 224 | #pragma mark - debug helpers 225 | 226 | - (MASConstraint * (^)(id))key { 227 | return ^id(id key) { 228 | self.mas_key = key; 229 | return self; 230 | }; 231 | } 232 | 233 | #pragma mark - NSLayoutConstraint constant setters 234 | 235 | - (void)setInsets:(MASEdgeInsets)insets { 236 | NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute; 237 | switch (layoutAttribute) { 238 | case NSLayoutAttributeLeft: 239 | case NSLayoutAttributeLeading: 240 | self.layoutConstant = insets.left; 241 | break; 242 | case NSLayoutAttributeTop: 243 | self.layoutConstant = insets.top; 244 | break; 245 | case NSLayoutAttributeBottom: 246 | self.layoutConstant = -insets.bottom; 247 | break; 248 | case NSLayoutAttributeRight: 249 | case NSLayoutAttributeTrailing: 250 | self.layoutConstant = -insets.right; 251 | break; 252 | default: 253 | break; 254 | } 255 | } 256 | 257 | - (void)setInset:(CGFloat)inset { 258 | [self setInsets:(MASEdgeInsets){.top = inset, .left = inset, .bottom = inset, .right = inset}]; 259 | } 260 | 261 | - (void)setOffset:(CGFloat)offset { 262 | self.layoutConstant = offset; 263 | } 264 | 265 | - (void)setSizeOffset:(CGSize)sizeOffset { 266 | NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute; 267 | switch (layoutAttribute) { 268 | case NSLayoutAttributeWidth: 269 | self.layoutConstant = sizeOffset.width; 270 | break; 271 | case NSLayoutAttributeHeight: 272 | self.layoutConstant = sizeOffset.height; 273 | break; 274 | default: 275 | break; 276 | } 277 | } 278 | 279 | - (void)setCenterOffset:(CGPoint)centerOffset { 280 | NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute; 281 | switch (layoutAttribute) { 282 | case NSLayoutAttributeCenterX: 283 | self.layoutConstant = centerOffset.x; 284 | break; 285 | case NSLayoutAttributeCenterY: 286 | self.layoutConstant = centerOffset.y; 287 | break; 288 | default: 289 | break; 290 | } 291 | } 292 | 293 | #pragma mark - MASConstraint 294 | 295 | - (void)activate { 296 | [self install]; 297 | } 298 | 299 | - (void)deactivate { 300 | [self uninstall]; 301 | } 302 | 303 | - (void)install { 304 | if (self.hasBeenInstalled) { 305 | return; 306 | } 307 | 308 | if ([self supportsActiveProperty] && self.layoutConstraint) { 309 | self.layoutConstraint.active = YES; 310 | [self.firstViewAttribute.view.mas_installedConstraints addObject:self]; 311 | return; 312 | } 313 | 314 | MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item; 315 | NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute; 316 | MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item; 317 | NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute; 318 | 319 | // alignment attributes must have a secondViewAttribute 320 | // therefore we assume that is refering to superview 321 | // eg make.left.equalTo(@10) 322 | if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) { 323 | secondLayoutItem = self.firstViewAttribute.view.superview; 324 | secondLayoutAttribute = firstLayoutAttribute; 325 | } 326 | 327 | MASLayoutConstraint *layoutConstraint 328 | = [MASLayoutConstraint constraintWithItem:firstLayoutItem 329 | attribute:firstLayoutAttribute 330 | relatedBy:self.layoutRelation 331 | toItem:secondLayoutItem 332 | attribute:secondLayoutAttribute 333 | multiplier:self.layoutMultiplier 334 | constant:self.layoutConstant]; 335 | 336 | layoutConstraint.priority = self.layoutPriority; 337 | layoutConstraint.mas_key = self.mas_key; 338 | 339 | if (self.secondViewAttribute.view) { 340 | MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view]; 341 | NSAssert(closestCommonSuperview, 342 | @"couldn't find a common superview for %@ and %@", 343 | self.firstViewAttribute.view, self.secondViewAttribute.view); 344 | self.installedView = closestCommonSuperview; 345 | } else if (self.firstViewAttribute.isSizeAttribute) { 346 | self.installedView = self.firstViewAttribute.view; 347 | } else { 348 | self.installedView = self.firstViewAttribute.view.superview; 349 | } 350 | 351 | 352 | MASLayoutConstraint *existingConstraint = nil; 353 | if (self.updateExisting) { 354 | existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint]; 355 | } 356 | if (existingConstraint) { 357 | // just update the constant 358 | existingConstraint.constant = layoutConstraint.constant; 359 | self.layoutConstraint = existingConstraint; 360 | } else { 361 | [self.installedView addConstraint:layoutConstraint]; 362 | self.layoutConstraint = layoutConstraint; 363 | [firstLayoutItem.mas_installedConstraints addObject:self]; 364 | } 365 | } 366 | 367 | - (MASLayoutConstraint *)layoutConstraintSimilarTo:(MASLayoutConstraint *)layoutConstraint { 368 | // check if any constraints are the same apart from the only mutable property constant 369 | 370 | // go through constraints in reverse as we do not want to match auto-resizing or interface builder constraints 371 | // and they are likely to be added first. 372 | for (NSLayoutConstraint *existingConstraint in self.installedView.constraints.reverseObjectEnumerator) { 373 | if (![existingConstraint isKindOfClass:MASLayoutConstraint.class]) continue; 374 | if (existingConstraint.firstItem != layoutConstraint.firstItem) continue; 375 | if (existingConstraint.secondItem != layoutConstraint.secondItem) continue; 376 | if (existingConstraint.firstAttribute != layoutConstraint.firstAttribute) continue; 377 | if (existingConstraint.secondAttribute != layoutConstraint.secondAttribute) continue; 378 | if (existingConstraint.relation != layoutConstraint.relation) continue; 379 | if (existingConstraint.multiplier != layoutConstraint.multiplier) continue; 380 | if (existingConstraint.priority != layoutConstraint.priority) continue; 381 | 382 | return (id)existingConstraint; 383 | } 384 | return nil; 385 | } 386 | 387 | - (void)uninstall { 388 | if ([self supportsActiveProperty]) { 389 | self.layoutConstraint.active = NO; 390 | [self.firstViewAttribute.view.mas_installedConstraints removeObject:self]; 391 | return; 392 | } 393 | 394 | [self.installedView removeConstraint:self.layoutConstraint]; 395 | self.layoutConstraint = nil; 396 | self.installedView = nil; 397 | 398 | [self.firstViewAttribute.view.mas_installedConstraints removeObject:self]; 399 | } 400 | 401 | @end 402 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/Masonry.h: -------------------------------------------------------------------------------- 1 | // 2 | // Masonry.h 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 20/07/13. 6 | // Copyright (c) 2013 cloudling. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Masonry. 12 | FOUNDATION_EXPORT double MasonryVersionNumber; 13 | 14 | //! Project version string for Masonry. 15 | FOUNDATION_EXPORT const unsigned char MasonryVersionString[]; 16 | 17 | #import "MASUtilities.h" 18 | #import "View+MASAdditions.h" 19 | #import "View+MASShorthandAdditions.h" 20 | #import "ViewController+MASAdditions.h" 21 | #import "NSArray+MASAdditions.h" 22 | #import "NSArray+MASShorthandAdditions.h" 23 | #import "MASConstraint.h" 24 | #import "MASCompositeConstraint.h" 25 | #import "MASViewAttribute.h" 26 | #import "MASViewConstraint.h" 27 | #import "MASConstraintMaker.h" 28 | #import "MASLayoutConstraint.h" 29 | #import "NSLayoutConstraint+MASDebugAdditions.h" 30 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/NSArray+MASAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+MASAdditions.h 3 | // 4 | // 5 | // Created by Daniel Hammond on 11/26/13. 6 | // 7 | // 8 | 9 | #import "MASUtilities.h" 10 | #import "MASConstraintMaker.h" 11 | #import "MASViewAttribute.h" 12 | 13 | typedef NS_ENUM(NSUInteger, MASAxisType) { 14 | MASAxisTypeHorizontal, 15 | MASAxisTypeVertical 16 | }; 17 | 18 | @interface NSArray (MASAdditions) 19 | 20 | /** 21 | * Creates a MASConstraintMaker with each view in the callee. 22 | * Any constraints defined are added to the view or the appropriate superview once the block has finished executing on each view 23 | * 24 | * @param block scope within which you can build up the constraints which you wish to apply to each view. 25 | * 26 | * @return Array of created MASConstraints 27 | */ 28 | - (NSArray *)mas_makeConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block; 29 | 30 | /** 31 | * Creates a MASConstraintMaker with each view in the callee. 32 | * Any constraints defined are added to each view or the appropriate superview once the block has finished executing on each view. 33 | * If an existing constraint exists then it will be updated instead. 34 | * 35 | * @param block scope within which you can build up the constraints which you wish to apply to each view. 36 | * 37 | * @return Array of created/updated MASConstraints 38 | */ 39 | - (NSArray *)mas_updateConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block; 40 | 41 | /** 42 | * Creates a MASConstraintMaker with each view in the callee. 43 | * Any constraints defined are added to each view or the appropriate superview once the block has finished executing on each view. 44 | * All constraints previously installed for the views will be removed. 45 | * 46 | * @param block scope within which you can build up the constraints which you wish to apply to each view. 47 | * 48 | * @return Array of created/updated MASConstraints 49 | */ 50 | - (NSArray *)mas_remakeConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block; 51 | 52 | /** 53 | * distribute with fixed spacing 54 | * 55 | * @param axisType which axis to distribute items along 56 | * @param fixedSpacing the spacing between each item 57 | * @param leadSpacing the spacing before the first item and the container 58 | * @param tailSpacing the spacing after the last item and the container 59 | */ 60 | - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing; 61 | 62 | /** 63 | * distribute with fixed item size 64 | * 65 | * @param axisType which axis to distribute items along 66 | * @param fixedItemLength the fixed length of each item 67 | * @param leadSpacing the spacing before the first item and the container 68 | * @param tailSpacing the spacing after the last item and the container 69 | */ 70 | - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing; 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/NSArray+MASAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+MASAdditions.m 3 | // 4 | // 5 | // Created by Daniel Hammond on 11/26/13. 6 | // 7 | // 8 | 9 | #import "NSArray+MASAdditions.h" 10 | #import "View+MASAdditions.h" 11 | 12 | @implementation NSArray (MASAdditions) 13 | 14 | - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block { 15 | NSMutableArray *constraints = [NSMutableArray array]; 16 | for (MAS_VIEW *view in self) { 17 | NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views"); 18 | [constraints addObjectsFromArray:[view mas_makeConstraints:block]]; 19 | } 20 | return constraints; 21 | } 22 | 23 | - (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block { 24 | NSMutableArray *constraints = [NSMutableArray array]; 25 | for (MAS_VIEW *view in self) { 26 | NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views"); 27 | [constraints addObjectsFromArray:[view mas_updateConstraints:block]]; 28 | } 29 | return constraints; 30 | } 31 | 32 | - (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block { 33 | NSMutableArray *constraints = [NSMutableArray array]; 34 | for (MAS_VIEW *view in self) { 35 | NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views"); 36 | [constraints addObjectsFromArray:[view mas_remakeConstraints:block]]; 37 | } 38 | return constraints; 39 | } 40 | 41 | - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing { 42 | if (self.count < 2) { 43 | NSAssert(self.count>1,@"views to distribute need to bigger than one"); 44 | return; 45 | } 46 | 47 | MAS_VIEW *tempSuperView = [self mas_commonSuperviewOfViews]; 48 | if (axisType == MASAxisTypeHorizontal) { 49 | MAS_VIEW *prev; 50 | for (int i = 0; i < self.count; i++) { 51 | MAS_VIEW *v = self[i]; 52 | [v mas_makeConstraints:^(MASConstraintMaker *make) { 53 | if (prev) { 54 | make.width.equalTo(prev); 55 | make.left.equalTo(prev.mas_right).offset(fixedSpacing); 56 | if (i == self.count - 1) {//last one 57 | make.right.equalTo(tempSuperView).offset(-tailSpacing); 58 | } 59 | } 60 | else {//first one 61 | make.left.equalTo(tempSuperView).offset(leadSpacing); 62 | } 63 | 64 | }]; 65 | prev = v; 66 | } 67 | } 68 | else { 69 | MAS_VIEW *prev; 70 | for (int i = 0; i < self.count; i++) { 71 | MAS_VIEW *v = self[i]; 72 | [v mas_makeConstraints:^(MASConstraintMaker *make) { 73 | if (prev) { 74 | make.height.equalTo(prev); 75 | make.top.equalTo(prev.mas_bottom).offset(fixedSpacing); 76 | if (i == self.count - 1) {//last one 77 | make.bottom.equalTo(tempSuperView).offset(-tailSpacing); 78 | } 79 | } 80 | else {//first one 81 | make.top.equalTo(tempSuperView).offset(leadSpacing); 82 | } 83 | 84 | }]; 85 | prev = v; 86 | } 87 | } 88 | } 89 | 90 | - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing { 91 | if (self.count < 2) { 92 | NSAssert(self.count>1,@"views to distribute need to bigger than one"); 93 | return; 94 | } 95 | 96 | MAS_VIEW *tempSuperView = [self mas_commonSuperviewOfViews]; 97 | if (axisType == MASAxisTypeHorizontal) { 98 | MAS_VIEW *prev; 99 | for (int i = 0; i < self.count; i++) { 100 | MAS_VIEW *v = self[i]; 101 | [v mas_makeConstraints:^(MASConstraintMaker *make) { 102 | make.width.equalTo(@(fixedItemLength)); 103 | if (prev) { 104 | if (i == self.count - 1) {//last one 105 | make.right.equalTo(tempSuperView).offset(-tailSpacing); 106 | } 107 | else { 108 | CGFloat offset = (1-(i/((CGFloat)self.count-1)))*(fixedItemLength+leadSpacing)-i*tailSpacing/(((CGFloat)self.count-1)); 109 | make.right.equalTo(tempSuperView).multipliedBy(i/((CGFloat)self.count-1)).with.offset(offset); 110 | } 111 | } 112 | else {//first one 113 | make.left.equalTo(tempSuperView).offset(leadSpacing); 114 | } 115 | }]; 116 | prev = v; 117 | } 118 | } 119 | else { 120 | MAS_VIEW *prev; 121 | for (int i = 0; i < self.count; i++) { 122 | MAS_VIEW *v = self[i]; 123 | [v mas_makeConstraints:^(MASConstraintMaker *make) { 124 | make.height.equalTo(@(fixedItemLength)); 125 | if (prev) { 126 | if (i == self.count - 1) {//last one 127 | make.bottom.equalTo(tempSuperView).offset(-tailSpacing); 128 | } 129 | else { 130 | CGFloat offset = (1-(i/((CGFloat)self.count-1)))*(fixedItemLength+leadSpacing)-i*tailSpacing/(((CGFloat)self.count-1)); 131 | make.bottom.equalTo(tempSuperView).multipliedBy(i/((CGFloat)self.count-1)).with.offset(offset); 132 | } 133 | } 134 | else {//first one 135 | make.top.equalTo(tempSuperView).offset(leadSpacing); 136 | } 137 | }]; 138 | prev = v; 139 | } 140 | } 141 | } 142 | 143 | - (MAS_VIEW *)mas_commonSuperviewOfViews 144 | { 145 | MAS_VIEW *commonSuperview = nil; 146 | MAS_VIEW *previousView = nil; 147 | for (id object in self) { 148 | if ([object isKindOfClass:[MAS_VIEW class]]) { 149 | MAS_VIEW *view = (MAS_VIEW *)object; 150 | if (previousView) { 151 | commonSuperview = [view mas_closestCommonSuperview:commonSuperview]; 152 | } else { 153 | commonSuperview = view; 154 | } 155 | previousView = view; 156 | } 157 | } 158 | NSAssert(commonSuperview, @"Can't constrain views that do not share a common superview. Make sure that all the views in this array have been added into the same view hierarchy."); 159 | return commonSuperview; 160 | } 161 | 162 | @end 163 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/NSArray+MASShorthandAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+MASShorthandAdditions.h 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 22/07/13. 6 | // Copyright (c) 2013 Jonas Budelmann. All rights reserved. 7 | // 8 | 9 | #import "NSArray+MASAdditions.h" 10 | 11 | #ifdef MAS_SHORTHAND 12 | 13 | /** 14 | * Shorthand array additions without the 'mas_' prefixes, 15 | * only enabled if MAS_SHORTHAND is defined 16 | */ 17 | @interface NSArray (MASShorthandAdditions) 18 | 19 | - (NSArray *)makeConstraints:(void(^)(MASConstraintMaker *make))block; 20 | - (NSArray *)updateConstraints:(void(^)(MASConstraintMaker *make))block; 21 | - (NSArray *)remakeConstraints:(void(^)(MASConstraintMaker *make))block; 22 | 23 | @end 24 | 25 | @implementation NSArray (MASShorthandAdditions) 26 | 27 | - (NSArray *)makeConstraints:(void(^)(MASConstraintMaker *))block { 28 | return [self mas_makeConstraints:block]; 29 | } 30 | 31 | - (NSArray *)updateConstraints:(void(^)(MASConstraintMaker *))block { 32 | return [self mas_updateConstraints:block]; 33 | } 34 | 35 | - (NSArray *)remakeConstraints:(void(^)(MASConstraintMaker *))block { 36 | return [self mas_remakeConstraints:block]; 37 | } 38 | 39 | @end 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/NSLayoutConstraint+MASDebugAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSLayoutConstraint+MASDebugAdditions.h 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 3/08/13. 6 | // Copyright (c) 2013 Jonas Budelmann. All rights reserved. 7 | // 8 | 9 | #import "MASUtilities.h" 10 | 11 | /** 12 | * makes debug and log output of NSLayoutConstraints more readable 13 | */ 14 | @interface NSLayoutConstraint (MASDebugAdditions) 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/NSLayoutConstraint+MASDebugAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSLayoutConstraint+MASDebugAdditions.m 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 3/08/13. 6 | // Copyright (c) 2013 Jonas Budelmann. All rights reserved. 7 | // 8 | 9 | #import "NSLayoutConstraint+MASDebugAdditions.h" 10 | #import "MASConstraint.h" 11 | #import "MASLayoutConstraint.h" 12 | 13 | @implementation NSLayoutConstraint (MASDebugAdditions) 14 | 15 | #pragma mark - description maps 16 | 17 | + (NSDictionary *)layoutRelationDescriptionsByValue { 18 | static dispatch_once_t once; 19 | static NSDictionary *descriptionMap; 20 | dispatch_once(&once, ^{ 21 | descriptionMap = @{ 22 | @(NSLayoutRelationEqual) : @"==", 23 | @(NSLayoutRelationGreaterThanOrEqual) : @">=", 24 | @(NSLayoutRelationLessThanOrEqual) : @"<=", 25 | }; 26 | }); 27 | return descriptionMap; 28 | } 29 | 30 | + (NSDictionary *)layoutAttributeDescriptionsByValue { 31 | static dispatch_once_t once; 32 | static NSDictionary *descriptionMap; 33 | dispatch_once(&once, ^{ 34 | descriptionMap = @{ 35 | @(NSLayoutAttributeTop) : @"top", 36 | @(NSLayoutAttributeLeft) : @"left", 37 | @(NSLayoutAttributeBottom) : @"bottom", 38 | @(NSLayoutAttributeRight) : @"right", 39 | @(NSLayoutAttributeLeading) : @"leading", 40 | @(NSLayoutAttributeTrailing) : @"trailing", 41 | @(NSLayoutAttributeWidth) : @"width", 42 | @(NSLayoutAttributeHeight) : @"height", 43 | @(NSLayoutAttributeCenterX) : @"centerX", 44 | @(NSLayoutAttributeCenterY) : @"centerY", 45 | @(NSLayoutAttributeBaseline) : @"baseline", 46 | 47 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) 48 | @(NSLayoutAttributeFirstBaseline) : @"firstBaseline", 49 | @(NSLayoutAttributeLastBaseline) : @"lastBaseline", 50 | #endif 51 | 52 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) 53 | @(NSLayoutAttributeLeftMargin) : @"leftMargin", 54 | @(NSLayoutAttributeRightMargin) : @"rightMargin", 55 | @(NSLayoutAttributeTopMargin) : @"topMargin", 56 | @(NSLayoutAttributeBottomMargin) : @"bottomMargin", 57 | @(NSLayoutAttributeLeadingMargin) : @"leadingMargin", 58 | @(NSLayoutAttributeTrailingMargin) : @"trailingMargin", 59 | @(NSLayoutAttributeCenterXWithinMargins) : @"centerXWithinMargins", 60 | @(NSLayoutAttributeCenterYWithinMargins) : @"centerYWithinMargins", 61 | #endif 62 | 63 | }; 64 | 65 | }); 66 | return descriptionMap; 67 | } 68 | 69 | 70 | + (NSDictionary *)layoutPriorityDescriptionsByValue { 71 | static dispatch_once_t once; 72 | static NSDictionary *descriptionMap; 73 | dispatch_once(&once, ^{ 74 | #if TARGET_OS_IPHONE || TARGET_OS_TV 75 | descriptionMap = @{ 76 | @(MASLayoutPriorityDefaultHigh) : @"high", 77 | @(MASLayoutPriorityDefaultLow) : @"low", 78 | @(MASLayoutPriorityDefaultMedium) : @"medium", 79 | @(MASLayoutPriorityRequired) : @"required", 80 | @(MASLayoutPriorityFittingSizeLevel) : @"fitting size", 81 | }; 82 | #elif TARGET_OS_MAC 83 | descriptionMap = @{ 84 | @(MASLayoutPriorityDefaultHigh) : @"high", 85 | @(MASLayoutPriorityDragThatCanResizeWindow) : @"drag can resize window", 86 | @(MASLayoutPriorityDefaultMedium) : @"medium", 87 | @(MASLayoutPriorityWindowSizeStayPut) : @"window size stay put", 88 | @(MASLayoutPriorityDragThatCannotResizeWindow) : @"drag cannot resize window", 89 | @(MASLayoutPriorityDefaultLow) : @"low", 90 | @(MASLayoutPriorityFittingSizeCompression) : @"fitting size", 91 | @(MASLayoutPriorityRequired) : @"required", 92 | }; 93 | #endif 94 | }); 95 | return descriptionMap; 96 | } 97 | 98 | #pragma mark - description override 99 | 100 | + (NSString *)descriptionForObject:(id)obj { 101 | if ([obj respondsToSelector:@selector(mas_key)] && [obj mas_key]) { 102 | return [NSString stringWithFormat:@"%@:%@", [obj class], [obj mas_key]]; 103 | } 104 | return [NSString stringWithFormat:@"%@:%p", [obj class], obj]; 105 | } 106 | 107 | - (NSString *)description { 108 | NSMutableString *description = [[NSMutableString alloc] initWithString:@"<"]; 109 | 110 | [description appendString:[self.class descriptionForObject:self]]; 111 | 112 | [description appendFormat:@" %@", [self.class descriptionForObject:self.firstItem]]; 113 | if (self.firstAttribute != NSLayoutAttributeNotAnAttribute) { 114 | [description appendFormat:@".%@", self.class.layoutAttributeDescriptionsByValue[@(self.firstAttribute)]]; 115 | } 116 | 117 | [description appendFormat:@" %@", self.class.layoutRelationDescriptionsByValue[@(self.relation)]]; 118 | 119 | if (self.secondItem) { 120 | [description appendFormat:@" %@", [self.class descriptionForObject:self.secondItem]]; 121 | } 122 | if (self.secondAttribute != NSLayoutAttributeNotAnAttribute) { 123 | [description appendFormat:@".%@", self.class.layoutAttributeDescriptionsByValue[@(self.secondAttribute)]]; 124 | } 125 | 126 | if (self.multiplier != 1) { 127 | [description appendFormat:@" * %g", self.multiplier]; 128 | } 129 | 130 | if (self.secondAttribute == NSLayoutAttributeNotAnAttribute) { 131 | [description appendFormat:@" %g", self.constant]; 132 | } else { 133 | if (self.constant) { 134 | [description appendFormat:@" %@ %g", (self.constant < 0 ? @"-" : @"+"), ABS(self.constant)]; 135 | } 136 | } 137 | 138 | if (self.priority != MASLayoutPriorityRequired) { 139 | [description appendFormat:@" ^%@", self.class.layoutPriorityDescriptionsByValue[@(self.priority)] ?: [NSNumber numberWithDouble:self.priority]]; 140 | } 141 | 142 | [description appendString:@">"]; 143 | return description; 144 | } 145 | 146 | @end 147 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/View+MASAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+MASAdditions.h 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 20/07/13. 6 | // Copyright (c) 2013 cloudling. All rights reserved. 7 | // 8 | 9 | #import "MASUtilities.h" 10 | #import "MASConstraintMaker.h" 11 | #import "MASViewAttribute.h" 12 | 13 | /** 14 | * Provides constraint maker block 15 | * and convience methods for creating MASViewAttribute which are view + NSLayoutAttribute pairs 16 | */ 17 | @interface MAS_VIEW (MASAdditions) 18 | 19 | /** 20 | * following properties return a new MASViewAttribute with current view and appropriate NSLayoutAttribute 21 | */ 22 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_left; 23 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_top; 24 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_right; 25 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottom; 26 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_leading; 27 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_trailing; 28 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_width; 29 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_height; 30 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_centerX; 31 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_centerY; 32 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_baseline; 33 | @property (nonatomic, strong, readonly) MASViewAttribute *(^mas_attribute)(NSLayoutAttribute attr); 34 | 35 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) 36 | 37 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_firstBaseline; 38 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_lastBaseline; 39 | 40 | #endif 41 | 42 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) 43 | 44 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_leftMargin; 45 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_rightMargin; 46 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_topMargin; 47 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomMargin; 48 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_leadingMargin; 49 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_trailingMargin; 50 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_centerXWithinMargins; 51 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_centerYWithinMargins; 52 | 53 | #endif 54 | 55 | #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) 56 | 57 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuide API_AVAILABLE(ios(11.0),tvos(11.0)); 58 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideTop API_AVAILABLE(ios(11.0),tvos(11.0)); 59 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideBottom API_AVAILABLE(ios(11.0),tvos(11.0)); 60 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideLeft API_AVAILABLE(ios(11.0),tvos(11.0)); 61 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideRight API_AVAILABLE(ios(11.0),tvos(11.0)); 62 | 63 | #endif 64 | 65 | /** 66 | * a key to associate with this view 67 | */ 68 | @property (nonatomic, strong) id mas_key; 69 | 70 | /** 71 | * Finds the closest common superview between this view and another view 72 | * 73 | * @param view other view 74 | * 75 | * @return returns nil if common superview could not be found 76 | */ 77 | - (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view; 78 | 79 | /** 80 | * Creates a MASConstraintMaker with the callee view. 81 | * Any constraints defined are added to the view or the appropriate superview once the block has finished executing 82 | * 83 | * @param block scope within which you can build up the constraints which you wish to apply to the view. 84 | * 85 | * @return Array of created MASConstraints 86 | */ 87 | - (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block; 88 | 89 | /** 90 | * Creates a MASConstraintMaker with the callee view. 91 | * Any constraints defined are added to the view or the appropriate superview once the block has finished executing. 92 | * If an existing constraint exists then it will be updated instead. 93 | * 94 | * @param block scope within which you can build up the constraints which you wish to apply to the view. 95 | * 96 | * @return Array of created/updated MASConstraints 97 | */ 98 | - (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block; 99 | 100 | /** 101 | * Creates a MASConstraintMaker with the callee view. 102 | * Any constraints defined are added to the view or the appropriate superview once the block has finished executing. 103 | * All constraints previously installed for the view will be removed. 104 | * 105 | * @param block scope within which you can build up the constraints which you wish to apply to the view. 106 | * 107 | * @return Array of created/updated MASConstraints 108 | */ 109 | - (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block; 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/View+MASAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+MASAdditions.m 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 20/07/13. 6 | // Copyright (c) 2013 cloudling. All rights reserved. 7 | // 8 | 9 | #import "View+MASAdditions.h" 10 | #import 11 | 12 | @implementation MAS_VIEW (MASAdditions) 13 | 14 | - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block { 15 | self.translatesAutoresizingMaskIntoConstraints = NO; 16 | MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; 17 | block(constraintMaker); 18 | return [constraintMaker install]; 19 | } 20 | 21 | - (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block { 22 | self.translatesAutoresizingMaskIntoConstraints = NO; 23 | MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; 24 | constraintMaker.updateExisting = YES; 25 | block(constraintMaker); 26 | return [constraintMaker install]; 27 | } 28 | 29 | - (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block { 30 | self.translatesAutoresizingMaskIntoConstraints = NO; 31 | MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; 32 | constraintMaker.removeExisting = YES; 33 | block(constraintMaker); 34 | return [constraintMaker install]; 35 | } 36 | 37 | #pragma mark - NSLayoutAttribute properties 38 | 39 | - (MASViewAttribute *)mas_left { 40 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeft]; 41 | } 42 | 43 | - (MASViewAttribute *)mas_top { 44 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTop]; 45 | } 46 | 47 | - (MASViewAttribute *)mas_right { 48 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeRight]; 49 | } 50 | 51 | - (MASViewAttribute *)mas_bottom { 52 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeBottom]; 53 | } 54 | 55 | - (MASViewAttribute *)mas_leading { 56 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeading]; 57 | } 58 | 59 | - (MASViewAttribute *)mas_trailing { 60 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTrailing]; 61 | } 62 | 63 | - (MASViewAttribute *)mas_width { 64 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeWidth]; 65 | } 66 | 67 | - (MASViewAttribute *)mas_height { 68 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeHeight]; 69 | } 70 | 71 | - (MASViewAttribute *)mas_centerX { 72 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeCenterX]; 73 | } 74 | 75 | - (MASViewAttribute *)mas_centerY { 76 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeCenterY]; 77 | } 78 | 79 | - (MASViewAttribute *)mas_baseline { 80 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeBaseline]; 81 | } 82 | 83 | - (MASViewAttribute *(^)(NSLayoutAttribute))mas_attribute 84 | { 85 | return ^(NSLayoutAttribute attr) { 86 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:attr]; 87 | }; 88 | } 89 | 90 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) 91 | 92 | - (MASViewAttribute *)mas_firstBaseline { 93 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeFirstBaseline]; 94 | } 95 | - (MASViewAttribute *)mas_lastBaseline { 96 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLastBaseline]; 97 | } 98 | 99 | #endif 100 | 101 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) 102 | 103 | - (MASViewAttribute *)mas_leftMargin { 104 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeftMargin]; 105 | } 106 | 107 | - (MASViewAttribute *)mas_rightMargin { 108 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeRightMargin]; 109 | } 110 | 111 | - (MASViewAttribute *)mas_topMargin { 112 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTopMargin]; 113 | } 114 | 115 | - (MASViewAttribute *)mas_bottomMargin { 116 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeBottomMargin]; 117 | } 118 | 119 | - (MASViewAttribute *)mas_leadingMargin { 120 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeadingMargin]; 121 | } 122 | 123 | - (MASViewAttribute *)mas_trailingMargin { 124 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTrailingMargin]; 125 | } 126 | 127 | - (MASViewAttribute *)mas_centerXWithinMargins { 128 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeCenterXWithinMargins]; 129 | } 130 | 131 | - (MASViewAttribute *)mas_centerYWithinMargins { 132 | return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeCenterYWithinMargins]; 133 | } 134 | 135 | #endif 136 | 137 | #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) 138 | 139 | - (MASViewAttribute *)mas_safeAreaLayoutGuide { 140 | return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; 141 | } 142 | - (MASViewAttribute *)mas_safeAreaLayoutGuideTop { 143 | return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeTop]; 144 | } 145 | - (MASViewAttribute *)mas_safeAreaLayoutGuideBottom { 146 | return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; 147 | } 148 | - (MASViewAttribute *)mas_safeAreaLayoutGuideLeft { 149 | return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeLeft]; 150 | } 151 | - (MASViewAttribute *)mas_safeAreaLayoutGuideRight { 152 | return [[MASViewAttribute alloc] initWithView:self item:self.safeAreaLayoutGuide layoutAttribute:NSLayoutAttributeRight]; 153 | } 154 | 155 | #endif 156 | 157 | #pragma mark - associated properties 158 | 159 | - (id)mas_key { 160 | return objc_getAssociatedObject(self, @selector(mas_key)); 161 | } 162 | 163 | - (void)setMas_key:(id)key { 164 | objc_setAssociatedObject(self, @selector(mas_key), key, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 165 | } 166 | 167 | #pragma mark - heirachy 168 | 169 | - (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view { 170 | MAS_VIEW *closestCommonSuperview = nil; 171 | 172 | MAS_VIEW *secondViewSuperview = view; 173 | while (!closestCommonSuperview && secondViewSuperview) { 174 | MAS_VIEW *firstViewSuperview = self; 175 | while (!closestCommonSuperview && firstViewSuperview) { 176 | if (secondViewSuperview == firstViewSuperview) { 177 | closestCommonSuperview = secondViewSuperview; 178 | } 179 | firstViewSuperview = firstViewSuperview.superview; 180 | } 181 | secondViewSuperview = secondViewSuperview.superview; 182 | } 183 | return closestCommonSuperview; 184 | } 185 | 186 | @end 187 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/View+MASShorthandAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+MASShorthandAdditions.h 3 | // Masonry 4 | // 5 | // Created by Jonas Budelmann on 22/07/13. 6 | // Copyright (c) 2013 Jonas Budelmann. All rights reserved. 7 | // 8 | 9 | #import "View+MASAdditions.h" 10 | 11 | #ifdef MAS_SHORTHAND 12 | 13 | /** 14 | * Shorthand view additions without the 'mas_' prefixes, 15 | * only enabled if MAS_SHORTHAND is defined 16 | */ 17 | @interface MAS_VIEW (MASShorthandAdditions) 18 | 19 | @property (nonatomic, strong, readonly) MASViewAttribute *left; 20 | @property (nonatomic, strong, readonly) MASViewAttribute *top; 21 | @property (nonatomic, strong, readonly) MASViewAttribute *right; 22 | @property (nonatomic, strong, readonly) MASViewAttribute *bottom; 23 | @property (nonatomic, strong, readonly) MASViewAttribute *leading; 24 | @property (nonatomic, strong, readonly) MASViewAttribute *trailing; 25 | @property (nonatomic, strong, readonly) MASViewAttribute *width; 26 | @property (nonatomic, strong, readonly) MASViewAttribute *height; 27 | @property (nonatomic, strong, readonly) MASViewAttribute *centerX; 28 | @property (nonatomic, strong, readonly) MASViewAttribute *centerY; 29 | @property (nonatomic, strong, readonly) MASViewAttribute *baseline; 30 | @property (nonatomic, strong, readonly) MASViewAttribute *(^attribute)(NSLayoutAttribute attr); 31 | 32 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) 33 | 34 | @property (nonatomic, strong, readonly) MASViewAttribute *firstBaseline; 35 | @property (nonatomic, strong, readonly) MASViewAttribute *lastBaseline; 36 | 37 | #endif 38 | 39 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) 40 | 41 | @property (nonatomic, strong, readonly) MASViewAttribute *leftMargin; 42 | @property (nonatomic, strong, readonly) MASViewAttribute *rightMargin; 43 | @property (nonatomic, strong, readonly) MASViewAttribute *topMargin; 44 | @property (nonatomic, strong, readonly) MASViewAttribute *bottomMargin; 45 | @property (nonatomic, strong, readonly) MASViewAttribute *leadingMargin; 46 | @property (nonatomic, strong, readonly) MASViewAttribute *trailingMargin; 47 | @property (nonatomic, strong, readonly) MASViewAttribute *centerXWithinMargins; 48 | @property (nonatomic, strong, readonly) MASViewAttribute *centerYWithinMargins; 49 | 50 | #endif 51 | 52 | #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) 53 | 54 | @property (nonatomic, strong, readonly) MASViewAttribute *safeAreaLayoutGuideTop API_AVAILABLE(ios(11.0),tvos(11.0)); 55 | @property (nonatomic, strong, readonly) MASViewAttribute *safeAreaLayoutGuideBottom API_AVAILABLE(ios(11.0),tvos(11.0)); 56 | @property (nonatomic, strong, readonly) MASViewAttribute *safeAreaLayoutGuideLeft API_AVAILABLE(ios(11.0),tvos(11.0)); 57 | @property (nonatomic, strong, readonly) MASViewAttribute *safeAreaLayoutGuideRight API_AVAILABLE(ios(11.0),tvos(11.0)); 58 | 59 | #endif 60 | 61 | - (NSArray *)makeConstraints:(void(^)(MASConstraintMaker *make))block; 62 | - (NSArray *)updateConstraints:(void(^)(MASConstraintMaker *make))block; 63 | - (NSArray *)remakeConstraints:(void(^)(MASConstraintMaker *make))block; 64 | 65 | @end 66 | 67 | #define MAS_ATTR_FORWARD(attr) \ 68 | - (MASViewAttribute *)attr { \ 69 | return [self mas_##attr]; \ 70 | } 71 | 72 | @implementation MAS_VIEW (MASShorthandAdditions) 73 | 74 | MAS_ATTR_FORWARD(top); 75 | MAS_ATTR_FORWARD(left); 76 | MAS_ATTR_FORWARD(bottom); 77 | MAS_ATTR_FORWARD(right); 78 | MAS_ATTR_FORWARD(leading); 79 | MAS_ATTR_FORWARD(trailing); 80 | MAS_ATTR_FORWARD(width); 81 | MAS_ATTR_FORWARD(height); 82 | MAS_ATTR_FORWARD(centerX); 83 | MAS_ATTR_FORWARD(centerY); 84 | MAS_ATTR_FORWARD(baseline); 85 | 86 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) 87 | 88 | MAS_ATTR_FORWARD(firstBaseline); 89 | MAS_ATTR_FORWARD(lastBaseline); 90 | 91 | #endif 92 | 93 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) 94 | 95 | MAS_ATTR_FORWARD(leftMargin); 96 | MAS_ATTR_FORWARD(rightMargin); 97 | MAS_ATTR_FORWARD(topMargin); 98 | MAS_ATTR_FORWARD(bottomMargin); 99 | MAS_ATTR_FORWARD(leadingMargin); 100 | MAS_ATTR_FORWARD(trailingMargin); 101 | MAS_ATTR_FORWARD(centerXWithinMargins); 102 | MAS_ATTR_FORWARD(centerYWithinMargins); 103 | 104 | #endif 105 | 106 | #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) 107 | 108 | MAS_ATTR_FORWARD(safeAreaLayoutGuideTop); 109 | MAS_ATTR_FORWARD(safeAreaLayoutGuideBottom); 110 | MAS_ATTR_FORWARD(safeAreaLayoutGuideLeft); 111 | MAS_ATTR_FORWARD(safeAreaLayoutGuideRight); 112 | 113 | #endif 114 | 115 | - (MASViewAttribute *(^)(NSLayoutAttribute))attribute { 116 | return [self mas_attribute]; 117 | } 118 | 119 | - (NSArray *)makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block { 120 | return [self mas_makeConstraints:block]; 121 | } 122 | 123 | - (NSArray *)updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block { 124 | return [self mas_updateConstraints:block]; 125 | } 126 | 127 | - (NSArray *)remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block { 128 | return [self mas_remakeConstraints:block]; 129 | } 130 | 131 | @end 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/ViewController+MASAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+MASAdditions.h 3 | // Masonry 4 | // 5 | // Created by Craig Siemens on 2015-06-23. 6 | // 7 | // 8 | 9 | #import "MASUtilities.h" 10 | #import "MASConstraintMaker.h" 11 | #import "MASViewAttribute.h" 12 | 13 | #ifdef MAS_VIEW_CONTROLLER 14 | 15 | @interface MAS_VIEW_CONTROLLER (MASAdditions) 16 | 17 | /** 18 | * following properties return a new MASViewAttribute with appropriate UILayoutGuide and NSLayoutAttribute 19 | */ 20 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuide; 21 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuide; 22 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuideTop; 23 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_topLayoutGuideBottom; 24 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuideTop; 25 | @property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomLayoutGuideBottom; 26 | 27 | 28 | @end 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /Pods/Masonry/Masonry/ViewController+MASAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+MASAdditions.m 3 | // Masonry 4 | // 5 | // Created by Craig Siemens on 2015-06-23. 6 | // 7 | // 8 | 9 | #import "ViewController+MASAdditions.h" 10 | 11 | #ifdef MAS_VIEW_CONTROLLER 12 | 13 | @implementation MAS_VIEW_CONTROLLER (MASAdditions) 14 | 15 | - (MASViewAttribute *)mas_topLayoutGuide { 16 | return [[MASViewAttribute alloc] initWithView:self.view item:self.topLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; 17 | } 18 | - (MASViewAttribute *)mas_topLayoutGuideTop { 19 | return [[MASViewAttribute alloc] initWithView:self.view item:self.topLayoutGuide layoutAttribute:NSLayoutAttributeTop]; 20 | } 21 | - (MASViewAttribute *)mas_topLayoutGuideBottom { 22 | return [[MASViewAttribute alloc] initWithView:self.view item:self.topLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; 23 | } 24 | 25 | - (MASViewAttribute *)mas_bottomLayoutGuide { 26 | return [[MASViewAttribute alloc] initWithView:self.view item:self.bottomLayoutGuide layoutAttribute:NSLayoutAttributeTop]; 27 | } 28 | - (MASViewAttribute *)mas_bottomLayoutGuideTop { 29 | return [[MASViewAttribute alloc] initWithView:self.view item:self.bottomLayoutGuide layoutAttribute:NSLayoutAttributeTop]; 30 | } 31 | - (MASViewAttribute *)mas_bottomLayoutGuideBottom { 32 | return [[MASViewAttribute alloc] initWithView:self.view item:self.bottomLayoutGuide layoutAttribute:NSLayoutAttributeBottom]; 33 | } 34 | 35 | 36 | 37 | @end 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /Pods/Masonry/README.md: -------------------------------------------------------------------------------- 1 | # Masonry [![Build Status](https://travis-ci.org/SnapKit/Masonry.svg?branch=master)](https://travis-ci.org/SnapKit/Masonry) [![Coverage Status](https://img.shields.io/coveralls/SnapKit/Masonry.svg?style=flat-square)](https://coveralls.io/r/SnapKit/Masonry) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) ![Pod Version](https://img.shields.io/cocoapods/v/Masonry.svg?style=flat) 2 | 3 | **Masonry is still actively maintained, we are committed to fixing bugs and merging good quality PRs from the wider community. However if you're using Swift in your project, we recommend using [SnapKit](https://github.com/SnapKit/SnapKit) as it provides better type safety with a simpler API.** 4 | 5 | Masonry is a light-weight layout framework which wraps AutoLayout with a nicer syntax. Masonry has its own layout DSL which provides a chainable way of describing your NSLayoutConstraints which results in layout code that is more concise and readable. 6 | Masonry supports iOS and Mac OS X. 7 | 8 | For examples take a look at the **Masonry iOS Examples** project in the Masonry workspace. You will need to run `pod install` after downloading. 9 | 10 | ## What's wrong with NSLayoutConstraints? 11 | 12 | Under the hood Auto Layout is a powerful and flexible way of organising and laying out your views. However creating constraints from code is verbose and not very descriptive. 13 | Imagine a simple example in which you want to have a view fill its superview but inset by 10 pixels on every side 14 | ```obj-c 15 | UIView *superview = self.view; 16 | 17 | UIView *view1 = [[UIView alloc] init]; 18 | view1.translatesAutoresizingMaskIntoConstraints = NO; 19 | view1.backgroundColor = [UIColor greenColor]; 20 | [superview addSubview:view1]; 21 | 22 | UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10); 23 | 24 | [superview addConstraints:@[ 25 | 26 | //view1 constraints 27 | [NSLayoutConstraint constraintWithItem:view1 28 | attribute:NSLayoutAttributeTop 29 | relatedBy:NSLayoutRelationEqual 30 | toItem:superview 31 | attribute:NSLayoutAttributeTop 32 | multiplier:1.0 33 | constant:padding.top], 34 | 35 | [NSLayoutConstraint constraintWithItem:view1 36 | attribute:NSLayoutAttributeLeft 37 | relatedBy:NSLayoutRelationEqual 38 | toItem:superview 39 | attribute:NSLayoutAttributeLeft 40 | multiplier:1.0 41 | constant:padding.left], 42 | 43 | [NSLayoutConstraint constraintWithItem:view1 44 | attribute:NSLayoutAttributeBottom 45 | relatedBy:NSLayoutRelationEqual 46 | toItem:superview 47 | attribute:NSLayoutAttributeBottom 48 | multiplier:1.0 49 | constant:-padding.bottom], 50 | 51 | [NSLayoutConstraint constraintWithItem:view1 52 | attribute:NSLayoutAttributeRight 53 | relatedBy:NSLayoutRelationEqual 54 | toItem:superview 55 | attribute:NSLayoutAttributeRight 56 | multiplier:1 57 | constant:-padding.right], 58 | 59 | ]]; 60 | ``` 61 | Even with such a simple example the code needed is quite verbose and quickly becomes unreadable when you have more than 2 or 3 views. 62 | Another option is to use Visual Format Language (VFL), which is a bit less long winded. 63 | However the ASCII type syntax has its own pitfalls and its also a bit harder to animate as `NSLayoutConstraint constraintsWithVisualFormat:` returns an array. 64 | 65 | ## Prepare to meet your Maker! 66 | 67 | Heres the same constraints created using MASConstraintMaker 68 | 69 | ```obj-c 70 | UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10); 71 | 72 | [view1 mas_makeConstraints:^(MASConstraintMaker *make) { 73 | make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler 74 | make.left.equalTo(superview.mas_left).with.offset(padding.left); 75 | make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom); 76 | make.right.equalTo(superview.mas_right).with.offset(-padding.right); 77 | }]; 78 | ``` 79 | Or even shorter 80 | 81 | ```obj-c 82 | [view1 mas_makeConstraints:^(MASConstraintMaker *make) { 83 | make.edges.equalTo(superview).with.insets(padding); 84 | }]; 85 | ``` 86 | 87 | Also note in the first example we had to add the constraints to the superview `[superview addConstraints:...`. 88 | Masonry however will automagically add constraints to the appropriate view. 89 | 90 | Masonry will also call `view1.translatesAutoresizingMaskIntoConstraints = NO;` for you. 91 | 92 | ## Not all things are created equal 93 | 94 | > `.equalTo` equivalent to **NSLayoutRelationEqual** 95 | 96 | > `.lessThanOrEqualTo` equivalent to **NSLayoutRelationLessThanOrEqual** 97 | 98 | > `.greaterThanOrEqualTo` equivalent to **NSLayoutRelationGreaterThanOrEqual** 99 | 100 | These three equality constraints accept one argument which can be any of the following: 101 | 102 | #### 1. MASViewAttribute 103 | 104 | ```obj-c 105 | make.centerX.lessThanOrEqualTo(view2.mas_left); 106 | ``` 107 | 108 | MASViewAttribute | NSLayoutAttribute 109 | ------------------------- | -------------------------- 110 | view.mas_left | NSLayoutAttributeLeft 111 | view.mas_right | NSLayoutAttributeRight 112 | view.mas_top | NSLayoutAttributeTop 113 | view.mas_bottom | NSLayoutAttributeBottom 114 | view.mas_leading | NSLayoutAttributeLeading 115 | view.mas_trailing | NSLayoutAttributeTrailing 116 | view.mas_width | NSLayoutAttributeWidth 117 | view.mas_height | NSLayoutAttributeHeight 118 | view.mas_centerX | NSLayoutAttributeCenterX 119 | view.mas_centerY | NSLayoutAttributeCenterY 120 | view.mas_baseline | NSLayoutAttributeBaseline 121 | 122 | #### 2. UIView/NSView 123 | 124 | if you want view.left to be greater than or equal to label.left : 125 | ```obj-c 126 | //these two constraints are exactly the same 127 | make.left.greaterThanOrEqualTo(label); 128 | make.left.greaterThanOrEqualTo(label.mas_left); 129 | ``` 130 | 131 | #### 3. NSNumber 132 | 133 | Auto Layout allows width and height to be set to constant values. 134 | if you want to set view to have a minimum and maximum width you could pass a number to the equality blocks: 135 | ```obj-c 136 | //width >= 200 && width <= 400 137 | make.width.greaterThanOrEqualTo(@200); 138 | make.width.lessThanOrEqualTo(@400) 139 | ``` 140 | 141 | However Auto Layout does not allow alignment attributes such as left, right, centerY etc to be set to constant values. 142 | So if you pass a NSNumber for these attributes Masonry will turn these into constraints relative to the view’s superview ie: 143 | ```obj-c 144 | //creates view.left = view.superview.left + 10 145 | make.left.lessThanOrEqualTo(@10) 146 | ``` 147 | 148 | Instead of using NSNumber, you can use primitives and structs to build your constraints, like so: 149 | ```obj-c 150 | make.top.mas_equalTo(42); 151 | make.height.mas_equalTo(20); 152 | make.size.mas_equalTo(CGSizeMake(50, 100)); 153 | make.edges.mas_equalTo(UIEdgeInsetsMake(10, 0, 10, 0)); 154 | make.left.mas_equalTo(view).mas_offset(UIEdgeInsetsMake(10, 0, 10, 0)); 155 | ``` 156 | 157 | By default, macros which support [autoboxing](https://en.wikipedia.org/wiki/Autoboxing#Autoboxing) are prefixed with `mas_`. Unprefixed versions are available by defining `MAS_SHORTHAND_GLOBALS` before importing Masonry. 158 | 159 | #### 4. NSArray 160 | 161 | An array of a mixture of any of the previous types 162 | ```obj-c 163 | make.height.equalTo(@[view1.mas_height, view2.mas_height]); 164 | make.height.equalTo(@[view1, view2]); 165 | make.left.equalTo(@[view1, @100, view3.right]); 166 | ```` 167 | 168 | ## Learn to prioritize 169 | 170 | > `.priority` allows you to specify an exact priority 171 | 172 | > `.priorityHigh` equivalent to **UILayoutPriorityDefaultHigh** 173 | 174 | > `.priorityMedium` is half way between high and low 175 | 176 | > `.priorityLow` equivalent to **UILayoutPriorityDefaultLow** 177 | 178 | Priorities are can be tacked on to the end of a constraint chain like so: 179 | ```obj-c 180 | make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow(); 181 | 182 | make.top.equalTo(label.mas_top).with.priority(600); 183 | ``` 184 | 185 | ## Composition, composition, composition 186 | 187 | Masonry also gives you a few convenience methods which create multiple constraints at the same time. These are called MASCompositeConstraints 188 | 189 | #### edges 190 | 191 | ```obj-c 192 | // make top, left, bottom, right equal view2 193 | make.edges.equalTo(view2); 194 | 195 | // make top = superview.top + 5, left = superview.left + 10, 196 | // bottom = superview.bottom - 15, right = superview.right - 20 197 | make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20)) 198 | ``` 199 | 200 | #### size 201 | 202 | ```obj-c 203 | // make width and height greater than or equal to titleLabel 204 | make.size.greaterThanOrEqualTo(titleLabel) 205 | 206 | // make width = superview.width + 100, height = superview.height - 50 207 | make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50)) 208 | ``` 209 | 210 | #### center 211 | ```obj-c 212 | // make centerX and centerY = button1 213 | make.center.equalTo(button1) 214 | 215 | // make centerX = superview.centerX - 5, centerY = superview.centerY + 10 216 | make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10)) 217 | ``` 218 | 219 | You can chain view attributes for increased readability: 220 | 221 | ```obj-c 222 | // All edges but the top should equal those of the superview 223 | make.left.right.and.bottom.equalTo(superview); 224 | make.top.equalTo(otherView); 225 | ``` 226 | 227 | ## Hold on for dear life 228 | 229 | Sometimes you need modify existing constraints in order to animate or remove/replace constraints. 230 | In Masonry there are a few different approaches to updating constraints. 231 | 232 | #### 1. References 233 | You can hold on to a reference of a particular constraint by assigning the result of a constraint make expression to a local variable or a class property. 234 | You could also reference multiple constraints by storing them away in an array. 235 | 236 | ```obj-c 237 | // in public/private interface 238 | @property (nonatomic, strong) MASConstraint *topConstraint; 239 | 240 | ... 241 | 242 | // when making constraints 243 | [view1 mas_makeConstraints:^(MASConstraintMaker *make) { 244 | self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top); 245 | make.left.equalTo(superview.mas_left).with.offset(padding.left); 246 | }]; 247 | 248 | ... 249 | // then later you can call 250 | [self.topConstraint uninstall]; 251 | ``` 252 | 253 | #### 2. mas_updateConstraints 254 | Alternatively if you are only updating the constant value of the constraint you can use the convience method `mas_updateConstraints` instead of `mas_makeConstraints` 255 | 256 | ```obj-c 257 | // this is Apple's recommended place for adding/updating constraints 258 | // this method can get called multiple times in response to setNeedsUpdateConstraints 259 | // which can be called by UIKit internally or in your code if you need to trigger an update to your constraints 260 | - (void)updateConstraints { 261 | [self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) { 262 | make.center.equalTo(self); 263 | make.width.equalTo(@(self.buttonSize.width)).priorityLow(); 264 | make.height.equalTo(@(self.buttonSize.height)).priorityLow(); 265 | make.width.lessThanOrEqualTo(self); 266 | make.height.lessThanOrEqualTo(self); 267 | }]; 268 | 269 | //according to apple super should be called at end of method 270 | [super updateConstraints]; 271 | } 272 | ``` 273 | 274 | ### 3. mas_remakeConstraints 275 | `mas_updateConstraints` is useful for updating a set of constraints, but doing anything beyond updating constant values can get exhausting. That's where `mas_remakeConstraints` comes in. 276 | 277 | `mas_remakeConstraints` is similar to `mas_updateConstraints`, but instead of updating constant values, it will remove all of its constraints before installing them again. This lets you provide different constraints without having to keep around references to ones which you want to remove. 278 | 279 | ```obj-c 280 | - (void)changeButtonPosition { 281 | [self.button mas_remakeConstraints:^(MASConstraintMaker *make) { 282 | make.size.equalTo(self.buttonSize); 283 | 284 | if (topLeft) { 285 | make.top.and.left.offset(10); 286 | } else { 287 | make.bottom.and.right.offset(-10); 288 | } 289 | }]; 290 | } 291 | ``` 292 | 293 | You can find more detailed examples of all three approaches in the **Masonry iOS Examples** project. 294 | 295 | ## When the ^&*!@ hits the fan! 296 | 297 | Laying out your views doesn't always goto plan. So when things literally go pear shaped, you don't want to be looking at console output like this: 298 | 299 | ```obj-c 300 | Unable to simultaneously satisfy constraints.....blah blah blah.... 301 | ( 302 | "=5000)]>", 303 | "", 304 | "", 305 | "" 306 | ) 307 | 308 | Will attempt to recover by breaking constraint 309 | =5000)]> 310 | ``` 311 | 312 | Masonry adds a category to NSLayoutConstraint which overrides the default implementation of `- (NSString *)description`. 313 | Now you can give meaningful names to views and constraints, and also easily pick out the constraints created by Masonry. 314 | 315 | which means your console output can now look like this: 316 | 317 | ```obj-c 318 | Unable to simultaneously satisfy constraints......blah blah blah.... 319 | ( 320 | "", 321 | "= 5000>", 322 | "", 323 | "" 324 | ) 325 | 326 | Will attempt to recover by breaking constraint 327 | = 5000> 328 | ``` 329 | 330 | For an example of how to set this up take a look at the **Masonry iOS Examples** project in the Masonry workspace. 331 | 332 | ## Where should I create my constraints? 333 | 334 | ```objc 335 | @implementation DIYCustomView 336 | 337 | - (id)init { 338 | self = [super init]; 339 | if (!self) return nil; 340 | 341 | // --- Create your views here --- 342 | self.button = [[UIButton alloc] init]; 343 | 344 | return self; 345 | } 346 | 347 | // tell UIKit that you are using AutoLayout 348 | + (BOOL)requiresConstraintBasedLayout { 349 | return YES; 350 | } 351 | 352 | // this is Apple's recommended place for adding/updating constraints 353 | - (void)updateConstraints { 354 | 355 | // --- remake/update constraints here 356 | [self.button remakeConstraints:^(MASConstraintMaker *make) { 357 | make.width.equalTo(@(self.buttonSize.width)); 358 | make.height.equalTo(@(self.buttonSize.height)); 359 | }]; 360 | 361 | //according to apple super should be called at end of method 362 | [super updateConstraints]; 363 | } 364 | 365 | - (void)didTapButton:(UIButton *)button { 366 | // --- Do your changes ie change variables that affect your layout etc --- 367 | self.buttonSize = CGSize(200, 200); 368 | 369 | // tell constraints they need updating 370 | [self setNeedsUpdateConstraints]; 371 | } 372 | 373 | @end 374 | ``` 375 | 376 | ## Installation 377 | Use the [orsome](http://www.youtube.com/watch?v=YaIZF8uUTtk) [CocoaPods](http://github.com/CocoaPods/CocoaPods). 378 | 379 | In your Podfile 380 | >`pod 'Masonry'` 381 | 382 | If you want to use masonry without all those pesky 'mas_' prefixes. Add #define MAS_SHORTHAND to your prefix.pch before importing Masonry 383 | >`#define MAS_SHORTHAND` 384 | 385 | Get busy Masoning 386 | >`#import "Masonry.h"` 387 | 388 | ## Code Snippets 389 | 390 | Copy the included code snippets to ``~/Library/Developer/Xcode/UserData/CodeSnippets`` to write your masonry blocks at lightning speed! 391 | 392 | `mas_make` -> ` [<#view#> mas_makeConstraints:^(MASConstraintMaker *make) { 393 | <#code#> 394 | }];` 395 | 396 | `mas_update` -> ` [<#view#> mas_updateConstraints:^(MASConstraintMaker *make) { 397 | <#code#> 398 | }];` 399 | 400 | `mas_remake` -> ` [<#view#> mas_remakeConstraints:^(MASConstraintMaker *make) { 401 | <#code#> 402 | }];` 403 | 404 | ## Features 405 | * Not limited to subset of Auto Layout. Anything NSLayoutConstraint can do, Masonry can do too! 406 | * Great debug support, give your views and constraints meaningful names. 407 | * Constraints read like sentences. 408 | * No crazy macro magic. Masonry won't pollute the global namespace with macros. 409 | * Not string or dictionary based and hence you get compile time checking. 410 | 411 | ## TODO 412 | * Eye candy 413 | * Mac example project 414 | * More tests and examples 415 | 416 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Masonry/Masonry-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Masonry/Masonry-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Masonry : NSObject 3 | @end 4 | @implementation PodsDummy_Masonry 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Masonry/Masonry-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Masonry/Masonry-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "MASCompositeConstraint.h" 14 | #import "MASConstraint+Private.h" 15 | #import "MASConstraint.h" 16 | #import "MASConstraintMaker.h" 17 | #import "MASLayoutConstraint.h" 18 | #import "Masonry.h" 19 | #import "MASUtilities.h" 20 | #import "MASViewAttribute.h" 21 | #import "MASViewConstraint.h" 22 | #import "NSArray+MASAdditions.h" 23 | #import "NSArray+MASShorthandAdditions.h" 24 | #import "NSLayoutConstraint+MASDebugAdditions.h" 25 | #import "View+MASAdditions.h" 26 | #import "View+MASShorthandAdditions.h" 27 | #import "ViewController+MASAdditions.h" 28 | 29 | FOUNDATION_EXPORT double MasonryVersionNumber; 30 | FOUNDATION_EXPORT const unsigned char MasonryVersionString[]; 31 | 32 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Masonry/Masonry.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Masonry 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "UIKit" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Masonry 9 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Masonry/Masonry.modulemap: -------------------------------------------------------------------------------- 1 | framework module Masonry { 2 | umbrella header "Masonry-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Masonry/Masonry.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Masonry 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "UIKit" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Masonry 9 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Masonry 5 | 6 | Copyright (c) 2011-2012 Masonry Team - https://github.com/Masonry 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | Generated by CocoaPods - https://cocoapods.org 26 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2011-2012 Masonry Team - https://github.com/Masonry 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | License 37 | MIT 38 | Title 39 | Masonry 40 | Type 41 | PSGroupSpecifier 42 | 43 | 44 | FooterText 45 | Generated by CocoaPods - https://cocoapods.org 46 | Title 47 | 48 | Type 49 | PSGroupSpecifier 50 | 51 | 52 | StringsTable 53 | Acknowledgements 54 | Title 55 | Acknowledgements 56 | 57 | 58 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_DetectFakeLocation : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_DetectFakeLocation 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-frameworks-Debug-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-frameworks-Debug-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-frameworks-Release-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-frameworks-Release-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | BCSYMBOLMAP_DIR="BCSymbolMaps" 23 | 24 | 25 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 26 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 27 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 28 | 29 | # Copies and strips a vendored framework 30 | install_framework() 31 | { 32 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 33 | local source="${BUILT_PRODUCTS_DIR}/$1" 34 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 35 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 36 | elif [ -r "$1" ]; then 37 | local source="$1" 38 | fi 39 | 40 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 41 | 42 | if [ -L "${source}" ]; then 43 | echo "Symlinked..." 44 | source="$(readlink "${source}")" 45 | fi 46 | 47 | if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then 48 | # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied 49 | find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do 50 | echo "Installing $f" 51 | install_bcsymbolmap "$f" "$destination" 52 | rm "$f" 53 | done 54 | rmdir "${source}/${BCSYMBOLMAP_DIR}" 55 | fi 56 | 57 | # Use filter instead of exclude so missing patterns don't throw errors. 58 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 59 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 60 | 61 | local basename 62 | basename="$(basename -s .framework "$1")" 63 | binary="${destination}/${basename}.framework/${basename}" 64 | 65 | if ! [ -r "$binary" ]; then 66 | binary="${destination}/${basename}" 67 | elif [ -L "${binary}" ]; then 68 | echo "Destination binary is symlinked..." 69 | dirname="$(dirname "${binary}")" 70 | binary="${dirname}/$(readlink "${binary}")" 71 | fi 72 | 73 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 74 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 75 | strip_invalid_archs "$binary" 76 | fi 77 | 78 | # Resign the code if required by the build settings to avoid unstable apps 79 | code_sign_if_enabled "${destination}/$(basename "$1")" 80 | 81 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 82 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 83 | local swift_runtime_libs 84 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 85 | for lib in $swift_runtime_libs; do 86 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 87 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 88 | code_sign_if_enabled "${destination}/${lib}" 89 | done 90 | fi 91 | } 92 | # Copies and strips a vendored dSYM 93 | install_dsym() { 94 | local source="$1" 95 | warn_missing_arch=${2:-true} 96 | if [ -r "$source" ]; then 97 | # Copy the dSYM into the targets temp dir. 98 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 99 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 100 | 101 | local basename 102 | basename="$(basename -s .dSYM "$source")" 103 | binary_name="$(ls "$source/Contents/Resources/DWARF")" 104 | binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" 105 | 106 | # Strip invalid architectures from the dSYM. 107 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 108 | strip_invalid_archs "$binary" "$warn_missing_arch" 109 | fi 110 | if [[ $STRIP_BINARY_RETVAL == 0 ]]; then 111 | # Move the stripped file into its final destination. 112 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 113 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 114 | else 115 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 116 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" 117 | fi 118 | fi 119 | } 120 | 121 | # Used as a return value for each invocation of `strip_invalid_archs` function. 122 | STRIP_BINARY_RETVAL=0 123 | 124 | # Strip invalid architectures 125 | strip_invalid_archs() { 126 | binary="$1" 127 | warn_missing_arch=${2:-true} 128 | # Get architectures for current target binary 129 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 130 | # Intersect them with the architectures we are building for 131 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 132 | # If there are no archs supported by this binary then warn the user 133 | if [[ -z "$intersected_archs" ]]; then 134 | if [[ "$warn_missing_arch" == "true" ]]; then 135 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 136 | fi 137 | STRIP_BINARY_RETVAL=1 138 | return 139 | fi 140 | stripped="" 141 | for arch in $binary_archs; do 142 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 143 | # Strip non-valid architectures in-place 144 | lipo -remove "$arch" -output "$binary" "$binary" 145 | stripped="$stripped $arch" 146 | fi 147 | done 148 | if [[ "$stripped" ]]; then 149 | echo "Stripped $binary of architectures:$stripped" 150 | fi 151 | STRIP_BINARY_RETVAL=0 152 | } 153 | 154 | # Copies the bcsymbolmap files of a vendored framework 155 | install_bcsymbolmap() { 156 | local bcsymbolmap_path="$1" 157 | local destination="${BUILT_PRODUCTS_DIR}" 158 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 159 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 160 | } 161 | 162 | # Signs a framework with the provided identity 163 | code_sign_if_enabled() { 164 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 165 | # Use the current code_sign_identity 166 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 167 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 168 | 169 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 170 | code_sign_cmd="$code_sign_cmd &" 171 | fi 172 | echo "$code_sign_cmd" 173 | eval "$code_sign_cmd" 174 | fi 175 | } 176 | 177 | if [[ "$CONFIGURATION" == "Debug" ]]; then 178 | install_framework "${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework" 179 | fi 180 | if [[ "$CONFIGURATION" == "Release" ]]; then 181 | install_framework "${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework" 182 | fi 183 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 184 | wait 185 | fi 186 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_DetectFakeLocationVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_DetectFakeLocationVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "Masonry" -framework "UIKit" 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_DetectFakeLocation { 2 | umbrella header "Pods-DetectFakeLocation-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DetectFakeLocation/Pods-DetectFakeLocation.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "Masonry" -framework "UIKit" 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DetectFakeLocation 2 | iOS检测虚拟位置,比如使用Xcode模拟位置、使用爱思助手模拟位置等 3 | 4 | 5 | # iOS 虚拟定位原理与预防 6 | 7 | ## 背景 8 | 9 | 说到虚拟定位,常有印象都是安卓上的分身软件甚至系统自带的位置穿越(笔者曾经使用过ZUK Z2系统自带的位置穿越),会认为iOS上虚拟定位比较困难。笔者没调研之前也是这么认为,之前已知的虚拟定位是使用Xcode添加GPX文件,编辑经纬度,从而实现虚拟定位。但是这种操作也只有熟悉iOS开发的人才能操作,而且需要mac电脑,故而笔者印象中也是iOS上虚拟定位比较困难。 10 | 11 | 12 | 13 | 然而经过调研之后,笔者发现,iOS的虚拟定位没有那么困难,甚至相比于安卓更简单。下面就来介绍一下iOS中几种虚拟定位的方法。 14 | 15 | ## 虚拟定位的几种办法及原理 16 | 17 | 笔者调研后,发现iOS上面虚拟定位大致可有4中情况: 18 | - 使用Xcode通过GPX文件虚拟定位 19 | - 使用爱思助手中的虚拟定位功能直接虚拟定位 20 | - 通过外接设备,比如蓝牙和手机连接,发送虚拟定位数据来虚拟定位 21 | - 越狱设备中通过hook定位方法,来虚拟定位 22 | 23 | 下面就来一个个分析实践: 24 | 25 | ### 使用Xcode通过GPX文件虚拟定位 26 | 27 | 使用Xcode通过GPX文件虚拟定位,iOS开发一般比较熟悉,操作步骤是: 28 | 29 | 新创建一个iOS项目,然后添加文件,选择创建GPX文件 30 | 31 | ![wecom20210804-151237.png](https://inews.gtimg.com/newsapp_ls/0/13846925548/0.png) 32 | 33 | 34 | 编辑其中内容,把`lat`、`lon`改为要模拟的经纬度,如下: 35 | 36 | ``` Objective-C 37 | 38 | 39 | Shanghai 40 | 41 | 42 | 43 | ``` 44 | 45 | 然后选择`Product` -> `Scheme` -> `Edit Scheme`,选中`Options` tab,勾选`Allow Location Simulation`,然后运行即可虚拟定位 46 | 47 | ![wecom20210804-152202.png](https://inews.gtimg.com/newsapp_ls/0/13846939343/0.png) 48 | 49 | **注意:** 通常情况下,使用Xcode运行虚拟定位,运行停止后,经纬度会恢复成原来的。但是当运行了项目,然后直接拔掉数据线(是运行状态下拔掉),手机的经纬度就会一直是模拟的经纬度,想要恢复需要重启手机或者等待2~5天自动恢复。 50 | 51 | **原理:** 调用iOS设备中的`com.apple.dt.simulatelocation`服务,[`com.apple.dt.simulatelocation`服务是苹果在Xcode 6、iOS 8.0之后提供的为设备模拟GPS位置的调试功能,原理是通过usb获取设备句柄后开启设备内的服务("`com.apple.dt.simulatelocation`"),再通过固定坐标或GPX文件进行位置模拟。](https://zhuanlan.zhihu.com/p/46572309) 52 | 53 | ### 使用爱思助手中的虚拟定位功能直接虚拟定位 54 | 55 | 这个方法最是简单,笔者之前不知道有这种方法,下载一个爱思助手,打开,连接手机到电脑,选择`工具箱`tab下的`虚拟定位` 56 | 57 | ![wecom20210804-142320@2x.png](https://inews.gtimg.com/newsapp_ls/0/13846970127/0.png) 58 | 59 | 然后输入要定位的位置搜索,点击修改定位即可 60 | 61 | ![wecom20210804-142450@2x.png](https://inews.gtimg.com/newsapp_ls/0/13846970132/0.png) 62 | 63 | **注意:** 这种虚拟定位的方法,真的是简单。就目前而言,笔者尝试后发现`钉钉`、`企业微信`,均没有对此类方法检测处理。 64 | 65 | **原理:** 通过[libimobiledevice](https://github.com/libimobiledevice/libimobiledevice)的service模块开启com.apple.dt.simulatelocation服务,然后实现脱离通过Xcode来模拟定位。[libimobiledevice](https://github.com/libimobiledevice/libimobiledevice)是开源的跨平台调用iOS协议的库。 66 | 67 | ### 通过外接设备,发送虚拟定位数据来虚拟定位 68 | 69 | 通过外接设备,发送虚拟定位这个方法笔者之前完全都没了解到,不得不说,真的是双击666,人民智慧无限强大,其中代表是位移精灵。笔者没有购买外设,所以也没办法尝试,但是可以附上一个视频,供大家参考: 70 | 71 | 视频链接:https://haokan.baidu.com/v?pd=wisenatural&vid=17675944846390412165 72 | 73 | 74 | **原理:** 通过MFi(Made For iOS)认证厂商,可以获得MFI Accessory Interface Specification文档,其中提供了很多隐藏功能,包含时间通讯、音乐数据通讯、定位功能等等。其中定位功能的使用只需要照着文档,按照协议格式发送对应位置数据,即可。[MFI Accessory Interface Specification](https://usermanual.wiki/Document/MFiAccessoryInterfaceSpecificationR18NoRestriction.1631705096.pdf)地址见:https://usermanual.wiki/Document/MFiAccessoryInterfaceSpecificationR18NoRestriction.1631705096.pdf 75 | 76 | 文档中搜索`Locaiton`即可看到定位相关信息,如下: 77 | 78 | ![wecom20210804-165553.png](https://inews.gtimg.com/newsapp_ls/0/13847282969/0.png) 79 | 80 | 81 | ### 越狱设备中通过hook定位方法,来虚拟定位 82 | 83 | 越狱设备虚拟定位,是越狱之后使用具备虚拟定位功能的越狱插件。在上帝模式下,越狱插件可以随意劫持系统函数。比如:[GPS定位管家,能够管理每个iOS应用的GPS位置。](http://article.docway.net/details?id=60d5e03bcbb464046e3508b6)。 84 | 85 | **原理:** 越狱后,通过注入库,hook了CLLocationManager中的定位代理方法,从而篡改正常定位信息。 86 | 87 | 88 | 总结如下: 89 | 90 | ![wecom20210804-171650.png](https://inews.gtimg.com/newsapp_ls/0/13847379681/0.png) 91 | 92 | ## 虚拟定位几种方法的检测 93 | 94 | 作为开发,知道了有哪些虚拟定位的,还不够,还需要知道怎么这些虚拟定位的方法怎么解决。尤其是OA应用和游戏应用的开发,需要特别注意。下面就来一步步看下: 95 | 96 | ### 使用Xcode通过GPX文件虚拟定位的检测 和 使用爱思助手中的虚拟定位功能直接虚拟定位的检测 97 | 98 | 使用Xcode通过GPX虚拟定位和使用爱思助手虚拟定位的,其最终的原理是一样的,都是通过调用com.apple.simulatelocation服务,从而实现虚拟定位。 99 | 100 | 笔者统计了一下,网上说的验证方式大致有两种: 101 | - 根据特征值判断 102 | - 定位的精度:虚拟定位的经纬度的精度不如真实定位的精度高,所以可以通过定位经纬度的精度来判断 103 | - 定位的海拔值和海拔精度:虚拟定位的海拔值为0,海拔垂直精度为-1;所以可以通过这两个值来判断 104 | - 定位回调调用次数:虚拟定位的回调只会调用一次,而真实定位的会多次触发;所以可以通过触发次数来判断 105 | - 函数响应时间:虚拟定位的响应是立马返回,而真实的不会;所以可以通过响应时间判断 106 | - 根据`CLLocation`的私有属性`_internal`中的type来判断 107 | 108 | 上面是笔者总结的网上给出的检测方案,下面来实践,看事实是否如上所描述,下面笔者采用的是使用爱思助手虚拟经纬度的方法来模拟。 109 | **强烈注意:**使用第三方地图的定位和系统定位回对下面对方法也有影响!!!笔者这里吃了很大的亏,好家伙。 110 | 111 | #### 根据特征值判断 112 | 113 | 1. 定位的精度 114 | 获取经纬度的精度,不能使用"%f"来直接格式化,因为格式化字符串默认“%f”,默认保留到小数点后第6位,对比不出来差异。 115 | 代码如下: 116 | 117 | ``` ObjectiveC 118 | /// 定位经纬度的精度 119 | /// @param location 定位Item 120 | - (void)testLocationAccuracy:(CLLocation *)location { 121 | NSString *longitudeStr = [NSString stringWithFormat:@"%@", 122 | @(location.coordinate.longitude)]; 123 | // NSString *longitudeStr = [[NSDecimalNumber numberWithDouble: 124 | location.coordinate.longitude] stringValue]; // 这种方法取到的是17位 125 | NSString *lastPartLongitudeStr = [[longitudeStr 126 | componentsSeparatedByString:@"."] lastObject]; 127 | 128 | NSString *latitudeStr = [NSString stringWithFormat:@"%@", @(location.coordinate.latitude)]; 129 | NSString *lastPartLatitudeStr = [[latitudeStr 130 | componentsSeparatedByString:@"."] lastObject]; 131 | 132 | NSLog(@"定位精度的 longitude位数:%d, latitude位数:%d", 133 | lastPartLongitudeStr.length, lastPartLatitudeStr.length); 134 | } 135 | ``` 136 | 137 | 使用正常定位时,结果如下: 138 | ``` ObjectiveC 139 | 定位精度的 longitude位数:13, latitude位数:14 140 | ``` 141 | 142 | 使用爱思助手搜索`虹桥火车站地铁站`,自动定位到经纬度是6位,输入框中最多可输入小数点后8位,开启虚拟定位后,结果如下: 143 | ``` ObjectiveC 144 | 定位精度的 longitude位数:13, latitude位数:14 145 | ``` 146 | 147 | 笔者这里测试了很久,由于小数精度的问题,笔者换了好几种方式,最终结论是使用此方法无法判断。虽然爱思助手设置的经纬度有个数限制,但是最终定位出来的经纬度和定位出来的并不相同,且由于小数精度问题,无法准确判断。故而此方法行不通。 148 | 149 | 150 | 2. 定位的海拔值和海拔精度 151 | 通过altitude和verticalAccuracy来判断,CLLocation的altitude的属性表示海拔。verticalAccuracy的属性来判断海拔的精确度。海拔数值可能会有verticalAccuracy大小的误差,当verticalAccuracy为负值时,表示不能获取海拔高度。 152 | 153 | 代码如下: 154 | 155 | ``` ObjectiveC 156 | /// 定位海拔、海拔垂直精度 157 | /// @param location 定位Item 158 | - (void)testLocationAltitudeAccuracy:(CLLocation *)location { 159 | NSLog(@"海拔高度:%f", location.altitude); 160 | NSLog(@"海拔垂直精度:%f", location.verticalAccuracy); 161 | } 162 | ``` 163 | 164 | 使用正常定位时,结果如下: 165 | 166 | ``` ObjectiveC 167 | 海拔高度:9.224902 168 | 海拔垂直精度:16.078646 169 | ``` 170 | 171 | 使用爱思助手开启定位后,结果如下: 172 | ``` ObjectiveC 173 | 海拔高度:0.000000 174 | 海拔垂直精度:-1.000000 175 | ``` 176 | 177 | 从上面可以看出,正常定位和模拟定位的海拔和海拔垂直精度是不同的,故而可以用来区分。但是真正的海拔高度为0的地方会不会被误杀,需要纳入考虑,待测试验证。 178 | 179 | 3. 定位回调调用次数 180 | 笔者在定位类中,声明一个回调次数的属性,在调用开始定位的方法中赋值为0,回调成功的方法中每次都加1,且打印出结果。 181 | 大致代码如下: 182 | 183 | ``` ObjectiveC 184 | 185 | @TestLocationManager() 186 | 187 | @property (nonatomic, assign) NSInteger callbackCount; 188 | 189 | @end 190 | 191 | @implementation TestLocationManager() 192 | 193 | - (void)startLocation { 194 | self.callbackCount = 0; 195 | } 196 | 197 | - (void)BMKLocationManager:(BMKLocationManager * _Nonnull)manager 198 | didUpdateLocation:(BMKLocation * _Nullable)location 199 | orError:(NSError * _Nullable)error 200 | { 201 | self.callbackCount += 1; 202 | NSLog(@"百度地图单次定位回调次数: %ld", self.callbackCount); 203 | } 204 | 205 | - (void)locationManager:(CLLocationManager *)manager 206 | didUpdateLocations:(NSArray *)locations { 207 | self.callbackCount += 1; 208 | NSLog(@"系统定位单次定位回调次数: %ld", self.callbackCount); 209 | } 210 | 211 | @end 212 | 213 | ``` 214 | 215 | 使用正常定位时,结果如下: 216 | ``` ObjectiveC 217 | 百度地图单次定位回调次数: 1 218 | 系统定位单次定位回调次数: 1 219 | 系统定位单次定位回调次数: 2 220 | ``` 221 | 222 | 使用爱思助手模拟定位时,结果如下: 223 | ``` ObjectiveC 224 | 百度地图单次定位回调次数: 1 225 | 系统定位单次定位回调次数: 1 226 | ``` 227 | 228 | 笔者这边测试出来的结果,使用第三方地图定位时虚拟定位和正常定位的回调次数没有区别,故而,使用第三方地图定位时此方法也行不通。使用系统定位时,虚拟定位和正常定位的回调次数不同,故而理论上使用系统定位时可以用此方法区分;但是使用这个判断的准确度并不高,因为系统定位偶尔也会只回调一次,而且,假如使用回调来判断,如何区分回调结束,是一个问题,比如笔者写了一个延时0.5s后,来查看回调次数;所以建议可以用作参考,但是不建议作为判断依据 229 | 230 | 4. 函数响应时间 231 | 笔者在定位类中,声明一个开始时间的属性,在开始调用定位的方法调用,然后定位结果的回调里取到结束时间,最后得出的时间差即是响应时间。 232 | 代码大致如下: 233 | 234 | ``` ObjectiveC 235 | 236 | @TestLocationManager() 237 | 238 | @property (nonatomic, strong) NSDate *locateBeginDate; 239 | 240 | @end 241 | 242 | @implementation TestLocationManager() 243 | 244 | - (void)startLocation { 245 | self.locateBeginDate = [NSDate date]; 246 | } 247 | 248 | - (void)BMKLocationManager:(BMKLocationManager * _Nonnull)manager 249 | didUpdateLocation:(BMKLocation * _Nullable)location 250 | orError:(NSError * _Nullable)error 251 | { 252 | NSDate *locateEndDate = [NSDate date]; 253 | NSTimeInterval gap = [locateEndDate timeIntervalSinceDate: 254 | self.locateBeginDate]; 255 | NSLog(@"单次定位时间:%lf", gap); 256 | } 257 | 258 | @end 259 | 260 | ``` 261 | 262 | 使用正常定位时,结果如下: 263 | ``` ObjectiveC 264 | 单次定位时间:0.332915 265 | ``` 266 | 267 | 使用爱思助手模拟定位时,结果如下: 268 | ``` ObjectiveC 269 | 单次定位时间:0.298709 270 | ``` 271 | 272 | 根据笔者测试出的结果,定位的间隔网络好的时候并没有明显差距,无法用固定的值区分,故而此方法也行不通。 273 | 274 | #### 根据`CLLocation`的私有属性`_internal`中的type来判断 275 | 276 | 参考[iOS防黑产虚假定位检测技术](https://cloud.tencent.com/developer/article/1800531),按照文章描述,定位对象`CLLocation`中有一个私有属性`type`,在不同定位来源的情况下,值是不同的。 277 | 278 | | 值 | 所表示的意义 | 备注 | 279 | | ---- | ---- | ---- | 280 | | 0 | unknown | 应用程序生成的定位数据,一般在越狱设备下,通过虚拟定位程序来生成。 | 281 | | 1 | gps | GPS生成的定位数据 | 282 | | 2 | nmea | | 283 | | 3 | accessory | 蓝牙等外部设备模拟定位生成的定位数据 | 284 | | 4 | wifi | WIFI定位生成的数据 | 285 | | 5 | skyhook | WIFI定位生成的数据 | 286 | | 6 | cell | 手机基站定位生成的数据 | 287 | | 7 | lac | LAC生成的定位数据 | 288 | | 8 | mcc | | 289 | | 9 | gpscoarse | | 290 | | 10 | pipeline | | 291 | | 11 | max | | 292 | 293 | 笔者验证步骤如下: 294 | 在定位成功的回调中,判断`CLLocation`的type属性。 295 | 296 | ``` ObjectiveC 297 | 298 | - (void)testLocationIntervalProperty:(CLLocation *)location { 299 | NSString *type = [location valueForKey:@"type"]; 300 | NSLog(@"定位来源类型:%@", type); 301 | return; 302 | // 如果想要看location的全部属性,可以通过下面的方法 303 | unsigned int intervalCount; 304 | objc_property_t *properties = class_copyPropertyList([location class], 305 | &intervalCount); 306 | for (unsigned int y = 0; y < intervalCount; y++) { 307 | objc_property_t property = properties[y]; 308 | NSString *propertyName = [[NSString alloc] initWithCString: 309 | property_getName(property)]; 310 | if ([propertyName containsString:@"type"]) { 311 | id propetyValue = [location valueForKey:propertyName]; 312 | 313 | NSLog(@"属性名称:%@, 属性信息:%@", propertyName, propetyValue); 314 | } 315 | else { 316 | // NSLog(@"属性名称:%@", propertyName); 317 | } 318 | } 319 | } 320 | 321 | ``` 322 | 323 | 正常定位时,Wi-Fi打开时,结果为4;Wi-Fi关闭时,结果为6;结果如下: 324 | 325 | ``` ObjectiveC 326 | // Wi-Fi打开时,结果为4;Wi-Fi关闭时,结果为6;移动网络也关闭时,结果为6; 327 | // 但是网络不好时,结果为1; 328 | 定位来源类型:4 329 | ``` 330 | 331 | 使用虚拟定位时,结果如下: 332 | 333 | ``` ObjectiveC 334 | 定位来源类型:1 335 | ``` 336 | 337 | 但是,当使用第三方地图定位时,不论是虚拟定位,还是正常定位,结果如下: 338 | 339 | ``` ObjectiveC 340 | 定位来源类型:0 341 | ``` 342 | 343 | 笔者这边对比结果如下:使用系统定位时,正常网络下虚拟定位和正常定位结果不同,但是网络不好时,定位来源类型都是1,故而区分不准确;使用第三方系统定位时,虚拟定位和正常定位结果相同,无法区分。 344 | 345 | 346 | ### 通过外接设备,发送虚拟定位数据来虚拟定位的检测 347 | 348 | 这种虚拟定位的笔者没有设备实践,但通过网上的资料,可以看出外接设备是通过蓝牙和手机连接,故而笔者推测,也可以根据`CLLocation`的私有属性`_internal`中的type来判断。因为type=3的定义是蓝牙等外部设备模拟定位生成的定位数据,所以这种虚拟定位应该可以通过此type值区分。 349 | 350 | 351 | ### 越狱设备中通过hook定位方法,来虚拟定位的检测 352 | 353 | 这种方法,笔者目前调研到的有两种,一种是直接针对越狱设备,判断出iPhone已越狱,就提示,从而避免;另外一种是判断代理方法是否被hook,以及代理方法被hook的实现是否在APP中。 354 | 355 | 方法一:判断设备是否已越狱,参考[用代码判断iOS 系统是否越狱的方法](https://www.huaweicloud.com/articles/7c6b8027253c4a97196d359840f638d9.html) 356 | 357 | 判断设备已越狱有下面几种方法 358 | 1. 判断常见越狱文件,维护一份常见越狱文件,判断其中一个存在,则说明已越狱 359 | 360 | ``` Swift 361 | 362 | /// 根据白名单判断设备是否已越狱 363 | /// - Returns: true-已越狱;false-未越狱 364 | class func isJailedDevice() -> Bool { 365 | let jailPaths = ["/Applications/Cydia.app", 366 | "/Library/MobileSubstrate/MobileSubstrate.dylib", 367 | "/bin/bash", 368 | "/usr/sbin/sshd", 369 | "/etc/apt"] 370 | var isJailDevice = false 371 | for item in jailPaths { 372 | if FileManager.default.fileExists(atPath: item) { 373 | isJailDevice = true 374 | break 375 | } 376 | } 377 | return isJailDevice 378 | } 379 | 380 | ``` 381 | 382 | 2. 判断cydia的URL Scheme,通过识别手机是否安装了Cydia.app,来判断是否已越狱。 383 | 384 | ``` Swift 385 | 386 | /// 根据cydia scheme能否打开判断是否已越狱 387 | /// - Returns: true-已越狱;false-未越狱 388 | class func isJailedDevice() -> Bool { 389 | let cydiaSchemeStr = "cydia://" 390 | if let url = URL(string: cydiaSchemeStr), 391 | UIApplication.shared.canOpenURL(url) { 392 | return true 393 | } 394 | else { 395 | return false 396 | } 397 | } 398 | 399 | ``` 400 | 3. 根据能否读取系统所有应用来判断,已越狱的设备可以读取,未越狱的设备不可以 401 | 402 | ``` Swift 403 | /// 根据能否获取到所有应用来判断是否越狱 404 | /// - Returns: true-已越狱;false-未越狱 405 | class func isJailedDevice() -> Bool { 406 | let appPathStr = "/User/Applications" 407 | if FileManager.default.fileExists(atPath: appPathStr) { 408 | do { 409 | let appList = 410 | try FileManager.default.contentsOfDirectory(atPath: 411 | appPathStr) 412 | if appList.count > 0 { 413 | return true 414 | } 415 | else { 416 | return false 417 | } 418 | } catch { 419 | return false 420 | } 421 | } 422 | else { 423 | return false 424 | } 425 | } 426 | ``` 427 | 428 | 方法二判断代理方法是否被hook,以及hook的实现是否在app中,参考[iOS上虚拟定位检测的探究](http://devliubo.com/2016/2016-12-23-iOS%E4%B8%8A%E8%99%9A%E6%8B%9F%E5%AE%9A%E4%BD%8D%E6%A3%80%E6%B5%8B%E7%9A%84%E6%8E%A2%E7%A9%B6.html) 429 | 430 | 注入dylib方法的实现虚拟定位,大部分会利用MethodSwizzling去hook定位方法的目标函数,method被替换的新implemention所在的module,不会与原始implemention所在的module一致。越狱插件的方式新implemention所在的module通常是插件本身的dylib;对ipa砸壳做动态库注入的方式,新implemention所在的module通常是被注入 的dylib。——来自http://devliubo.com/2016/2016-12-23-iOS%E4%B8%8A%E8%99%9A%E6%8B%9F%E5%AE%9A%E4%BD%8D%E6%A3%80%E6%B5%8B%E7%9A%84%E6%8E%A2%E7%A9%B6.html 431 | 432 | 实践: 433 | 434 | 代码如下: 435 | 436 | ``` ObjectiveC 437 | 438 | #import 439 | #import 440 | 441 | static void logMethodInfo(const char *className, const char *sel) 442 | { 443 | Dl_info info; 444 | IMP imp = class_getMethodImplementation(objc_getClass(className), 445 | sel_registerName(sel)); 446 | if(dladdr(imp,&info)) { 447 | NSLog(@"method %s %s:", className, sel); 448 | NSLog(@"dli_fname:%s",info.dli_fname); 449 | NSLog(@"dli_sname:%s",info.dli_sname); 450 | NSLog(@"dli_fbase:%p",info.dli_fbase); 451 | NSLog(@"dli_saddr:%p",info.dli_saddr); 452 | } else { 453 | NSLog(@"error: can't find that symbol."); 454 | } 455 | } 456 | 457 | ``` 458 | 459 | 比如,笔者这里验证使用如下,笔者的项目中对UIView的`layoutSubviews`做了`MethodSwizzling`,而`viewDidLayoutSubviews`方法没有,对比如下: 460 | 461 | ``` ObjectiveC 462 | 463 | - (void)testViewDidLayoutSubviews { 464 | const char *className = object_getClassName([UIView new]); 465 | SEL selector = @selector(viewDidLayoutSubviews); 466 | const char *selName = sel_getName(selector); 467 | logMethodInfo(className, selName); 468 | } 469 | 470 | - (void)testLayoutSubviews { 471 | const char *className = object_getClassName([UIView new]); 472 | SEL selector = @selector(layoutSubviews); 473 | const char *selName = sel_getName(selector); 474 | logMethodInfo(className, selName); 475 | } 476 | 477 | ``` 478 | 479 | 结果对比: 480 | 481 | ![wecom20210805-085332@2x.png](https://inews.gtimg.com/newsapp_ls/0/13849141373/0.png) 482 | 483 | ![wecom20210805-103558@2x.png](https://inews.gtimg.com/newsapp_ls/0/13849414536/0.png) 484 | 485 | 从上面的结果对比,发现未使用`MethodSwizzling`方法的`dli_fname`为`/usr/lib/libobjc.A.dylib`,`dli_sname` 486 | 为`_objc_msgForward`;而使用了`MethodSwizzling`方法的`dli_fname`为`/private/var/containers/Bundle/Application/0106942C-7D3F-45A9-BB1B-2C0FBD994744/xxx.app/xxx`,`dli_sname`为`-[UIView(MSCorner)ms_layoutSubviews]`,可以看出,从`dli_sname`可以判断方法是否被hook过,从`dli_fname`可以知道方法的implemention是否在项目的module中。(笔者手头没有越狱的手机,也没有做过砸壳注入,大家如果有兴趣可以验证一下试试。) 487 | 488 | ## 总结 489 | 490 | 笔者验证的结果,通过特征值中的海拔和海拔精度可以判断出使用爱思助手或者 Xcode 模拟定位,通过 type 可以区分不同定位来源。为了准确,笔者通过海拔、海拔精度、type 三个字段结合起来判断。 491 | 492 | 笔者写了一个检测的代码,仓库地址如下:https://github.com/mokong/DetectFakeLocation 493 | 494 | 原理是:使用`swizzlemethod` hook了`CLLocationManager`的`startUpdatingLocation`方法,以及`CLLocationManagerDelegate`的`locationManager:didUpdateLocations:`方法,然后检测越狱、海拔和海拔精度、定位类型,根据这几个方面判断是否是虚拟定位。 495 | 496 | ![ios_虚拟定位的方法.png](https://inews.gtimg.com/newsapp_ls/0/13851091768/0.png) 497 | 498 | 499 | ## 参考 500 | 501 | - [iOS 虚拟定位监测](http://article.docway.net/details?id=60d5e03bcbb464046e3508b6) 502 | - [苹果虚拟定位技术原理和检测](https://www.jianshu.com/p/7c213781c4b8) 503 | - [免越狱虚拟定位外挂的调试小记与检测方案 | B1nGzL 著](https://zhuanlan.zhihu.com/p/46572309) 504 | - [iOS防黑产虚假定位检测技术](https://cloud.tencent.com/developer/article/1800531) 505 | - [iOS实现虚拟定位的多种玩法](https://juejin.cn/post/6844903975624376333) 506 | - [iOS 识别虚拟定位调研](https://juejin.cn/post/6982907103631376420) 507 | - [iOS上虚拟定位检测的探究](http://devliubo.com/2016/2016-12-23-iOS%E4%B8%8A%E8%99%9A%E6%8B%9F%E5%AE%9A%E4%BD%8D%E6%A3%80%E6%B5%8B%E7%9A%84%E6%8E%A2%E7%A9%B6.html) 508 | --------------------------------------------------------------------------------