├── .gitignore ├── Demo ├── ConstraintDebug.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcuserdata │ │ └── zhangweike.xcuserdatad │ │ └── xcschemes │ │ ├── ConstraintDebug.xcscheme │ │ └── xcschememanagement.plist ├── ConstraintDebug.xcworkspace │ └── contents.xcworkspacedata ├── ConstraintDebug │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── MainViewController.h │ ├── MainViewController.m │ └── main.m └── Podfile ├── LICENSE ├── README.md ├── ZIKConstraintsGuard.podspec └── ZIKConstraintsGuard ├── UIView+ZIKDebug.h ├── UIView+ZIKDebug.m ├── ZIKConstraintsGuard.h ├── ZIKConstraintsGuard.m ├── ZIKMethodSwizzler.h └── ZIKMethodSwizzler.m /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Note: if you ignore the Pods directory, make sure to uncomment 31 | # `pod install` in .travis.yml 32 | # 33 | # Pods/ 34 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4896C895371F41E1EC9A4756 /* libPods-ConstraintDebug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 603622CD1F52C70B50A9DECE /* libPods-ConstraintDebug.a */; }; 11 | F8529E721DF9781A0043F94D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F8529E711DF9781A0043F94D /* main.m */; }; 12 | F8529E751DF9781A0043F94D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F8529E741DF9781A0043F94D /* AppDelegate.m */; }; 13 | F8529E781DF9781A0043F94D /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8529E771DF9781A0043F94D /* MainViewController.m */; }; 14 | F8529E7B1DF9781A0043F94D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F8529E791DF9781A0043F94D /* Main.storyboard */; }; 15 | F8529E7D1DF9781A0043F94D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F8529E7C1DF9781A0043F94D /* Assets.xcassets */; }; 16 | F8529E801DF9781A0043F94D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F8529E7E1DF9781A0043F94D /* LaunchScreen.storyboard */; }; 17 | F89EB58A1DFFDB0F0098D53A /* ZIKConstraintsGuard.m in Sources */ = {isa = PBXBuildFile; fileRef = F89EB5871DFFDB0F0098D53A /* ZIKConstraintsGuard.m */; }; 18 | F89EB58B1DFFDB0F0098D53A /* ZIKMethodSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = F89EB5891DFFDB0F0098D53A /* ZIKMethodSwizzler.m */; }; 19 | F8EFDC101E02873D0017E123 /* UIView+ZIKDebug.m in Sources */ = {isa = PBXBuildFile; fileRef = F8EFDC0F1E02873D0017E123 /* UIView+ZIKDebug.m */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 00B8A251DEC7E5D5B3B08D4B /* Pods-ConstraintDebug.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ConstraintDebug.release.xcconfig"; path = "Pods/Target Support Files/Pods-ConstraintDebug/Pods-ConstraintDebug.release.xcconfig"; sourceTree = ""; }; 24 | 603622CD1F52C70B50A9DECE /* libPods-ConstraintDebug.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ConstraintDebug.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | E18F5A7D0B939E50EB496B74 /* Pods-ConstraintDebug.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ConstraintDebug.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ConstraintDebug/Pods-ConstraintDebug.debug.xcconfig"; sourceTree = ""; }; 26 | F8529E6D1DF9781A0043F94D /* ConstraintDebug.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ConstraintDebug.app; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | F8529E711DF9781A0043F94D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 28 | F8529E731DF9781A0043F94D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 29 | F8529E741DF9781A0043F94D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 30 | F8529E761DF9781A0043F94D /* MainViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = ""; }; 31 | F8529E771DF9781A0043F94D /* MainViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = ""; }; 32 | F8529E7A1DF9781A0043F94D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 33 | F8529E7C1DF9781A0043F94D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | F8529E7F1DF9781A0043F94D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 35 | F8529E811DF9781A0043F94D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | F89EB5861DFFDB0F0098D53A /* ZIKConstraintsGuard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZIKConstraintsGuard.h; sourceTree = ""; }; 37 | F89EB5871DFFDB0F0098D53A /* ZIKConstraintsGuard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZIKConstraintsGuard.m; sourceTree = ""; }; 38 | F89EB5881DFFDB0F0098D53A /* ZIKMethodSwizzler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZIKMethodSwizzler.h; sourceTree = ""; }; 39 | F89EB5891DFFDB0F0098D53A /* ZIKMethodSwizzler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZIKMethodSwizzler.m; sourceTree = ""; }; 40 | F8EFDC0E1E02873D0017E123 /* UIView+ZIKDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+ZIKDebug.h"; sourceTree = ""; }; 41 | F8EFDC0F1E02873D0017E123 /* UIView+ZIKDebug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+ZIKDebug.m"; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | F8529E6A1DF9781A0043F94D /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | 4896C895371F41E1EC9A4756 /* libPods-ConstraintDebug.a in Frameworks */, 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | 0BC110D0C88E6368B4562D20 /* Pods */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | E18F5A7D0B939E50EB496B74 /* Pods-ConstraintDebug.debug.xcconfig */, 60 | 00B8A251DEC7E5D5B3B08D4B /* Pods-ConstraintDebug.release.xcconfig */, 61 | ); 62 | name = Pods; 63 | sourceTree = ""; 64 | }; 65 | EC818859B5729E854AD4D31D /* Frameworks */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | 603622CD1F52C70B50A9DECE /* libPods-ConstraintDebug.a */, 69 | ); 70 | name = Frameworks; 71 | sourceTree = ""; 72 | }; 73 | F8529E641DF9781A0043F94D = { 74 | isa = PBXGroup; 75 | children = ( 76 | F8529E6F1DF9781A0043F94D /* ConstraintDebug */, 77 | F8529E6E1DF9781A0043F94D /* Products */, 78 | 0BC110D0C88E6368B4562D20 /* Pods */, 79 | EC818859B5729E854AD4D31D /* Frameworks */, 80 | ); 81 | sourceTree = ""; 82 | }; 83 | F8529E6E1DF9781A0043F94D /* Products */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | F8529E6D1DF9781A0043F94D /* ConstraintDebug.app */, 87 | ); 88 | name = Products; 89 | sourceTree = ""; 90 | }; 91 | F8529E6F1DF9781A0043F94D /* ConstraintDebug */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | F89EB5851DFFDB0F0098D53A /* ZIKConstraintsGuard */, 95 | F8529E731DF9781A0043F94D /* AppDelegate.h */, 96 | F8529E741DF9781A0043F94D /* AppDelegate.m */, 97 | F8529E761DF9781A0043F94D /* MainViewController.h */, 98 | F8529E771DF9781A0043F94D /* MainViewController.m */, 99 | F8529E791DF9781A0043F94D /* Main.storyboard */, 100 | F8529E7C1DF9781A0043F94D /* Assets.xcassets */, 101 | F8529E7E1DF9781A0043F94D /* LaunchScreen.storyboard */, 102 | F8529E811DF9781A0043F94D /* Info.plist */, 103 | F8529E701DF9781A0043F94D /* Supporting Files */, 104 | ); 105 | path = ConstraintDebug; 106 | sourceTree = ""; 107 | }; 108 | F8529E701DF9781A0043F94D /* Supporting Files */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | F8529E711DF9781A0043F94D /* main.m */, 112 | ); 113 | name = "Supporting Files"; 114 | sourceTree = ""; 115 | }; 116 | F89EB5851DFFDB0F0098D53A /* ZIKConstraintsGuard */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | F89EB5861DFFDB0F0098D53A /* ZIKConstraintsGuard.h */, 120 | F89EB5871DFFDB0F0098D53A /* ZIKConstraintsGuard.m */, 121 | F89EB5881DFFDB0F0098D53A /* ZIKMethodSwizzler.h */, 122 | F89EB5891DFFDB0F0098D53A /* ZIKMethodSwizzler.m */, 123 | F8EFDC0E1E02873D0017E123 /* UIView+ZIKDebug.h */, 124 | F8EFDC0F1E02873D0017E123 /* UIView+ZIKDebug.m */, 125 | ); 126 | name = ZIKConstraintsGuard; 127 | path = ../../ZIKConstraintsGuard; 128 | sourceTree = ""; 129 | }; 130 | /* End PBXGroup section */ 131 | 132 | /* Begin PBXNativeTarget section */ 133 | F8529E6C1DF9781A0043F94D /* ConstraintDebug */ = { 134 | isa = PBXNativeTarget; 135 | buildConfigurationList = F8529E841DF9781A0043F94D /* Build configuration list for PBXNativeTarget "ConstraintDebug" */; 136 | buildPhases = ( 137 | 79944C6C505F6E8ACB9328E6 /* [CP] Check Pods Manifest.lock */, 138 | F8529E691DF9781A0043F94D /* Sources */, 139 | F8529E6A1DF9781A0043F94D /* Frameworks */, 140 | F8529E6B1DF9781A0043F94D /* Resources */, 141 | ED82E7648CDFB7A31E9EFD9B /* [CP] Embed Pods Frameworks */, 142 | C8B3F8E7F709F634628A73C4 /* [CP] Copy Pods Resources */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = ConstraintDebug; 149 | productName = ConstraintDebug; 150 | productReference = F8529E6D1DF9781A0043F94D /* ConstraintDebug.app */; 151 | productType = "com.apple.product-type.application"; 152 | }; 153 | /* End PBXNativeTarget section */ 154 | 155 | /* Begin PBXProject section */ 156 | F8529E651DF9781A0043F94D /* Project object */ = { 157 | isa = PBXProject; 158 | attributes = { 159 | LastUpgradeCheck = 0820; 160 | ORGANIZATIONNAME = "张玮珂"; 161 | TargetAttributes = { 162 | F8529E6C1DF9781A0043F94D = { 163 | CreatedOnToolsVersion = 8.2; 164 | DevelopmentTeam = 8FE9268LAG; 165 | ProvisioningStyle = Automatic; 166 | }; 167 | }; 168 | }; 169 | buildConfigurationList = F8529E681DF9781A0043F94D /* Build configuration list for PBXProject "ConstraintDebug" */; 170 | compatibilityVersion = "Xcode 3.2"; 171 | developmentRegion = English; 172 | hasScannedForEncodings = 0; 173 | knownRegions = ( 174 | en, 175 | Base, 176 | ); 177 | mainGroup = F8529E641DF9781A0043F94D; 178 | productRefGroup = F8529E6E1DF9781A0043F94D /* Products */; 179 | projectDirPath = ""; 180 | projectRoot = ""; 181 | targets = ( 182 | F8529E6C1DF9781A0043F94D /* ConstraintDebug */, 183 | ); 184 | }; 185 | /* End PBXProject section */ 186 | 187 | /* Begin PBXResourcesBuildPhase section */ 188 | F8529E6B1DF9781A0043F94D /* Resources */ = { 189 | isa = PBXResourcesBuildPhase; 190 | buildActionMask = 2147483647; 191 | files = ( 192 | F8529E801DF9781A0043F94D /* LaunchScreen.storyboard in Resources */, 193 | F8529E7D1DF9781A0043F94D /* Assets.xcassets in Resources */, 194 | F8529E7B1DF9781A0043F94D /* Main.storyboard in Resources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXResourcesBuildPhase section */ 199 | 200 | /* Begin PBXShellScriptBuildPhase section */ 201 | 79944C6C505F6E8ACB9328E6 /* [CP] Check Pods Manifest.lock */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | inputPaths = ( 207 | ); 208 | name = "[CP] Check Pods Manifest.lock"; 209 | outputPaths = ( 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | shellPath = /bin/sh; 213 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 214 | showEnvVarsInLog = 0; 215 | }; 216 | C8B3F8E7F709F634628A73C4 /* [CP] Copy Pods Resources */ = { 217 | isa = PBXShellScriptBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | ); 221 | inputPaths = ( 222 | ); 223 | name = "[CP] Copy Pods Resources"; 224 | outputPaths = ( 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | shellPath = /bin/sh; 228 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ConstraintDebug/Pods-ConstraintDebug-resources.sh\"\n"; 229 | showEnvVarsInLog = 0; 230 | }; 231 | ED82E7648CDFB7A31E9EFD9B /* [CP] Embed Pods Frameworks */ = { 232 | isa = PBXShellScriptBuildPhase; 233 | buildActionMask = 2147483647; 234 | files = ( 235 | ); 236 | inputPaths = ( 237 | ); 238 | name = "[CP] Embed Pods Frameworks"; 239 | outputPaths = ( 240 | ); 241 | runOnlyForDeploymentPostprocessing = 0; 242 | shellPath = /bin/sh; 243 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ConstraintDebug/Pods-ConstraintDebug-frameworks.sh\"\n"; 244 | showEnvVarsInLog = 0; 245 | }; 246 | /* End PBXShellScriptBuildPhase section */ 247 | 248 | /* Begin PBXSourcesBuildPhase section */ 249 | F8529E691DF9781A0043F94D /* Sources */ = { 250 | isa = PBXSourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | F8EFDC101E02873D0017E123 /* UIView+ZIKDebug.m in Sources */, 254 | F8529E781DF9781A0043F94D /* MainViewController.m in Sources */, 255 | F89EB58A1DFFDB0F0098D53A /* ZIKConstraintsGuard.m in Sources */, 256 | F89EB58B1DFFDB0F0098D53A /* ZIKMethodSwizzler.m in Sources */, 257 | F8529E751DF9781A0043F94D /* AppDelegate.m in Sources */, 258 | F8529E721DF9781A0043F94D /* main.m in Sources */, 259 | ); 260 | runOnlyForDeploymentPostprocessing = 0; 261 | }; 262 | /* End PBXSourcesBuildPhase section */ 263 | 264 | /* Begin PBXVariantGroup section */ 265 | F8529E791DF9781A0043F94D /* Main.storyboard */ = { 266 | isa = PBXVariantGroup; 267 | children = ( 268 | F8529E7A1DF9781A0043F94D /* Base */, 269 | ); 270 | name = Main.storyboard; 271 | sourceTree = ""; 272 | }; 273 | F8529E7E1DF9781A0043F94D /* LaunchScreen.storyboard */ = { 274 | isa = PBXVariantGroup; 275 | children = ( 276 | F8529E7F1DF9781A0043F94D /* Base */, 277 | ); 278 | name = LaunchScreen.storyboard; 279 | sourceTree = ""; 280 | }; 281 | /* End PBXVariantGroup section */ 282 | 283 | /* Begin XCBuildConfiguration section */ 284 | F8529E821DF9781A0043F94D /* Debug */ = { 285 | isa = XCBuildConfiguration; 286 | buildSettings = { 287 | ALWAYS_SEARCH_USER_PATHS = NO; 288 | CLANG_ANALYZER_NONNULL = YES; 289 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 290 | CLANG_CXX_LIBRARY = "libc++"; 291 | CLANG_ENABLE_MODULES = YES; 292 | CLANG_ENABLE_OBJC_ARC = YES; 293 | CLANG_WARN_BOOL_CONVERSION = YES; 294 | CLANG_WARN_CONSTANT_CONVERSION = YES; 295 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 296 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 297 | CLANG_WARN_EMPTY_BODY = YES; 298 | CLANG_WARN_ENUM_CONVERSION = YES; 299 | CLANG_WARN_INFINITE_RECURSION = YES; 300 | CLANG_WARN_INT_CONVERSION = YES; 301 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 302 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 303 | CLANG_WARN_UNREACHABLE_CODE = YES; 304 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 305 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 306 | COPY_PHASE_STRIP = NO; 307 | DEBUG_INFORMATION_FORMAT = dwarf; 308 | ENABLE_STRICT_OBJC_MSGSEND = YES; 309 | ENABLE_TESTABILITY = YES; 310 | GCC_C_LANGUAGE_STANDARD = gnu99; 311 | GCC_DYNAMIC_NO_PIC = NO; 312 | GCC_NO_COMMON_BLOCKS = YES; 313 | GCC_OPTIMIZATION_LEVEL = 0; 314 | GCC_PREPROCESSOR_DEFINITIONS = ( 315 | "DEBUG=1", 316 | "$(inherited)", 317 | ); 318 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 319 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 320 | GCC_WARN_UNDECLARED_SELECTOR = YES; 321 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 322 | GCC_WARN_UNUSED_FUNCTION = YES; 323 | GCC_WARN_UNUSED_VARIABLE = YES; 324 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 325 | MTL_ENABLE_DEBUG_INFO = YES; 326 | ONLY_ACTIVE_ARCH = YES; 327 | SDKROOT = iphoneos; 328 | TARGETED_DEVICE_FAMILY = "1,2"; 329 | }; 330 | name = Debug; 331 | }; 332 | F8529E831DF9781A0043F94D /* Release */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | ALWAYS_SEARCH_USER_PATHS = NO; 336 | CLANG_ANALYZER_NONNULL = YES; 337 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 338 | CLANG_CXX_LIBRARY = "libc++"; 339 | CLANG_ENABLE_MODULES = YES; 340 | CLANG_ENABLE_OBJC_ARC = YES; 341 | CLANG_WARN_BOOL_CONVERSION = YES; 342 | CLANG_WARN_CONSTANT_CONVERSION = YES; 343 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 344 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 345 | CLANG_WARN_EMPTY_BODY = YES; 346 | CLANG_WARN_ENUM_CONVERSION = YES; 347 | CLANG_WARN_INFINITE_RECURSION = YES; 348 | CLANG_WARN_INT_CONVERSION = YES; 349 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 350 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 351 | CLANG_WARN_UNREACHABLE_CODE = YES; 352 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 353 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 354 | COPY_PHASE_STRIP = NO; 355 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 356 | ENABLE_NS_ASSERTIONS = NO; 357 | ENABLE_STRICT_OBJC_MSGSEND = YES; 358 | GCC_C_LANGUAGE_STANDARD = gnu99; 359 | GCC_NO_COMMON_BLOCKS = YES; 360 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 361 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 362 | GCC_WARN_UNDECLARED_SELECTOR = YES; 363 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 364 | GCC_WARN_UNUSED_FUNCTION = YES; 365 | GCC_WARN_UNUSED_VARIABLE = YES; 366 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 367 | MTL_ENABLE_DEBUG_INFO = NO; 368 | SDKROOT = iphoneos; 369 | TARGETED_DEVICE_FAMILY = "1,2"; 370 | VALIDATE_PRODUCT = YES; 371 | }; 372 | name = Release; 373 | }; 374 | F8529E851DF9781A0043F94D /* Debug */ = { 375 | isa = XCBuildConfiguration; 376 | baseConfigurationReference = E18F5A7D0B939E50EB496B74 /* Pods-ConstraintDebug.debug.xcconfig */; 377 | buildSettings = { 378 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 379 | DEVELOPMENT_TEAM = 8FE9268LAG; 380 | FRAMEWORK_SEARCH_PATHS = ( 381 | "$(inherited)", 382 | "$(PROJECT_DIR)/ConstraintDebug", 383 | ); 384 | INFOPLIST_FILE = ConstraintDebug/Info.plist; 385 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 386 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 387 | PRODUCT_BUNDLE_IDENTIFIER = com.zuik.ConstraintDebug; 388 | PRODUCT_NAME = "$(TARGET_NAME)"; 389 | }; 390 | name = Debug; 391 | }; 392 | F8529E861DF9781A0043F94D /* Release */ = { 393 | isa = XCBuildConfiguration; 394 | baseConfigurationReference = 00B8A251DEC7E5D5B3B08D4B /* Pods-ConstraintDebug.release.xcconfig */; 395 | buildSettings = { 396 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 397 | DEVELOPMENT_TEAM = 8FE9268LAG; 398 | FRAMEWORK_SEARCH_PATHS = ( 399 | "$(inherited)", 400 | "$(PROJECT_DIR)/ConstraintDebug", 401 | ); 402 | INFOPLIST_FILE = ConstraintDebug/Info.plist; 403 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 404 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 405 | PRODUCT_BUNDLE_IDENTIFIER = com.zuik.ConstraintDebug; 406 | PRODUCT_NAME = "$(TARGET_NAME)"; 407 | }; 408 | name = Release; 409 | }; 410 | /* End XCBuildConfiguration section */ 411 | 412 | /* Begin XCConfigurationList section */ 413 | F8529E681DF9781A0043F94D /* Build configuration list for PBXProject "ConstraintDebug" */ = { 414 | isa = XCConfigurationList; 415 | buildConfigurations = ( 416 | F8529E821DF9781A0043F94D /* Debug */, 417 | F8529E831DF9781A0043F94D /* Release */, 418 | ); 419 | defaultConfigurationIsVisible = 0; 420 | defaultConfigurationName = Release; 421 | }; 422 | F8529E841DF9781A0043F94D /* Build configuration list for PBXNativeTarget "ConstraintDebug" */ = { 423 | isa = XCConfigurationList; 424 | buildConfigurations = ( 425 | F8529E851DF9781A0043F94D /* Debug */, 426 | F8529E861DF9781A0043F94D /* Release */, 427 | ); 428 | defaultConfigurationIsVisible = 0; 429 | defaultConfigurationName = Release; 430 | }; 431 | /* End XCConfigurationList section */ 432 | }; 433 | rootObject = F8529E651DF9781A0043F94D /* Project object */; 434 | } 435 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug.xcodeproj/xcuserdata/zhangweike.xcuserdatad/xcschemes/ConstraintDebug.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug.xcodeproj/xcuserdata/zhangweike.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ConstraintDebug.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | F8529E6C1DF9781A0043F94D 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ConstraintDebug 4 | // 5 | // Created by zuik on 2016/12/8. 6 | // Copyright © 2016年 zuik. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ConstraintDebug 4 | // 5 | // Created by zuik on 2016/12/8. 6 | // Copyright © 2016年 zuik. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ZIKConstraintsGuard.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | 21 | [ZIKConstraintsGuard monitorUnsatisfiableConstraintWithHandler:^(UIView *view, UIViewController *viewController, NSLayoutConstraint *constraintToBreak, NSArray *currentConstraints, NSString *description) { 22 | 23 | NSString *className = NSStringFromClass([viewController class]); 24 | if ([className hasPrefix:@"UI"] && ![className isEqualToString:@"UINavigationController"]) { 25 | NSLog(@"ignore conflict in system view:%@",viewController); 26 | return; 27 | } 28 | 29 | //write error to your log file 30 | NSLog(@"%@",description); 31 | }]; 32 | 33 | [ZIKConstraintsGuard monitorErrorFromLayoutviewsWithHandler:^(UIView * _Nonnull view, UIViewController * _Nullable viewController, NSString * _Nonnull description) { 34 | 35 | //write error to your log file before crash 36 | NSLog(@"%@",description); 37 | }]; 38 | 39 | return YES; 40 | } 41 | 42 | - (void)applicationWillResignActive:(UIApplication *)application { 43 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 44 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 45 | } 46 | 47 | 48 | - (void)applicationDidEnterBackground:(UIApplication *)application { 49 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 50 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 51 | } 52 | 53 | 54 | - (void)applicationWillEnterForeground:(UIApplication *)application { 55 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 56 | } 57 | 58 | 59 | - (void)applicationDidBecomeActive:(UIApplication *)application { 60 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 61 | } 62 | 63 | 64 | - (void)applicationWillTerminate:(UIApplication *)application { 65 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 66 | } 67 | 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Demo/ConstraintDebug/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 | 27 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug/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 | APPL 17 | CFBundleShortVersionString 18 | 1.1 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug/MainViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.h 3 | // ConstraintDebug 4 | // 5 | // Created by zuik on 2016/12/8. 6 | // Copyright © 2016年 zuik. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MainViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug/MainViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.m 3 | // ConstraintDebug 4 | // 5 | // Created by zuik on 2016/12/8. 6 | // Copyright © 2016年 zuik. All rights reserved. 7 | // 8 | 9 | #import "MainViewController.h" 10 | #import "Masonry.h" 11 | 12 | @interface MainViewController () 13 | @property (weak, nonatomic) IBOutlet UIView *myView; 14 | @end 15 | 16 | @implementation MainViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | [self addBadConstraints]; 22 | } 23 | 24 | - (void)addBadConstraints { 25 | UITableView *tableView = [[UITableView alloc] init]; 26 | tableView.backgroundColor = [UIColor redColor]; 27 | [self.view addSubview:tableView]; 28 | 29 | [tableView mas_makeConstraints:^(MASConstraintMaker *make) { 30 | make.centerX.equalTo(self.view.mas_centerX); 31 | make.centerY.equalTo(self.view.mas_centerY); 32 | make.width.equalTo(@250); 33 | make.height.equalTo(@250); 34 | 35 | //conflicting constraint 36 | make.left.equalTo(self.view.mas_left); 37 | }]; 38 | 39 | //add subview to UITableView will lead to crash in iOS6 and iOS7 40 | UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 41 | [tableView addSubview:indicator]; 42 | [indicator startAnimating]; 43 | 44 | [indicator mas_makeConstraints:^(MASConstraintMaker *make) { 45 | make.centerX.equalTo(tableView.mas_centerX); 46 | make.centerY.equalTo(tableView.mas_centerY); 47 | }]; 48 | } 49 | 50 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 51 | return 1; 52 | } 53 | 54 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 55 | UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"myCell"]; 56 | 57 | return cell; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Demo/ConstraintDebug/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ConstraintDebug 4 | // 5 | // Created by zuik on 2016/12/8. 6 | // Copyright © 2016年 zuik. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Demo/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '6.0' 2 | target :ConstraintDebug do 3 | pod 'Masonry' 4 | end 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Zhang Weike 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZIKConstraintsGuard 2 | A tool for debugging iOS view constraint.Monitor constraint conflict and crash problem below iOS7. 3 | 4 | ## Features 5 | 6 | * monitor constraint conflict. 7 | * monitor crash below iOS7 causing by layoutSubviews. 8 | * callback with more information: 9 | * the UIView where conflict happened 10 | * the view controller 11 | * all current constraints 12 | * the constraint to be breaked 13 | * view hierarchy 14 | 15 | With the view and view controller, you can quickly find out the exactly wrong constraint and which view it came from. 16 | 17 | ## Apple's bug below iOS7 18 | 19 | When implementation your custom view's `layoutSubviews`, remember: 20 | 21 | * Call `[super layoutSubviews]`. 22 | 23 | * Don't add constraints in `layoutSubviews`. 24 | 25 | If not, adding subview to this view in iOS6 or iOS7 will crash you app with `'Auto Layout still required after executing - layoutSubviews..'` printed in console.Apple fixed this after iOS8. 26 | 27 | Some system view like `UITableView`,`UITableViewCell` doesn't call `[super layoutSubviews]`. So don't add subview to them in iOS6 and iOS7, or you can use method swizzling to fix them. 28 | 29 | If your app need to support iOS7,this will offer a great help,since Xcode8 can't debug iOS7 device any more. 30 | 31 | ## CocoaPods 32 | 33 | To your podspec add: 34 | 35 | ``` 36 | pod 'ZIKConstraintsGuard' 37 | ``` 38 | 39 | ## How to use 40 | 41 | ``` 42 | //monitor constraint conflict 43 | [ZIKConstraintsGuard monitorUnsatisfiableConstraintWithHandler:^(UIView *view, 44 | UIViewController *viewController, 45 | NSLayoutConstraint *constraintToBreak, 46 | NSArray *currentConstraints, 47 | NSString *description) { 48 | 49 | NSString *className = NSStringFromClass([viewController class]); 50 | if ([className hasPrefix:@"UI"] && ![className isEqualToString:@"UINavigationController"]) { 51 | NSLog(@"ignore conflict in system view:%@",viewController); 52 | return; 53 | } 54 | 55 | //formatted description with enough debugging info, write to your log file 56 | NSLog(@"%@",description); 57 | }]; 58 | ``` 59 | 60 | ``` 61 | //monitor crash below iOS7 62 | [ZIKConstraintsGuard monitorErrorFromLayoutviewsWithHandler:^(UIView * _Nonnull view, UIViewController * _Nullable viewController, NSString * _Nonnull description) { 63 | 64 | //write error info to your log file before crash 65 | NSLog(@"%@",description); 66 | }]; 67 | ``` 68 | 69 | ## How it works 70 | 71 | Hook private API to get the UIView.Then get other informations from the UIView. 72 | 73 | API when meet unsatisfiable constraint: 74 | 75 | ``` 76 | -[UIView engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:] 77 | ``` 78 | 79 | API when system checking if view miss to call super layoutSubviews: 80 | 81 | ``` 82 | -[UIView _wantsWarningForMissingSuperLayoutSubviews] 83 | ``` 84 | These private API names were encrypted when using,in case you want to use this tool in an AppStore app. 85 | ___ 86 | 87 | 一个调试iOS约束的工具。可以检测约束冲突和iOS7以下的crash问题。 88 | 89 | ## 功能 90 | * 监控约束冲突。 91 | * 监控iOS7以下系统UIView中的layoutSubviews导致的crash。 92 | * 发生冲突时,回调附带更多关键信息: 93 | * 冲突所在的view 94 | * 冲突所在的view controller 95 | * 目前所有的约束 96 | * 系统将要打破的约束 97 | * view层级 98 | 99 | 通过捕捉到的view和view controller,就可以快速地找到是哪个界面的哪个控件的哪个约束出现了问题。 100 | 101 | ## 自动布局在iOS7以下的bug 102 | 103 | 当你在实现自定义view的`layoutSubviews`方法时,记住: 104 | 105 | * 调用`[super layoutSubviews]` 106 | * 不要在`layoutSubviews`里增加约束 107 | 108 | 如果不遵守这两条,当你向这个view上增加子view时,在iOS6和iOS7上会crash,控制台会输出提示:`'Auto Layout still required after executing - layoutSubviews..'` 。iOS8开始则不会crash。 109 | 110 | 某些系统控件,例如`UITableView`,`UITableViewCell`没有调用`[super layoutSubviews]`,所以在iOS6和iOS7上不能在它们上面增加子view,除非你用method swizlling修复它们的`layoutSubviews`方法。 111 | 112 | 当你的app需要支持iOS7时,这个工具会提供很大帮助。因为Xcode8已经不支持iOS7的调试了。 113 | 114 | ## CocoaPods 115 | 116 | 添加: 117 | 118 | ``` 119 | pod 'ZIKConstraintsGuard' 120 | ``` 121 | 122 | ## 使用方法 123 | 见英文部分: 124 | [使用方法](#how-to-use) 125 | 126 | ## 实现原理 127 | 128 | 替换了两个私有API,获取对应的view,再从view里获取其他相关信息。 129 | 130 | 当遇到约束冲突时的API: 131 | 132 | ``` 133 | -[UIView engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:] 134 | ``` 135 | 136 | 系统用于检测当前view是否调用了`[super layoutSubviews]`的API: 137 | 138 | ``` 139 | -[UIView _wantsWarningForMissingSuperLayoutSubviews] 140 | ``` 141 | 这些私有API名字已经经过混淆。 142 | 143 | -------------------------------------------------------------------------------- /ZIKConstraintsGuard.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint ZIKConstraintsGuard.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "ZIKConstraintsGuard" 19 | s.version = "1.1" 20 | s.summary = "A tool for debugging iOS view constraint.Monitor constraint conflict and crash problem below iOS7." 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = <<-DESC 28 | iOS auto layout engine doesn't show the info about which view or view controller contains the bad constraint when constraint conflict or crash happens.This tool can monitor constraint conflict and crash,and gives more specific information. 29 | DESC 30 | 31 | s.homepage = "https://github.com/Zuikyo/ZIKConstraintsGuard" 32 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 33 | 34 | 35 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 36 | # 37 | # Licensing your code is important. See http://choosealicense.com for more info. 38 | # CocoaPods will detect a license file if there is a named LICENSE* 39 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 40 | # 41 | 42 | s.license = "MIT" 43 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 44 | 45 | 46 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 47 | # 48 | # Specify the authors of the library, with email addresses. Email addresses 49 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 50 | # accepts just a name if you'd rather not provide an email address. 51 | # 52 | # Specify a social_media_url where others can refer to, for example a twitter 53 | # profile URL. 54 | # 55 | 56 | s.author = { "Zuikyo" => "zuilongzhizhu@gmail.com" } 57 | # Or just: s.author = "Zuikyo" 58 | # s.authors = { "Zuikyo" => "zuilongzhizhu@gmail.com" } 59 | # s.social_media_url = "http://twitter.com/Zuikyo" 60 | 61 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 62 | # 63 | # If this Pod runs only on iOS or OS X, then specify the platform and 64 | # the deployment target. You can optionally include the target after the platform. 65 | # 66 | 67 | # s.platform = :ios 68 | s.platform = :ios, "6.0" 69 | 70 | # When using multiple platforms 71 | # s.ios.deployment_target = "5.0" 72 | # s.osx.deployment_target = "10.7" 73 | # s.watchos.deployment_target = "2.0" 74 | # s.tvos.deployment_target = "9.0" 75 | 76 | 77 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 78 | # 79 | # Specify the location from where the source should be retrieved. 80 | # Supports git, hg, bzr, svn and HTTP. 81 | # 82 | 83 | s.source = { :git => "https://github.com/Zuikyo/ZIKConstraintsGuard.git", :tag => "#{s.version}" } 84 | 85 | 86 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 87 | # 88 | # CocoaPods is smart about how it includes source code. For source files 89 | # giving a folder will include any swift, h, m, mm, c & cpp files. 90 | # For header files it will include any header in the folder. 91 | # Not including the public_header_files will make all headers public. 92 | # 93 | 94 | s.source_files = "ZIKConstraintsGuard", "ZIKConstraintsGuard/**/*.{h,m}" 95 | s.exclude_files = "Classes/Exclude" 96 | 97 | # s.public_header_files = "Classes/**/*.h" 98 | 99 | 100 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 101 | # 102 | # A list of resources included with the Pod. These are copied into the 103 | # target bundle with a build phase script. Anything else will be cleaned. 104 | # You can preserve files from being cleaned, please don't preserve 105 | # non-essential files like tests, examples and documentation. 106 | # 107 | 108 | # s.resource = "icon.png" 109 | # s.resources = "Resources/*.png" 110 | 111 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 112 | 113 | 114 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 115 | # 116 | # Link your library with frameworks, or libraries. Libraries do not include 117 | # the lib prefix of their name. 118 | # 119 | 120 | # s.framework = "SomeFramework" 121 | s.frameworks = "UIKit", "Foundation" 122 | 123 | # s.library = "iconv" 124 | # s.libraries = "iconv", "xml2" 125 | 126 | 127 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 128 | # 129 | # If your library depends on compiler flags you can set them in the xcconfig hash 130 | # where they will only apply to your library. If you depend on other Podspecs 131 | # you can include multiple dependencies to ensure it works. 132 | 133 | s.requires_arc = true 134 | 135 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 136 | # s.dependency "JSONKit", "~> 1.4" 137 | 138 | end 139 | -------------------------------------------------------------------------------- /ZIKConstraintsGuard/UIView+ZIKDebug.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+ZIKDebug.h 3 | // ConstraintDebug 4 | // 5 | // Created by zuik on 2016/12/15. 6 | // Copyright © 2016年 zuik. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | ///decrypt the selector name,in case you want to use private API in AppStore version 12 | #define selectorFromASCII(ASCIIOfSelectorName) ({ \ 13 | char *hex = ASCIIOfSelectorName; \ 14 | NSString *selectorName = [[NSString alloc] initWithCString:hex encoding:NSASCIIStringEncoding]; \ 15 | SEL selector = NSSelectorFromString(selectorName); \ 16 | selector; \ 17 | }) 18 | 19 | NS_ASSUME_NONNULL_BEGIN 20 | 21 | @interface UIView (ZIKDebug) 22 | ///return the first subview in hierarchy tree for specific kind of class 23 | - (nullable UIView *)o_recursiveSearchSubviewWithClass:(Class)viewClass; 24 | ///view controller of the view 25 | - (UIViewController *)o_viewController; 26 | ///get view hierarchy; use private API -[UIView recursiveDescription].Selector name encrypted 27 | - (NSString *)o_viewHierarchyInfo; 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /ZIKConstraintsGuard/UIView+ZIKDebug.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+ZIKDebug.m 3 | // ConstraintDebug 4 | // 5 | // Created by zuik on 2016/12/15. 6 | // Copyright © 2016年 zuik. All rights reserved. 7 | // 8 | 9 | #import "UIView+ZIKDebug.h" 10 | 11 | ///recursiveDescription 12 | #define kRecursiveDescriptionSelectorASCII (char *)(uint8_t[]){0x72,0x65,0x63,0x75,0x72,0x73,0x69,0x76,0x65,0x44,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,'\0'} 13 | 14 | @implementation UIView (ZIKDebug) 15 | - (UIView *)o_recursiveSearchSubviewWithClass:(Class)viewClass { 16 | UIView *resultView; 17 | for (UIView *subview in [self subviews]) { 18 | if ([subview isKindOfClass:viewClass]) { 19 | resultView = subview; 20 | return resultView; 21 | } else { 22 | UIView *resultInSubview = [subview o_recursiveSearchSubviewWithClass:viewClass]; 23 | if (resultInSubview) { 24 | return resultInSubview; 25 | } 26 | } 27 | } 28 | return resultView; 29 | } 30 | 31 | //http://stackoverflow.com/questions/1372977/given-a-view-how-do-i-get-its-viewcontroller 32 | - (UIViewController *)o_viewController { 33 | UIViewController *viewController; 34 | 35 | UIResponder *responder = self; 36 | while ([responder isKindOfClass:[UIView class]]) 37 | responder = [responder nextResponder]; 38 | 39 | viewController = (UIViewController *)responder; 40 | return viewController; 41 | } 42 | 43 | - (NSString *)o_viewHierarchyInfo { 44 | SEL selector = selectorFromASCII(kRecursiveDescriptionSelectorASCII); 45 | if ([self respondsToSelector:selector]) { 46 | #pragma clang diagnostic push 47 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 48 | return [self performSelector:selector]; 49 | #pragma clang diagnostic pop 50 | } 51 | return nil; 52 | } 53 | @end 54 | -------------------------------------------------------------------------------- /ZIKConstraintsGuard/ZIKConstraintsGuard.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZIKConstraintsGuard.h 3 | // ConstraintDebug 4 | // 5 | // Created by zuik on 2016/12/8. 6 | // Copyright © 2016年 zuik. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /** 14 | callback when meet constraint conflict. 15 | 16 | @param view the view where conflict happened,always the top view of current viewController;use API in UIView+ZIKDebug to get detail info 17 | @param viewController the viewController where conflict happened 18 | @param constraintToBreak constraint which will be breaked by system to recover from the conflict 19 | @param currentConstraints all current constraints 20 | @param description formatted description contains enough debug information 21 | */ 22 | typedef void(^ZIKUnsatisfiableConstraintHandler)(UIView *view, UIViewController *viewController, NSLayoutConstraint *constraintToBreak, NSArray *currentConstraints, NSString *description); 23 | 24 | /** 25 | callback when meet problem about 'Auto Layout still required after executing...'. 26 | 27 | @param view the view where baddly implementation it's -layoutSubviews 28 | @param viewController view controller of view 29 | @param description formatted description contains enough debug information 30 | */ 31 | typedef void(^ZIKErrorFromLayoutviewsHandler)(UIView *view, UIViewController *_Nullable viewController, NSString *description); 32 | 33 | ///monitor constraint problem 34 | @interface ZIKConstraintsGuard : NSObject 35 | 36 | /** 37 | start monitor constraint conflict. Only take affect after iOS6. 38 | 39 | @note Some system view like UIAlertController has constraint conflict,you should manually ignore them by checking [viewController class] 40 | 41 | @param handler called from main thread 42 | */ 43 | + (void)monitorUnsatisfiableConstraintWithHandler:(ZIKUnsatisfiableConstraintHandler)handler NS_AVAILABLE_IOS(6_0); 44 | 45 | 46 | /** 47 | monitor crash problem about 'Auto Layout still required after executing - layoutSubviews'. Only take affect in iOS6 and iOS7. 48 | 49 | @note When implementation your custom view's -layoutSubviews, remember: 50 | 51 | 1.Call [super layoutSubviews] (if not, add subview to this view in iOS6 or iOS7 system will crash). 52 | 53 | 2.Don't add constraints in -layoutSubviews. 54 | 55 | Some system view like UITableView,UITableViewCell doesn't call [super layoutSubviews]. So don't add subview to them in iOS6 and iOS7, or you can use method swizzling to fix them. 56 | 57 | @param handler called from main thread 58 | */ 59 | + (void)monitorErrorFromLayoutviewsWithHandler:(ZIKErrorFromLayoutviewsHandler)handler NS_AVAILABLE_IOS(6_0); 60 | 61 | @end 62 | 63 | NS_ASSUME_NONNULL_END 64 | -------------------------------------------------------------------------------- /ZIKConstraintsGuard/ZIKConstraintsGuard.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZIKConstraintsGuard.m 3 | // ConstraintDebug 4 | // 5 | // Created by zuik on 2016/12/8. 6 | // Copyright © 2016年 zuik. All rights reserved. 7 | // 8 | 9 | #import "ZIKConstraintsGuard.h" 10 | #import "ZIKMethodSwizzler.h" 11 | #import "UIView+ZIKDebug.h" 12 | 13 | ///engine:willBreakConstraint:dueToMutuallyExclusiveConstraints: 14 | #define kOriginalUnsatisfiableConstraintHandleSelectorASCII (char *)(uint8_t[]){0x65,0x6e,0x67,0x69,0x6e,0x65,0x3a,0x77,0x69,0x6c,0x6c,0x42,0x72,0x65,0x61,0x6b,0x43,0x6f,0x6e,0x73,0x74,0x72,0x61,0x69,0x6e,0x74,0x3a,0x64,0x75,0x65,0x54,0x6f,0x4d,0x75,0x74,0x75,0x61,0x6c,0x6c,0x79,0x45,0x78,0x63,0x6c,0x75,0x73,0x69,0x76,0x65,0x43,0x6f,0x6e,0x73,0x74,0x72,0x61,0x69,0x6e,0x74,0x73,0x3a,'\0'} 15 | ///_wantsWarningForMissingSuperLayoutSubviews 16 | #define kWantsWarningForMissingSuperLayoutSubviewsSelectorASCII (char *)(uint8_t[]){0x5f,0x77,0x61,0x6e,0x74,0x73,0x57,0x61,0x72,0x6e,0x69,0x6e,0x67,0x46,0x6f,0x72,0x4d,0x69,0x73,0x73,0x69,0x6e,0x67,0x53,0x75,0x70,0x65,0x72,0x4c,0x61,0x79,0x6f,0x75,0x74,0x53,0x75,0x62,0x76,0x69,0x65,0x77,0x73,'\0'} 17 | ///UIViewControllerWrapperView 18 | #define kUIViewControllerWrapperViewASCII (char *)(uint8_t[]){0x55,0x49,0x56,0x69,0x65,0x77,0x43,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x6c,0x65,0x72,0x57,0x72,0x61,0x70,0x70,0x65,0x72,0x56,0x69,0x65,0x77,'\0'} 19 | 20 | 21 | static ZIKUnsatisfiableConstraintHandler unsatisfiableConstraintHandler; 22 | static ZIKErrorFromLayoutviewsHandler errorFromLayoutviewsHandler; 23 | 24 | @implementation ZIKConstraintsGuard 25 | 26 | + (void)monitorUnsatisfiableConstraintWithHandler:(ZIKUnsatisfiableConstraintHandler)handler { 27 | float systemVersion = [UIDevice currentDevice].systemVersion.floatValue; 28 | if (systemVersion < 6.0) { 29 | return; 30 | } 31 | 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^{ 34 | #pragma clang diagnostic push 35 | #pragma clang diagnostic ignored "-Wundeclared-selector" 36 | SEL originalSelector = selectorFromASCII(kOriginalUnsatisfiableConstraintHandleSelectorASCII); 37 | Class originalClass; 38 | if (systemVersion < 7.0) { 39 | originalClass = [UIWindow class]; 40 | } else { 41 | originalClass = [UIView class]; 42 | } 43 | 44 | BOOL success = replaceMethodWithMethod(originalClass, originalSelector, [self class], @selector(o_engine:willBreakConstraint:currentConstraints:)); 45 | if (!success) { 46 | NSLog(@"monitor unsatisfiable constraint failed!"); 47 | } 48 | #pragma clang diagnostic pop 49 | }); 50 | 51 | unsatisfiableConstraintHandler = handler; 52 | } 53 | 54 | + (void)monitorErrorFromLayoutviewsWithHandler:(ZIKErrorFromLayoutviewsHandler)handler { 55 | float systemVersion = [UIDevice currentDevice].systemVersion.floatValue; 56 | if (systemVersion >= 8.0 || systemVersion < 6.0) { 57 | return; 58 | } 59 | 60 | static dispatch_once_t onceToken; 61 | dispatch_once(&onceToken, ^{ 62 | #pragma clang diagnostic push 63 | #pragma clang diagnostic ignored "-Wundeclared-selector" 64 | SEL originalSelector = selectorFromASCII(kWantsWarningForMissingSuperLayoutSubviewsSelectorASCII); 65 | BOOL success = replaceMethodWithMethod([UIView class], originalSelector, [self class], @selector(o_hasErrorInLayoutSubviews)); 66 | if (!success) { 67 | NSLog(@"monitor error from -layoutviews failed!"); 68 | } 69 | #pragma clang diagnostic pop 70 | }); 71 | errorFromLayoutviewsHandler = handler; 72 | } 73 | 74 | + (void)o_engine:(id)engine willBreakConstraint:(id)breakConstraint currentConstraints:(id)currentConstraints { 75 | UIView *view = (UIView *)self; 76 | if ([view isKindOfClass:[UIWindow class]]) { 77 | NSString *WrapperViewName = [[NSString alloc] initWithCString:kUIViewControllerWrapperViewASCII encoding:NSASCIIStringEncoding]; 78 | UIView *viewControllerWrapperView = [view o_recursiveSearchSubviewWithClass:NSClassFromString(WrapperViewName)]; 79 | NSArray *subviews = [viewControllerWrapperView subviews]; 80 | if (subviews && subviews.count > 0) { 81 | view = [subviews firstObject]; 82 | } 83 | } 84 | UIViewController *viewController = [view o_viewController]; 85 | if (unsatisfiableConstraintHandler) { 86 | NSString *description = [ZIKConstraintsGuard o_descriptionForUnsatisfiableConstraintWithView:view viewController:viewController constraintToBreak:breakConstraint currentConstraints:currentConstraints]; 87 | unsatisfiableConstraintHandler(view, viewController, breakConstraint, currentConstraints, description); 88 | } 89 | 90 | [self o_engine:engine willBreakConstraint:breakConstraint currentConstraints:currentConstraints]; 91 | } 92 | 93 | + (BOOL) o_hasErrorInLayoutSubviews { 94 | BOOL hasError = [self o_hasErrorInLayoutSubviews]; 95 | 96 | if (hasError && errorFromLayoutviewsHandler) { 97 | UIView *view = (UIView *)self; 98 | NSString *description = [ZIKConstraintsGuard o_descriptionForErrorFromLayoutviewsWithView:view viewController:[view o_viewController]]; 99 | errorFromLayoutviewsHandler(view, [view o_viewController] , description); 100 | } 101 | return hasError; 102 | } 103 | 104 | + (NSString *)o_descriptionForUnsatisfiableConstraintWithView:(UIView *)view viewController:(UIViewController *)viewController constraintToBreak:(NSLayoutConstraint *)constraintToBreak currentConstraints:(NSArray *)currentConstraints { 105 | NSMutableString *description = [NSMutableString string]; 106 | [description appendFormat:@"constraint conflict in viewController:\n%@\nview:\n%@\n",viewController,view]; 107 | [description appendFormat:@"view hierarchy:\n%@\n",[view o_viewHierarchyInfo]]; 108 | [description appendFormat:@"current constraints:\n%@\n",currentConstraints]; 109 | [description appendFormat:@"try to break constraint:\n%@",constraintToBreak]; 110 | return description; 111 | } 112 | 113 | + (NSString *)o_descriptionForErrorFromLayoutviewsWithView:(UIView *)view viewController:(UIViewController *)viewController { 114 | NSMutableString *description = [NSMutableString string]; 115 | [description appendFormat:@"Check %@'s implementation of -layoutSubviews:\n1.Call [super layoutSubviews](if not, add subview to %@ in iOS6 or iOS7 system will crash) 2.Don't add constraints in -layoutSubviews.Some system view like UITableView,UITableViewCell doesn't call [super layoutSubviews],so don't add subview to them in iOS6 and iOS7, or you can use method swizzling to fix them.\n",[view class],[view class]]; 116 | [description appendFormat:@"error in viewController:\n%@ \nview:\n%@\n",viewController,view]; 117 | [description appendFormat:@"view hierarchy:\n%@",[view o_viewHierarchyInfo]]; 118 | return description; 119 | } 120 | 121 | @end 122 | -------------------------------------------------------------------------------- /ZIKConstraintsGuard/ZIKMethodSwizzler.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZIKMethodSwizzler.h 3 | // ConstraintDebug 4 | // 5 | // Created by zuik on 2016/12/9. 6 | // Copyright © 2016年 zuik. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | /** 13 | find the originalSelector method in originalClass,and the swizzledSelector method in swizzledClass.If both exist, replace them.When searching method in class,search the instance method first,if not exist,then class method. 14 | 15 | @return true if replace successfully,otherwise false. 16 | */ 17 | bool replaceMethodWithMethod(Class originalClass, 18 | SEL originalSelector, 19 | Class swizzledClass, 20 | SEL swizzledSelector); 21 | -------------------------------------------------------------------------------- /ZIKConstraintsGuard/ZIKMethodSwizzler.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZIKMethodSwizzler.m 3 | // ConstraintDebug 4 | // 5 | // Created by zuik on 2016/12/9. 6 | // Copyright © 2016年 zuik. All rights reserved. 7 | // 8 | 9 | #import "ZIKMethodSwizzler.h" 10 | #import 11 | 12 | bool replaceMethodWithMethod(Class originalClass, SEL originalSelector, Class swizzledClass, SEL swizzledSelector) { 13 | Method originalMethod = class_getInstanceMethod(originalClass, originalSelector); 14 | Method swizzledMethod = class_getInstanceMethod(swizzledClass, swizzledSelector); 15 | if (!originalMethod) { 16 | originalMethod = class_getClassMethod(originalClass, originalSelector); 17 | // originalClass = objc_getMetaClass(object_getClassName(originalClass)); 18 | } 19 | if (!originalMethod) { 20 | NSLog(@"replace failed, can't find original method:%@",NSStringFromSelector(originalSelector)); 21 | return false; 22 | } 23 | 24 | if (!swizzledMethod) { 25 | swizzledMethod = class_getClassMethod(swizzledClass, swizzledSelector); 26 | } 27 | if (!swizzledMethod) { 28 | NSLog(@"replace failed, can't find swizzled method:%@",NSStringFromSelector(swizzledSelector)); 29 | return false; 30 | } 31 | 32 | IMP originalIMP = method_getImplementation(originalMethod); 33 | IMP swizzledIMP = method_getImplementation(swizzledMethod); 34 | const char *originalType = method_getTypeEncoding(originalMethod); 35 | const char *swizzledType = method_getTypeEncoding(swizzledMethod); 36 | int cmpResult = strcmp(originalType, swizzledType); 37 | if (cmpResult != 0) { 38 | NSLog(@"warning:method signature not match, please confirm!original method:%@\n signature:%s\nswizzled method:%@\nsignature:%s",NSStringFromSelector(originalSelector),originalType,NSStringFromSelector(swizzledSelector),swizzledType); 39 | swizzledType = originalType; 40 | } 41 | 42 | class_replaceMethod(originalClass,swizzledSelector,originalIMP,originalType); 43 | class_replaceMethod(originalClass,originalSelector,swizzledIMP,swizzledType); 44 | return true; 45 | } 46 | --------------------------------------------------------------------------------